topologicpy 0.7.17__py3-none-any.whl → 0.7.18__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 +3 -1
- topologicpy/CellComplex.py +1 -1
- topologicpy/Face.py +83 -86
- topologicpy/Graph.py +82 -0
- topologicpy/Topology.py +4 -24
- topologicpy/Wire.py +96 -1
- topologicpy/version.py +1 -1
- {topologicpy-0.7.17.dist-info → topologicpy-0.7.18.dist-info}/METADATA +37 -1
- {topologicpy-0.7.17.dist-info → topologicpy-0.7.18.dist-info}/RECORD +12 -12
- {topologicpy-0.7.17.dist-info → topologicpy-0.7.18.dist-info}/LICENSE +0 -0
- {topologicpy-0.7.17.dist-info → topologicpy-0.7.18.dist-info}/WHEEL +0 -0
- {topologicpy-0.7.17.dist-info → topologicpy-0.7.18.dist-info}/top_level.txt +0 -0
topologicpy/Cell.py
CHANGED
@@ -377,6 +377,8 @@ class Cell():
|
|
377
377
|
If set to True, the last wire in the list of input wires will be connected to the first wire in the list of input wires. The default is False.
|
378
378
|
triangulate : bool , optional
|
379
379
|
If set to True, the faces will be triangulated. The default is True.
|
380
|
+
planarize : bool, optional
|
381
|
+
If set to True, the created faces are planarized before building the cell. Otherwise, they are not. The default is False.
|
380
382
|
mantissa : int , optional
|
381
383
|
The desired length of the mantissa. The default is 6.
|
382
384
|
tolerance : float , optional
|
@@ -908,7 +910,7 @@ class Cell():
|
|
908
910
|
from topologicpy.Topology import Topology
|
909
911
|
|
910
912
|
def angleCode(f, up, tiltAngle):
|
911
|
-
dirA = Face.
|
913
|
+
dirA = Face.Normal(f)
|
912
914
|
ang = round(Vector.Angle(dirA, up), 2)
|
913
915
|
if abs(ang - 90) < tiltAngle:
|
914
916
|
code = 0
|
topologicpy/CellComplex.py
CHANGED
@@ -467,7 +467,7 @@ class CellComplex():
|
|
467
467
|
from topologicpy.Topology import Topology
|
468
468
|
|
469
469
|
def angleCode(f, up, tiltAngle):
|
470
|
-
dirA = Face.
|
470
|
+
dirA = Face.Normal(f)
|
471
471
|
ang = round(Vector.Angle(dirA, up), 2)
|
472
472
|
if abs(ang - 90) < tiltAngle:
|
473
473
|
code = 0
|
topologicpy/Face.py
CHANGED
@@ -131,8 +131,8 @@ class Face():
|
|
131
131
|
if not Topology.IsInstance(faceB, "Face"):
|
132
132
|
print("Face.Angle - Warning: The input faceB parameter is not a valid topologic face. Returning None.")
|
133
133
|
return None
|
134
|
-
dirA = Face.
|
135
|
-
dirB = Face.
|
134
|
+
dirA = Face.Normal(faceA, outputType="xyz", mantissa=3)
|
135
|
+
dirB = Face.Normal(faceB, outputType="xyz", mantissa=3)
|
136
136
|
return round((Vector.Angle(dirA, dirB)), mantissa)
|
137
137
|
|
138
138
|
@staticmethod
|
@@ -439,7 +439,9 @@ class Face():
|
|
439
439
|
if not Topology.IsInstance(wire, "Wire"):
|
440
440
|
print("Face.ByThickenedWire - Error: The input wire parameter is not a valid wire. Returning None.")
|
441
441
|
return None
|
442
|
-
|
442
|
+
if not Wire.IsManifold(wire):
|
443
|
+
print("Face.ByThickenedWire - Error: The input wire parameter is not a manifold wire. Returning None.")
|
444
|
+
return None
|
443
445
|
three_vertices = Wire.Vertices(wire)[0:3]
|
444
446
|
temp_w = Wire.ByVertices(three_vertices, close=True)
|
445
447
|
flat_face = Face.ByWire(temp_w, tolerance=tolerance)
|
@@ -858,7 +860,7 @@ class Face():
|
|
858
860
|
return None
|
859
861
|
if not north:
|
860
862
|
north = Vector.North()
|
861
|
-
dirA = Face.
|
863
|
+
dirA = Face.Normal(face, outputType="xyz", mantissa=mantissa)
|
862
864
|
return Vector.CompassAngle(vectorA=dirA, vectorB=north, mantissa=mantissa, tolerance=tolerance)
|
863
865
|
|
864
866
|
@staticmethod
|
@@ -1041,12 +1043,9 @@ class Face():
|
|
1041
1043
|
|
1042
1044
|
eb = face.ExternalBoundary()
|
1043
1045
|
f_dir = Face.Normal(face)
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
if Vector.IsAntiParallel(f_dir, temp_dir):
|
1048
|
-
faceVertices.reverse()
|
1049
|
-
eb = Wire.ByVertices(faceVertices)
|
1046
|
+
w_dir = Wire.Normal(eb)
|
1047
|
+
if Vector.IsAntiParallel(f_dir, w_dir):
|
1048
|
+
eb = Wire.Reverse(eb)
|
1050
1049
|
return eb
|
1051
1050
|
|
1052
1051
|
@staticmethod
|
@@ -1648,7 +1647,7 @@ class Face():
|
|
1648
1647
|
return medialAxis
|
1649
1648
|
|
1650
1649
|
@staticmethod
|
1651
|
-
def Normal(face, outputType
|
1650
|
+
def Normal(face, outputType="xyz", mantissa=6):
|
1652
1651
|
"""
|
1653
1652
|
Returns the normal vector to the input face. A normal vector of a face is a vector perpendicular to it.
|
1654
1653
|
|
@@ -1661,55 +1660,86 @@ class Face():
|
|
1661
1660
|
mantissa : int , optional
|
1662
1661
|
The desired length of the mantissa. The default is 6.
|
1663
1662
|
|
1664
|
-
Returns
|
1665
|
-
-------
|
1666
|
-
list
|
1667
|
-
The normal vector to the input face. This is computed at the approximate center of the face.
|
1668
|
-
|
1669
|
-
"""
|
1670
|
-
return Face.NormalAtParameters(face, u=0.5, v=0.5, outputType=outputType, mantissa=mantissa)
|
1671
|
-
|
1672
|
-
@staticmethod
|
1673
|
-
def NormalAtParameters(face, u: float = 0.5, v: float = 0.5, outputType: str = "xyz", mantissa: int = 6) -> list:
|
1674
|
-
"""
|
1675
|
-
Returns the normal vector to the input face. A normal vector of a face is a vector perpendicular to it.
|
1676
|
-
|
1677
|
-
Parameters
|
1678
|
-
----------
|
1679
|
-
face : topologic_core.Face
|
1680
|
-
The input face.
|
1681
|
-
u : float , optional
|
1682
|
-
The *u* parameter at which to compute the normal to the input face. The default is 0.5.
|
1683
|
-
v : float , optional
|
1684
|
-
The *v* parameter at which to compute the normal to the input face. The default is 0.5.
|
1685
|
-
outputType : string , optional
|
1686
|
-
The string defining the desired output. This can be any subset or permutation of "xyz". It is case insensitive. The default is "xyz".
|
1687
|
-
mantissa : int , optional
|
1688
|
-
The desired length of the mantissa. The default is 6.
|
1689
|
-
|
1690
1663
|
Returns
|
1691
1664
|
-------
|
1692
1665
|
list
|
1693
1666
|
The normal vector to the input face.
|
1694
1667
|
|
1695
1668
|
"""
|
1696
|
-
|
1669
|
+
from topologicpy.Topology import Topology
|
1670
|
+
from topologicpy.Vertex import Vertex
|
1671
|
+
import os
|
1672
|
+
import warnings
|
1697
1673
|
try:
|
1698
|
-
|
1699
|
-
x = round(coords[0], mantissa)
|
1700
|
-
y = round(coords[1], mantissa)
|
1701
|
-
z = round(coords[2], mantissa)
|
1702
|
-
outputType = list(outputType.lower())
|
1703
|
-
for axis in outputType:
|
1704
|
-
if axis == "x":
|
1705
|
-
returnResult.append(x)
|
1706
|
-
elif axis == "y":
|
1707
|
-
returnResult.append(y)
|
1708
|
-
elif axis == "z":
|
1709
|
-
returnResult.append(z)
|
1674
|
+
import numpy as np
|
1710
1675
|
except:
|
1711
|
-
|
1712
|
-
|
1676
|
+
print("Face.Normal - Warning: Installing required numpy library.")
|
1677
|
+
try:
|
1678
|
+
os.system("pip install numpy")
|
1679
|
+
except:
|
1680
|
+
os.system("pip install numpy --user")
|
1681
|
+
try:
|
1682
|
+
import numpy as np
|
1683
|
+
print("Face.Normal - Warning: numpy library installed correctly.")
|
1684
|
+
except:
|
1685
|
+
warnings.warn("Face.Normal - Error: Could not import numpy. Please try to install numpy manually. Returning None.")
|
1686
|
+
return None
|
1687
|
+
|
1688
|
+
if not Topology.IsInstance(face, "Face"):
|
1689
|
+
print("Face.Normal - Error: The input face parameter is not a valid face. Returning None.")
|
1690
|
+
return None
|
1691
|
+
|
1692
|
+
vertices = Topology.Vertices(face)
|
1693
|
+
vertices = [Vertex.Coordinates(v, mantissa=mantissa) for v in vertices]
|
1694
|
+
|
1695
|
+
if len(vertices) < 3:
|
1696
|
+
print("Face.Normal - Error: At least three vertices are required to define a plane. Returning None.")
|
1697
|
+
return None
|
1698
|
+
|
1699
|
+
# Convert vertices to numpy array for easier manipulation
|
1700
|
+
vertices = np.array(vertices)
|
1701
|
+
|
1702
|
+
# Try to find two non-collinear edge vectors
|
1703
|
+
vec1 = None
|
1704
|
+
vec2 = None
|
1705
|
+
for i in range(1, len(vertices)):
|
1706
|
+
for j in range(i + 1, len(vertices)):
|
1707
|
+
temp_vec1 = vertices[i] - vertices[0]
|
1708
|
+
temp_vec2 = vertices[j] - vertices[0]
|
1709
|
+
cross_product = np.cross(temp_vec1, temp_vec2)
|
1710
|
+
if np.linalg.norm(cross_product) > 1e-6: # Check if the cross product is not near zero
|
1711
|
+
vec1 = temp_vec1
|
1712
|
+
vec2 = temp_vec2
|
1713
|
+
break
|
1714
|
+
if vec1 is not None and vec2 is not None:
|
1715
|
+
break
|
1716
|
+
|
1717
|
+
if vec1 is None or vec2 is None:
|
1718
|
+
print("Face.Normal - Error: The given vertices do not form a valid plane (all vertices might be collinear). Returning None.")
|
1719
|
+
return None
|
1720
|
+
|
1721
|
+
# Calculate the cross product of the two edge vectors
|
1722
|
+
normal = np.cross(vec1, vec2)
|
1723
|
+
|
1724
|
+
# Normalize the normal vector
|
1725
|
+
normal_length = np.linalg.norm(normal)
|
1726
|
+
if normal_length == 0:
|
1727
|
+
print("Face.Normal - Error: The given vertices do not form a valid plane (cross product resulted in a zero vector). Returning None.")
|
1728
|
+
return None
|
1729
|
+
|
1730
|
+
normal = normal / normal_length
|
1731
|
+
normal = normal.tolist()
|
1732
|
+
normal = [round(x, mantissa) for x in normal]
|
1733
|
+
return_normal = []
|
1734
|
+
outputType = list(outputType.lower())
|
1735
|
+
for axis in outputType:
|
1736
|
+
if axis == "x":
|
1737
|
+
return_normal.append(normal[0])
|
1738
|
+
elif axis == "y":
|
1739
|
+
return_normal.append(normal[1])
|
1740
|
+
elif axis == "z":
|
1741
|
+
return_normal.append(normal[2])
|
1742
|
+
return return_normal
|
1713
1743
|
|
1714
1744
|
@staticmethod
|
1715
1745
|
def NormalEdge(face, length: float = 1.0, tolerance: float = 0.0001, silent: bool = False):
|
@@ -1744,43 +1774,10 @@ class Face():
|
|
1744
1774
|
return None
|
1745
1775
|
iv = Face.InternalVertex(face)
|
1746
1776
|
u, v = Face.VertexParameters(face, iv)
|
1747
|
-
vec = Face.
|
1777
|
+
vec = Face.Normal(face)
|
1748
1778
|
ev = Topology.TranslateByDirectionDistance(iv, vec, length)
|
1749
1779
|
return Edge.ByVertices([iv, ev], tolerance=tolerance, silent=silent)
|
1750
1780
|
|
1751
|
-
@staticmethod
|
1752
|
-
def NormalEdgeAtParameters(face, u: float = 0.5, v: float = 0.5, length: float = 1.0, tolerance: float = 0.0001):
|
1753
|
-
"""
|
1754
|
-
Returns the normal vector to the input face as an edge with the desired input length. A normal vector of a face is a vector perpendicular to it.
|
1755
|
-
|
1756
|
-
Parameters
|
1757
|
-
----------
|
1758
|
-
face : topologic_core.Face
|
1759
|
-
The input face.
|
1760
|
-
u : float , optional
|
1761
|
-
The *u* parameter at which to compute the normal to the input face. The default is 0.5.
|
1762
|
-
v : float , optional
|
1763
|
-
The *v* parameter at which to compute the normal to the input face. The default is 0.5.
|
1764
|
-
length : float , optional
|
1765
|
-
The desired length of the normal edge. The default is 1.
|
1766
|
-
tolerance : float , optional
|
1767
|
-
The desired tolerance. The default is 0.0001.
|
1768
|
-
|
1769
|
-
Returns
|
1770
|
-
-------
|
1771
|
-
topologic_core.Edge
|
1772
|
-
The created normal edge to the input face. This is computed at the approximate center of the face.
|
1773
|
-
|
1774
|
-
"""
|
1775
|
-
from topologicpy.Edge import Edge
|
1776
|
-
from topologicpy.Topology import Topology
|
1777
|
-
if not Topology.IsInstance(face, "Face"):
|
1778
|
-
return None
|
1779
|
-
sv = Face.VertexByParameters(face=face, u=u, v=v)
|
1780
|
-
vec = Face.NormalAtParameters(face, u=u, v=v)
|
1781
|
-
ev = Topology.TranslateByDirectionDistance(sv, vec, length)
|
1782
|
-
return Edge.ByVertices([sv, ev], tolerance=tolerance, silent=True)
|
1783
|
-
|
1784
1781
|
@staticmethod
|
1785
1782
|
def PlaneEquation(face, mantissa: int = 6) -> dict:
|
1786
1783
|
"""
|
topologicpy/Graph.py
CHANGED
@@ -269,6 +269,88 @@ class _DrawTree(object):
|
|
269
269
|
return self.__str__()
|
270
270
|
|
271
271
|
class Graph:
|
272
|
+
def AdjacencyDictionary(graph, vertexLabelKey: str = "label", edgeKey: str = "Length", reverse: bool = False, mantissa: int = 6):
|
273
|
+
"""
|
274
|
+
Returns the adjacency dictionary of the input Graph.
|
275
|
+
|
276
|
+
Parameters
|
277
|
+
----------
|
278
|
+
graph : topologic_core.Graph
|
279
|
+
The input graph.
|
280
|
+
vertexLabelKey : str , optional
|
281
|
+
The returned vertices are labelled according to the dictionary values stored under this key.
|
282
|
+
If the vertexLabelKey does not exist, it will be created and the vertices are labelled numerically and stored in the vertex dictionary under this key. The default is "label".
|
283
|
+
edgeWeightKey : str , optional
|
284
|
+
If set, the edges' dictionaries will be searched for this key to set their weight. If the key is set to "length" (case insensitive), the length of the edge will be used as its weight. If set to None, a weight of 1 will be used. The default is "Length".
|
285
|
+
reverse : bool , optional
|
286
|
+
If set to True, the vertices are sorted in reverse order (only if vertexKey is set). Otherwise, they are not. The default is False.
|
287
|
+
mantissa : int , optional
|
288
|
+
The desired length of the mantissa. The default is 6.
|
289
|
+
|
290
|
+
Returns
|
291
|
+
-------
|
292
|
+
dict
|
293
|
+
The adjacency dictionary.
|
294
|
+
"""
|
295
|
+
from topologicpy.Vertex import Vertex
|
296
|
+
from topologicpy.Edge import Edge
|
297
|
+
from topologicpy.Dictionary import Dictionary
|
298
|
+
from topologicpy.Topology import Topology
|
299
|
+
from topologicpy.Helper import Helper
|
300
|
+
|
301
|
+
if not Topology.IsInstance(graph, "Graph"):
|
302
|
+
print("Graph.AdjacencyDictionary - Error: The input graph is not a valid graph. Returning None.")
|
303
|
+
return None
|
304
|
+
if not isinstance(vertexLabelKey, str):
|
305
|
+
print("Graph.AdjacencyDictionary - Error: The input vertexLabelKey is not a valid string. Returning None.")
|
306
|
+
return None
|
307
|
+
vertices = Graph.Vertices(graph)
|
308
|
+
labels = []
|
309
|
+
n = max(len(str(len(vertices))), 3)
|
310
|
+
for i, v in enumerate(vertices):
|
311
|
+
d = Topology.Dictionary(v)
|
312
|
+
value = Dictionary.ValueAtKey(d, vertexLabelKey)
|
313
|
+
if value == None:
|
314
|
+
value = str(i).zfill(n)
|
315
|
+
if d == None:
|
316
|
+
d = Dictionary.ByKeyValue(vertexLabelKey, value)
|
317
|
+
else:
|
318
|
+
d = Dictionary.SetValueAtKey(d, vertexLabelKey, value)
|
319
|
+
v = Topology.SetDictionary(v, d)
|
320
|
+
labels.append(value)
|
321
|
+
vertices = Helper.Sort(vertices, labels)
|
322
|
+
labels.sort()
|
323
|
+
if reverse == True:
|
324
|
+
vertices.reverse()
|
325
|
+
labels.reverse()
|
326
|
+
order = len(vertices)
|
327
|
+
adjDict = {}
|
328
|
+
for i in range(order):
|
329
|
+
v = Graph.NearestVertex(graph, vertices[i])
|
330
|
+
vertex_label = labels[i]
|
331
|
+
adjVertices = Graph.AdjacentVertices(graph, v)
|
332
|
+
temp_list = []
|
333
|
+
for adjVertex in adjVertices:
|
334
|
+
if edgeKey == None:
|
335
|
+
weight = 1
|
336
|
+
elif "length" in edgeKey.lower():
|
337
|
+
edge = Graph.Edge(graph, v, adjVertex)
|
338
|
+
weight = Edge.Length(edge, mantissa=mantissa)
|
339
|
+
else:
|
340
|
+
edge = Graph.Edge(graph, v, adjVertex)
|
341
|
+
weight = Dictionary.ValueAtKey(Topology.Dictionary(edge), edgeKey)
|
342
|
+
if weight == None:
|
343
|
+
weight = Edge.Length(edge, mantissa=mantissa)
|
344
|
+
else:
|
345
|
+
weight = round(weight, mantissa)
|
346
|
+
adjIndex = Vertex.Index(adjVertex, vertices)
|
347
|
+
adjLabel = labels[adjIndex]
|
348
|
+
if not adjIndex == None:
|
349
|
+
temp_list.append((adjLabel, weight))
|
350
|
+
temp_list.sort()
|
351
|
+
adjDict[vertex_label] = temp_list
|
352
|
+
return adjDict
|
353
|
+
|
272
354
|
@staticmethod
|
273
355
|
def AdjacencyMatrix(graph, vertexKey=None, reverse=False, edgeKeyFwd=None, edgeKeyBwd=None, bidirKey=None, bidirectional=True, useEdgeIndex=False, useEdgeLength=False, tolerance=0.0001):
|
274
356
|
"""
|
topologicpy/Topology.py
CHANGED
@@ -3236,7 +3236,7 @@ class Topology():
|
|
3236
3236
|
_ = topology.Faces(None, faces)
|
3237
3237
|
normals = []
|
3238
3238
|
for aFace in faces:
|
3239
|
-
normals.append(Face.
|
3239
|
+
normals.append(Face.Normal(aFace, outputType="XYZ", mantissa=3))
|
3240
3240
|
# build a matrix of similarity
|
3241
3241
|
mat = buildSimilarityMatrix(normals, angTolerance)
|
3242
3242
|
categories = categorizeIntoClusters(mat)
|
@@ -4799,17 +4799,6 @@ class Topology():
|
|
4799
4799
|
_ = topology.CellComplexes(None, topologies)
|
4800
4800
|
return topologies
|
4801
4801
|
|
4802
|
-
def triangulateFace(face):
|
4803
|
-
faceTriangles = []
|
4804
|
-
for i in range(0, 5, 1):
|
4805
|
-
try:
|
4806
|
-
_ = topologic.FaceUtility.Triangulate(face, float(i)*0.1, faceTriangles)
|
4807
|
-
return faceTriangles
|
4808
|
-
except:
|
4809
|
-
continue
|
4810
|
-
faceTriangles.append(face)
|
4811
|
-
return faceTriangles
|
4812
|
-
|
4813
4802
|
vertices = []
|
4814
4803
|
edges = []
|
4815
4804
|
faces = []
|
@@ -4858,14 +4847,10 @@ class Topology():
|
|
4858
4847
|
ib = []
|
4859
4848
|
_ = aFace.InternalBoundaries(ib)
|
4860
4849
|
if(len(ib) > 0):
|
4861
|
-
triFaces =
|
4850
|
+
triFaces = Face.Triangulate(aFace)
|
4862
4851
|
for aTriFace in triFaces:
|
4863
|
-
wire =
|
4852
|
+
wire = Face.ExternalBoundary(aTriFace)
|
4864
4853
|
faceVertices = getSubTopologies(wire, topologic.Vertex)
|
4865
|
-
temp_face = Face.ByWire(wire)
|
4866
|
-
temp_dir = Face.Normal(temp_face)
|
4867
|
-
if Vector.IsAntiParallel(f_dir, temp_dir):
|
4868
|
-
faceVertices.reverse()
|
4869
4854
|
f = []
|
4870
4855
|
for aVertex in faceVertices:
|
4871
4856
|
try:
|
@@ -4876,13 +4861,8 @@ class Topology():
|
|
4876
4861
|
f.append(fVertexIndex)
|
4877
4862
|
faces.append(f)
|
4878
4863
|
else:
|
4879
|
-
wire =
|
4880
|
-
#wire = topologic.WireUtility.RemoveCollinearEdges(wire, 0.1) #This is an angle Tolerance
|
4864
|
+
wire = Face.ExternalBoundary(aFace)
|
4881
4865
|
faceVertices = getSubTopologies(wire, topologic.Vertex)
|
4882
|
-
temp_face = Face.ByWire(wire)
|
4883
|
-
temp_dir = Face.Normal(temp_face)
|
4884
|
-
if Vector.IsAntiParallel(f_dir, temp_dir):
|
4885
|
-
faceVertices.reverse()
|
4886
4866
|
f = []
|
4887
4867
|
for aVertex in faceVertices:
|
4888
4868
|
try:
|
topologicpy/Wire.py
CHANGED
@@ -1953,6 +1953,101 @@ class Wire(Topology):
|
|
1953
1953
|
return_wire = Topology.Unflatten(flat_wire, origin=Vertex.Origin(), direction=normal)
|
1954
1954
|
return return_wire
|
1955
1955
|
|
1956
|
+
@staticmethod
|
1957
|
+
def Normal(wire, outputType="xyz", mantissa=6):
|
1958
|
+
"""
|
1959
|
+
Returns the normal vector to the input wire. A normal vector of a wire is a vector perpendicular to it.
|
1960
|
+
|
1961
|
+
Parameters
|
1962
|
+
----------
|
1963
|
+
wire : topologic_core.Wire
|
1964
|
+
The input wire.
|
1965
|
+
outputType : string , optional
|
1966
|
+
The string defining the desired output. This can be any subset or permutation of "xyz". It is case insensitive. The default is "xyz".
|
1967
|
+
mantissa : int , optional
|
1968
|
+
The desired length of the mantissa. The default is 6.
|
1969
|
+
|
1970
|
+
Returns
|
1971
|
+
-------
|
1972
|
+
list
|
1973
|
+
The normal vector to the input face.
|
1974
|
+
|
1975
|
+
"""
|
1976
|
+
from topologicpy.Topology import Topology
|
1977
|
+
from topologicpy.Vertex import Vertex
|
1978
|
+
import os
|
1979
|
+
import warnings
|
1980
|
+
try:
|
1981
|
+
import numpy as np
|
1982
|
+
except:
|
1983
|
+
print("Wire.Normal - Warning: Installing required numpy library.")
|
1984
|
+
try:
|
1985
|
+
os.system("pip install numpy")
|
1986
|
+
except:
|
1987
|
+
os.system("pip install numpy --user")
|
1988
|
+
try:
|
1989
|
+
import numpy as np
|
1990
|
+
print("Wire.Normal - Warning: numpy library installed correctly.")
|
1991
|
+
except:
|
1992
|
+
warnings.warn("Wire.Normal - Error: Could not import numpy. Please try to install numpy manually. Returning None.")
|
1993
|
+
return None
|
1994
|
+
|
1995
|
+
if not Topology.IsInstance(wire, "Wire"):
|
1996
|
+
print("Wire.Normal - Error: The input wire parameter is not a valid wire. Returning None.")
|
1997
|
+
return None
|
1998
|
+
|
1999
|
+
vertices = Topology.Vertices(wire)
|
2000
|
+
vertices = [Vertex.Coordinates(v, mantissa=mantissa) for v in vertices]
|
2001
|
+
|
2002
|
+
if len(vertices) < 3:
|
2003
|
+
print("Wire.Normal - Error: At least three vertices are required to define a plane. Returning None.")
|
2004
|
+
return None
|
2005
|
+
|
2006
|
+
# Convert vertices to numpy array for easier manipulation
|
2007
|
+
vertices = np.array(vertices)
|
2008
|
+
|
2009
|
+
# Try to find two non-collinear edge vectors
|
2010
|
+
vec1 = None
|
2011
|
+
vec2 = None
|
2012
|
+
for i in range(1, len(vertices)):
|
2013
|
+
for j in range(i + 1, len(vertices)):
|
2014
|
+
temp_vec1 = vertices[i] - vertices[0]
|
2015
|
+
temp_vec2 = vertices[j] - vertices[0]
|
2016
|
+
cross_product = np.cross(temp_vec1, temp_vec2)
|
2017
|
+
if np.linalg.norm(cross_product) > 1e-6: # Check if the cross product is not near zero
|
2018
|
+
vec1 = temp_vec1
|
2019
|
+
vec2 = temp_vec2
|
2020
|
+
break
|
2021
|
+
if vec1 is not None and vec2 is not None:
|
2022
|
+
break
|
2023
|
+
|
2024
|
+
if vec1 is None or vec2 is None:
|
2025
|
+
print("Wire.Normal - Error: The given vertices do not form a valid plane (all vertices might be collinear). Returning None.")
|
2026
|
+
return None
|
2027
|
+
|
2028
|
+
# Calculate the cross product of the two edge vectors
|
2029
|
+
normal = np.cross(vec1, vec2)
|
2030
|
+
|
2031
|
+
# Normalize the normal vector
|
2032
|
+
normal_length = np.linalg.norm(normal)
|
2033
|
+
if normal_length == 0:
|
2034
|
+
print("Wire.Normal - Error: The given vertices do not form a valid plane (cross product resulted in a zero vector). Returning None.")
|
2035
|
+
return None
|
2036
|
+
|
2037
|
+
normal = normal / normal_length
|
2038
|
+
normal = normal.tolist()
|
2039
|
+
normal = [round(x, mantissa) for x in normal]
|
2040
|
+
return_normal = []
|
2041
|
+
outputType = list(outputType.lower())
|
2042
|
+
for axis in outputType:
|
2043
|
+
if axis == "x":
|
2044
|
+
return_normal.append(normal[0])
|
2045
|
+
elif axis == "y":
|
2046
|
+
return_normal.append(normal[1])
|
2047
|
+
elif axis == "z":
|
2048
|
+
return_normal.append(normal[2])
|
2049
|
+
return return_normal
|
2050
|
+
|
1956
2051
|
@staticmethod
|
1957
2052
|
def OrientEdges(wire, vertexA, tolerance=0.0001):
|
1958
2053
|
"""
|
@@ -2106,7 +2201,7 @@ class Wire(Topology):
|
|
2106
2201
|
if not Topology.IsInstance(face, "Face"):
|
2107
2202
|
return None
|
2108
2203
|
if not direction:
|
2109
|
-
direction = -1*Face.
|
2204
|
+
direction = -1*Face.Normal(face, outputType="xyz", mantissa=mantissa)
|
2110
2205
|
large_face = Topology.Scale(face, face.CenterOfMass(), 500, 500, 500)
|
2111
2206
|
edges = []
|
2112
2207
|
_ = wire.Edges(None, edges)
|
topologicpy/version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = '0.7.
|
1
|
+
__version__ = '0.7.18'
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: topologicpy
|
3
|
-
Version: 0.7.
|
3
|
+
Version: 0.7.18
|
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
|
@@ -108,6 +108,42 @@ topologicpy depends on the following python libraries which will be installed au
|
|
108
108
|
## API Documentation
|
109
109
|
API documentation can be found at [https://topologicpy.readthedocs.io](https://topologicpy.readthedocs.io)
|
110
110
|
|
111
|
+
## How to cite topologicpy
|
112
|
+
If you wish to cite the actual software, you can use:
|
113
|
+
|
114
|
+
**Jabi, W. (2024). topologicpy. pypi.org. http://doi.org/10.5281/zenodo.11555172**
|
115
|
+
|
116
|
+
To cite one of the main papers that defines topologicpy, you can use:
|
117
|
+
|
118
|
+
**Jabi, W., & Chatzivasileiadi, A. (2021). Topologic: Exploring Spatial Reasoning Through Geometry, Topology, and Semantics. In S. Eloy, D. Leite Viana, F. Morais, & J. Vieira Vaz (Eds.), Formal Methods in Architecture (pp. 277–285). Springer International Publishing. https://doi.org/10.1007/978-3-030-57509-0_25**
|
119
|
+
|
120
|
+
Or you can import the following .bib formatted references into your favourite reference manager
|
121
|
+
```
|
122
|
+
@misc{Jabi2024,
|
123
|
+
author = {Wassim Jabi},
|
124
|
+
doi = {https://doi.org/10.5281/zenodo.11555173},
|
125
|
+
title = {topologicpy},
|
126
|
+
url = {http://pypi.org/projects/topologicpy},
|
127
|
+
year = {2024},
|
128
|
+
}
|
129
|
+
```
|
130
|
+
```
|
131
|
+
@inbook{Jabi2021,
|
132
|
+
abstract = {Topologic is a software modelling library that supports a comprehensive conceptual framework for the hierarchical spatial representation of buildings based on the data structures and concepts of non-manifold topology (NMT). Topologic supports conceptual design and spatial reasoning through the integration of geometry, topology, and semantics. This enables architects and designers to reflect on their design decisions before the complexities of building information modelling (BIM) set in. We summarize below related work on NMT starting in the late 1980s, describe Topologic’s software architecture, methods, and classes, and discuss how Topologic’s features support conceptual design and spatial reasoning. We also report on a software usability workshop that was conducted to validate a software evaluation methodology and reports on the collected qualitative data. A reflection on Topologic’s features and software architecture illustrates how it enables a fundamental shift from pursuing fidelity of design form to pursuing fidelity of design intent.},
|
133
|
+
author = {Wassim Jabi and Aikaterini Chatzivasileiadi},
|
134
|
+
city = {Cham},
|
135
|
+
doi = {10.1007/978-3-030-57509-0_25},
|
136
|
+
editor = {Sara Eloy and David Leite Viana and Franklim Morais and Jorge Vieira Vaz},
|
137
|
+
isbn = {978-3-030-57509-0},
|
138
|
+
journal = {Formal Methods in Architecture},
|
139
|
+
pages = {277-285},
|
140
|
+
publisher = {Springer International Publishing},
|
141
|
+
title = {Topologic: Exploring Spatial Reasoning Through Geometry, Topology, and Semantics},
|
142
|
+
url = {https://link.springer.com/10.1007/978-3-030-57509-0_25},
|
143
|
+
year = {2021},
|
144
|
+
}
|
145
|
+
```
|
146
|
+
|
111
147
|
topologicpy: © 2024 Wassim Jabi
|
112
148
|
|
113
149
|
Topologic: © 2024 Cardiff University and UCL
|
@@ -1,6 +1,6 @@
|
|
1
1
|
topologicpy/Aperture.py,sha256=p9pUzTQSBWoUaDiug1V1R1hnEIEwYSXFg2t7iRAmNRY,2723
|
2
|
-
topologicpy/Cell.py,sha256=
|
3
|
-
topologicpy/CellComplex.py,sha256=
|
2
|
+
topologicpy/Cell.py,sha256=FNmDHdNkJYaM982MLktfSipqFnfJBQmdxKp3kmuBKjA,99861
|
3
|
+
topologicpy/CellComplex.py,sha256=Fd70Dj7E4LqV5O-7kcCCYnrFEyqheZvxc0YWktm0EL8,47440
|
4
4
|
topologicpy/Cluster.py,sha256=HvomWm_V4bx76YMxqOEhAUrsvcU6z5e_zry6WxMuV2M,54819
|
5
5
|
topologicpy/Color.py,sha256=UlmRcCSOhqcM_OyMWz4t3Kr75KcgXDhz3uctAJ2n7Ic,18031
|
6
6
|
topologicpy/Context.py,sha256=ppApYKngZZCQBFWaxIMi2z2dokY23c935IDCBosxDAE,3055
|
@@ -8,8 +8,8 @@ topologicpy/DGL.py,sha256=5jNn1L8F8REx1-oAh9sr_cfil0R5kUuueCNDKwNU_GM,138991
|
|
8
8
|
topologicpy/Dictionary.py,sha256=pMbfE2RYGCNpVr2x58qiHRc-aBWnp1jLlyzwS9nz6-w,25891
|
9
9
|
topologicpy/Edge.py,sha256=f7LjP662_yoqopAXBh1Gqv8DvdzvfM_heJTP2XnFskY,58447
|
10
10
|
topologicpy/EnergyModel.py,sha256=ni0H1JgvLl1-q90yK9Sm1qj5P1fTuidlimEIcwuj6qE,53287
|
11
|
-
topologicpy/Face.py,sha256=
|
12
|
-
topologicpy/Graph.py,sha256=
|
11
|
+
topologicpy/Face.py,sha256=1J1dnS3tNnaoFQyWdWjn9YnHldyiN-J67IjytIT0-R4,108142
|
12
|
+
topologicpy/Graph.py,sha256=JM4N6EShvDspFcE9_E027HKAkHhZ5e5UD4I9LxlRfmA,391560
|
13
13
|
topologicpy/Grid.py,sha256=Cpzs9l5-SptMQbUR8AvbbIOHrGMGlK0Qx8FWmQBgvX0,18497
|
14
14
|
topologicpy/Helper.py,sha256=07V9IFu5ilMpvAdZVhIbdBOjBJSRTtJ0BfR1IoRaRXU,17743
|
15
15
|
topologicpy/Honeybee.py,sha256=dlr5OEH93q51ZmEgvi8PXGfCHBDAjIZ1cm38Rft1Bz4,20235
|
@@ -20,14 +20,14 @@ topologicpy/Polyskel.py,sha256=pNawz5lnvy4oTzCL91fGY2PblW2hmcYBdT5268m2RZs,19743
|
|
20
20
|
topologicpy/Shell.py,sha256=fdMhCKH5Oq2id16XjTWRe8-06jZ8z8LnrVqMR-33F3s,79144
|
21
21
|
topologicpy/Speckle.py,sha256=rUS6PCaxIjEF5_fUruxvMH47FMKg-ohcoU0qAUb-yNM,14267
|
22
22
|
topologicpy/Sun.py,sha256=mN3RzlslcZT3APUtwmWIXVbPkJ6OcKTaTf6338gbMJE,37152
|
23
|
-
topologicpy/Topology.py,sha256=
|
23
|
+
topologicpy/Topology.py,sha256=Y0McJCx_eH6y92bMbRfG82Foe0A9K7j3Fp8w-dD9h4k,338825
|
24
24
|
topologicpy/Vector.py,sha256=G4mIIcE5Y-EHfiNV_rxiOz8pJeV3NMEwLu5EOgM0gKA,32653
|
25
25
|
topologicpy/Vertex.py,sha256=bD3JnNhizbp6HFhHRve2LK_y5w27jytCbsagOLxKjZQ,71198
|
26
|
-
topologicpy/Wire.py,sha256=
|
26
|
+
topologicpy/Wire.py,sha256=OtqCUmZeqeNF3baBLmUjB8yBZ3gmzzpSPcD-zzKzz4k,143577
|
27
27
|
topologicpy/__init__.py,sha256=D7ky87CAQMiS2KE6YLvcTLkTgA2PY7rASe6Z23pjp9k,872
|
28
|
-
topologicpy/version.py,sha256=
|
29
|
-
topologicpy-0.7.
|
30
|
-
topologicpy-0.7.
|
31
|
-
topologicpy-0.7.
|
32
|
-
topologicpy-0.7.
|
33
|
-
topologicpy-0.7.
|
28
|
+
topologicpy/version.py,sha256=JKTQ-L90z9YkGvUYq8IRiy8FeivJmY_tDcKgSZ-ssw4,23
|
29
|
+
topologicpy-0.7.18.dist-info/LICENSE,sha256=BRNw73R2WdDBICtwhI3wm3cxsaVqLTAGuRwrTltcfxs,1068
|
30
|
+
topologicpy-0.7.18.dist-info/METADATA,sha256=NiOC_LjQzxSER_rnY5Kz05fNWAbiEOuGxcoFAh2-_VA,10916
|
31
|
+
topologicpy-0.7.18.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
32
|
+
topologicpy-0.7.18.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
|
33
|
+
topologicpy-0.7.18.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|