topologicpy 0.8.22__py3-none-any.whl → 0.8.24__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/ANN.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2024
1
+ # Copyright (C) 2025
2
2
  # Wassim Jabi <wassim.jabi@gmail.com>
3
3
  #
4
4
  # This program is free software: you can redistribute it and/or modify it under
topologicpy/Aperture.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2024
1
+ # Copyright (C) 2025
2
2
  # Wassim Jabi <wassim.jabi@gmail.com>
3
3
  #
4
4
  # This program is free software: you can redistribute it and/or modify it under
topologicpy/BVH.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2024
1
+ # Copyright (C) 2025
2
2
  # Wassim Jabi <wassim.jabi@gmail.com>
3
3
  #
4
4
  # This program is free software: you can redistribute it and/or modify it under
topologicpy/CSG.py ADDED
@@ -0,0 +1,389 @@
1
+ # Copyright (C) 2025
2
+ # Wassim Jabi <wassim.jabi@gmail.com>
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify it under
5
+ # the terms of the GNU Affero General Public License as published by the Free Software
6
+ # Foundation, either version 3 of the License, or (at your option) any later
7
+ # version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful, but WITHOUT
10
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
+ # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
12
+ # details.
13
+ #
14
+ # You should have received a copy of the GNU Affero General Public License along with
15
+ # this program. If not, see <https://www.gnu.org/licenses/>.
16
+
17
+ class CSG():
18
+ @staticmethod
19
+ def _unique_coords(used_coords=[], width=10, length=10, height=10, max_attempts=1000, mantissa=6, tolerance=0.0001):
20
+ import math
21
+ import random
22
+
23
+ def is_too_close(p1, p2):
24
+ return math.dist(p1, p2) < tolerance
25
+
26
+ if used_coords == []:
27
+ return [0,0,0]
28
+
29
+ attempts = 0
30
+ while attempts < max_attempts:
31
+ x = round(random.uniform(0, width), mantissa)
32
+ y = round(random.uniform(0, length), mantissa)
33
+ z = round(random.uniform(0, height), mantissa)
34
+ candidate = [x, y, z]
35
+ if all(not is_too_close(candidate, used) for used in used_coords):
36
+ return candidate
37
+ attempts += 1
38
+
39
+ raise RuntimeError("Could not find a unique coordinate within the attempt limit.")
40
+
41
+ @staticmethod
42
+ def AddTopologyVertex(graph, topology, matrix: list = None, mantissa: int = 6, tolerance: float = 0.0001, silent: bool = False):
43
+ """
44
+ Adds a topology vertex to the graph.
45
+
46
+ Parameters
47
+ ----------
48
+ graph : topologic_core.Graph
49
+ The input graph.
50
+ topology : topologic_core.Topology
51
+ The input topology..
52
+ matrix : list , optional
53
+ The desired 4X4 transformation matrix to apply to the result before any further operations. The default is None.
54
+ mantissa : int , optional
55
+ The desired length of the mantissa. The default is 6.
56
+ tolerance : float , optional
57
+ The desired tolerance. The default is 0.0001.
58
+ silent : bool , optional
59
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
60
+
61
+ Returns
62
+ -------
63
+ topologic_core.Vertex
64
+ The added vertex.
65
+ """
66
+
67
+ from topologicpy.Vertex import Vertex
68
+ from topologicpy.Dictionary import Dictionary
69
+ from topologicpy.Matrix import Matrix
70
+ from topologicpy.Topology import Topology
71
+ from topologicpy.Graph import Graph
72
+
73
+ if matrix == None:
74
+ matrix = Matrix.Identity()
75
+ if graph == None:
76
+ used_coords = []
77
+ else:
78
+ used_coords = [Vertex.Coordinates(v, mantissa=mantissa) for v in Graph.Vertices(graph)]
79
+ loc = CSG._unique_coords(used_coords=used_coords, width=10, length=10, height=10, max_attempts=1000, mantissa=mantissa, tolerance=tolerance)
80
+ v = Vertex.ByCoordinates(loc)
81
+ keys = ["brep",
82
+ "brepType",
83
+ "brepTypeString",
84
+ "matrix",
85
+ "type",
86
+ "id"]
87
+ values = [Topology.BREPString(topology),
88
+ Topology.Type(topology),
89
+ Topology.TypeAsString(topology),
90
+ matrix,
91
+ "topology",
92
+ Topology.UUID(v)]
93
+
94
+ d = Dictionary.ByKeysValues(keys, values)
95
+ v = Topology.SetDictionary(v, d)
96
+ if graph == None:
97
+ graph = Graph.ByVerticesEdges([v], [])
98
+ else:
99
+ graph = Graph.AddVertex(graph, v, tolerance=tolerance, silent=silent)
100
+ return v
101
+
102
+ @staticmethod
103
+ def AddOperationVertex(graph, operation, a, b, matrix = None, mantissa: int = 6, tolerance: float = 0.0001, silent: bool = False):
104
+ """
105
+ Adds an operation vertex to the graph that performs a CSG operation on two child vertices. For ordered operations, the order of a and b inputs is important.
106
+
107
+ Parameters
108
+ ----------
109
+ graph : topologic_core.Graph
110
+ The input graph.
111
+ operation : str
112
+ The operation to perform. One of "union", "difference", "intersection", "xor", "merge", "impose", "imprint", "slice".
113
+ a : topologic_core.Vertex
114
+ The first input vertex.
115
+ b : topologic_core.Vertex
116
+ The second input vertex.
117
+ matrix : list , optional
118
+ The desired 4X4 transformation matrix to apply to the result before any further operations. The default is None.
119
+ mantissa : int , optional
120
+ The desired length of the mantissa. The default is 6.
121
+ tolerance : float , optional
122
+ The desired tolerance. The default is 0.0001.
123
+ silent : bool , optional
124
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
125
+
126
+ Returns
127
+ -------
128
+ topologic_core.Vertex
129
+ The added vertex.
130
+ """
131
+ from topologicpy.Vertex import Vertex
132
+ from topologicpy.Dictionary import Dictionary
133
+ from topologicpy.Topology import Topology
134
+ from topologicpy.Graph import Graph
135
+
136
+ if graph == None:
137
+ used_coords = []
138
+ else:
139
+ used_coords = [Vertex.Coordinates(v, mantissa=mantissa) for v in Graph.Vertices(graph)]
140
+ loc = CSG._unique_coords(used_coords=used_coords, width=10, length=10, height=10, max_attempts=1000, mantissa=mantissa, tolerance=tolerance)
141
+ v = Vertex.ByCoordinates(loc)
142
+ a_id = Dictionary.ValueAtKey(Topology.Dictionary(a), "id")
143
+ b_id = Dictionary.ValueAtKey(Topology.Dictionary(b), "id")
144
+
145
+ keys = ["brep",
146
+ "brepType",
147
+ "brepTypeString",
148
+ "matrix",
149
+ "type",
150
+ "id",
151
+ "operation",
152
+ "a_id",
153
+ "b_id"]
154
+ values = [None,
155
+ None,
156
+ None,
157
+ matrix,
158
+ "operation",
159
+ Topology.UUID(v),
160
+ operation,
161
+ a_id,
162
+ b_id]
163
+
164
+ d = Dictionary.ByKeysValues(keys, values)
165
+ v = Topology.SetDictionary(v, d)
166
+ return v
167
+
168
+ @staticmethod
169
+ def Connect(graph, vertexA, vertexB, tolerance: float = 0.0001, silent: bool = False):
170
+ """
171
+ Connects two vertices in the graph with a directional edge from vertexA to vertexB.
172
+
173
+ Parameters
174
+ ----------
175
+ graph : topologic_core.Graph
176
+ The input graph.
177
+ vertexA : topologic_core.Vertex
178
+ The first input vertex.
179
+ vertexB : topologic_core.Vertex
180
+ The second input vertex.
181
+ matrix : list , optional
182
+ The desired 4X4 transformation matrix to apply to the result before any further operations. The default is None.
183
+ tolerance : float , optional
184
+ The desired tolerance. The default is 0.0001.
185
+ silent : bool , optional
186
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
187
+
188
+ Returns
189
+ -------
190
+ topologic_core.Vertex
191
+ The added vertex.
192
+ """
193
+ from topologicpy.Graph import Graph
194
+ from topologicpy.Edge import Edge
195
+ from topologicpy.Topology import Topology
196
+
197
+ if not Topology.IsInstance(vertexA, "Vertex"):
198
+ if not silent:
199
+ print("CSG.Connect - Error: The input vertexA parameter is not a valid vertex. Returning None.")
200
+ return None
201
+ if not Topology.IsInstance(vertexB, "Vertex"):
202
+ if not silent:
203
+ print("CSG.Connect - Error: The input vertexB parameter is not a valid vertex. Returning None.")
204
+ return None
205
+ edge = Edge.ByVertices(vertexA, vertexB, tolerance=tolerance)
206
+ if graph == None:
207
+ vertices = [vertexA, vertexB]
208
+ edges = [edge]
209
+ else:
210
+ if not Topology.IsInstance(graph, "Graph"):
211
+ if not silent:
212
+ print("CSG.Connect - Error: The input graph parameter is not a valid graph. Returning None.")
213
+ return None
214
+ vertices = Graph.Vertices(graph)
215
+ edges = Graph.Edges(graph)
216
+ if len(edges) > 0:
217
+ edges.append(edge)
218
+ else:
219
+ edges = [edge]
220
+ graph = Graph.ByVerticesEdges(vertices, edges)
221
+ return graph
222
+
223
+ @staticmethod
224
+ def Init():
225
+ """
226
+ Returns an initial empty graph.
227
+
228
+ Parameters
229
+ ----------
230
+
231
+ Returns
232
+ -------
233
+ topologic_core.Graph
234
+ The initialized empty graph.
235
+ """
236
+
237
+ from topologicpy.Graph import Graph
238
+
239
+ return Graph.ByVerticesEdges([], [])
240
+
241
+ @staticmethod
242
+ def Invoke(graph, silent: bool = False):
243
+ """
244
+ Traverses the graph and evaluates all CSG operations from leaves to root, returning the final result.
245
+
246
+ Parameters
247
+ ----------
248
+ graph : topologic_core.Graph
249
+ The input graph.
250
+ silent : bool , optional
251
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
252
+
253
+ Returns
254
+ -------
255
+ topologic_core.Topology
256
+ The final topology.
257
+ """
258
+
259
+ from topologicpy.Topology import Topology
260
+ from topologicpy.Dictionary import Dictionary
261
+ from topologicpy.Graph import Graph
262
+
263
+ if not Topology.IsInstance(graph, "Graph"):
264
+ if not silent:
265
+ print("CSG.Connect - Error: The input graphA parameter is not a valid graph. Returning None.")
266
+ return None
267
+
268
+ def traverse(vertex):
269
+ d = Topology.Dictionary(vertex)
270
+ node_type = Dictionary.ValueAtKey(d, "type")
271
+
272
+ if node_type == "topology":
273
+ topology = Topology.ByBREPString(Dictionary.ValueAtKey(d, "brep"))
274
+ matrix = Dictionary.ValueAtKey(d, "matrix")
275
+ topology = Topology.Transform(topology, matrix)
276
+ topology = Topology.SetDictionary(topology, d)
277
+ return topology
278
+
279
+ elif node_type == "operation":
280
+ op = Dictionary.ValueAtKey(d, "operation")
281
+ a_id = Dictionary.ValueAtKey(d, "a_id")
282
+
283
+ children = Graph.IncomingVertices(graph, vertex, directed=True)
284
+ if len(children) != 2:
285
+ if not silent:
286
+ print(f"CSG.Invoke - Error: Operation '{op}' must have exactly 2 children. Returning None.")
287
+ return None
288
+
289
+ child_0 = children[0]
290
+ child_1 = children[1]
291
+ child_0_id = Dictionary.ValueAtKey(Topology.Dictionary(child_0), "id")
292
+ if child_0_id != a_id:
293
+ child_0 = children[1]
294
+ child_1 = children[0]
295
+ a = traverse(child_0)
296
+ b = traverse(child_1)
297
+ if op.lower() == "union":
298
+ result = Topology.Union(a, b, silent=silent)
299
+ elif op.lower() == "intersection":
300
+ result = Topology.Intersect(a, b, silent=silent)
301
+ elif op.lower() == "difference":
302
+ result = Topology.Difference(a, b, silent=silent)
303
+ elif op.lower() == "xor" or "sym" in op.lower():
304
+ result = Topology.SymmeticDifference(a, b, silent=silent)
305
+ elif op.lower() == "merge":
306
+ result = Topology.Merge(a, b, silent=silent)
307
+ elif op.lower() == "impose":
308
+ result = Topology.Impose(a, b, silent=silent)
309
+ elif op.lower() == "imprint":
310
+ result = Topology.Imprint(a, b, silent=silent)
311
+ elif op.lower() == "slice":
312
+ result = Topology.Slice(a, b, silent=silent)
313
+ else:
314
+ if not silent:
315
+ print(f"CSG.Invoke - Error: Unknown operation '{op}'. Returning None.")
316
+ return None
317
+ keys = ["brep",
318
+ "brepType",
319
+ "brepTypeString"]
320
+ values = [Topology.BREPString(result),
321
+ Topology.Type(result),
322
+ Topology.TypeAsString(result)]
323
+ d = Dictionary.SetValuesAtKeys(d, keys=keys, values=values)
324
+ vertex = Topology.SetDictionary(vertex, d)
325
+ matrix = Dictionary.ValueAtKey(d, "matrix")
326
+ if not matrix == None:
327
+ result = Topology.Transform(result, matrix)
328
+ result = Topology.SetDictionary(result, d)
329
+ return result
330
+ else:
331
+ if not silent:
332
+ print(f"CSG.Invoke - Error: Unknown node type '{node_type}'. Returning None.")
333
+ return None
334
+
335
+ # Assume root is the vertex with no outgoing edges
336
+ roots = [v for v in Graph.Vertices(graph) if not Graph.OutgoingVertices(graph, v, directed=True)]
337
+ if len(roots) != 1:
338
+ if not silent:
339
+ print("CSG.Invoke - Error: Graph must have exactly one root node. Returning None.")
340
+ return None
341
+
342
+ return traverse(roots[0])
343
+
344
+ def Topologies(graph, xOffset: float = 0, yOffset: float = 0, zOffset: float = 0, scale: float = 1, silent: bool = False):
345
+ """
346
+ Places each geometry (using its centroid) at its corresponding vertex location in the graph.
347
+
348
+ Parameters
349
+ ----------
350
+ graph : topologic_core.Graph
351
+ The input graph.
352
+ xOffset : float , optional
353
+ An additional x offset. The default is 0.
354
+ yOffset : float , optional
355
+ An additional y offset. The default is 0.
356
+ zOffset : float , optional
357
+ An additional z offset. The default is 0.
358
+ scale : float , optional
359
+ A desired scale to resize the placed topologies. The default is 1.
360
+ silent : bool , optional
361
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
362
+
363
+ Returns
364
+ -------
365
+ list
366
+ The list of topologies placed at their corresponding location in the graph.
367
+ """
368
+
369
+ from topologicpy.Topology import Topology
370
+ from topologicpy.Dictionary import Dictionary
371
+ from topologicpy.Graph import Graph
372
+
373
+ if not Topology.IsInstance(graph, "Graph"):
374
+ if not silent:
375
+ print("CSG.Topologies - Error: The input graph parameter is not a valid graph. Returning None.")
376
+ return None
377
+ placed_topologies= []
378
+
379
+ for vertex in Graph.Vertices(graph):
380
+ geom = Topology.ByBREPString(Dictionary.ValueAtKey(Topology.Dictionary(vertex), "brep"))
381
+ originA = Topology.Centroid(geom)
382
+ geom = Topology.Scale(geom, origin=originA, x=scale, y=scale, z=scale)
383
+ originB = vertex
384
+ placed = Topology.Place(geom, originA, originB)
385
+ placed = Topology.Translate(placed, x=xOffset, y=yOffset, z=zOffset)
386
+ placed_topologies.append(placed)
387
+
388
+ return placed_topologies
389
+
topologicpy/Cell.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2024
1
+ # Copyright (C) 2025
2
2
  # Wassim Jabi <wassim.jabi@gmail.com>
3
3
  #
4
4
  # This program is free software: you can redistribute it and/or modify it under
@@ -850,6 +850,43 @@ class Cell():
850
850
  print("Cell.ContainmentStatus - Error: Could not determine containment status. Returning None.")
851
851
  return None
852
852
 
853
+ @staticmethod
854
+ def Cube(origin = None,
855
+ size: float = 1,
856
+ uSides: int = 1, vSides: int = 1, wSides: int = 1,
857
+ direction: list = [0, 0, 1], placement: str ="center", tolerance: float = 0.0001):
858
+ """
859
+ Creates a cube.
860
+
861
+ Parameters
862
+ ----------
863
+ origin : topologic_core.Vertex , optional
864
+ The origin location of the cube. The default is None which results in the cube being placed at (0, 0, 0).
865
+ size : float , optional
866
+ The size of the cube. The default is 1.
867
+ uSides : int , optional
868
+ The number of sides along the width. The default is 1.
869
+ vSides : int , optional
870
+ The number of sides along the length. The default is 1.
871
+ wSides : int , optional
872
+ The number of sides along the height. The default is 1.
873
+ direction : list , optional
874
+ The vector representing the up direction of the cube. The default is [0, 0, 1].
875
+ placement : str , optional
876
+ The description of the placement of the origin of the cube. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
877
+ tolerance : float , optional
878
+ The desired tolerance. The default is 0.0001.
879
+
880
+ Returns
881
+ -------
882
+ topologic_core.Cell
883
+ The created cube.
884
+
885
+ """
886
+ return Cell.Prism(origin=origin, width=size, length=size, height=size,
887
+ uSides=uSides, vSides=vSides, wSides=wSides,
888
+ direction=direction, placement=placement, tolerance=tolerance)
889
+
853
890
  @staticmethod
854
891
  def Cylinder(origin = None, radius: float = 0.5, height: float = 1, uSides: int = 16, vSides: int = 1, direction: list = [0, 0, 1],
855
892
  placement: str = "center", mantissa: int = 6, tolerance: float = 0.0001):
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2024
1
+ # Copyright (C) 2025
2
2
  # Wassim Jabi <wassim.jabi@gmail.com>
3
3
  #
4
4
  # This program is free software: you can redistribute it and/or modify it under
@@ -450,6 +450,44 @@ class CellComplex():
450
450
  _ = cellComplex.Cells(None, cells) # Hook to Core
451
451
  return cells
452
452
 
453
+ @staticmethod
454
+ def Cube(origin= None,
455
+ size: float = 1.0,
456
+ uSides: int = 2, vSides: int = 2, wSides: int = 2,
457
+ direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001):
458
+ """
459
+ Creates a cube with internal cells.
460
+
461
+ Parameters
462
+ ----------
463
+ origin : topologic_core.Vertex , optional
464
+ The origin location of the cube. The default is None which results in the cube being placed at (0, 0, 0).
465
+ size : float , optional
466
+ The size of the cube. The default is 1.
467
+ uSides : int , optional
468
+ The number of sides along the width. The default is 1.
469
+ vSides : int, optional
470
+ The number of sides along the length. The default is 1.
471
+ wSides : int , optional
472
+ The number of sides along the height. The default is 1.
473
+ direction : list , optional
474
+ The vector representing the up direction of the cube. The default is [0, 0, 1].
475
+ placement : str , optional
476
+ The description of the placement of the origin of the cube. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
477
+ tolerance : float , optional
478
+ The desired tolerance. The default is 0.0001.
479
+
480
+ Returns
481
+ -------
482
+ topologic_core.CellComplex
483
+ The created cube.
484
+
485
+ """
486
+ return CellComplex.Prism(origin=origin,
487
+ width=size, length=size, height=size,
488
+ uSides=uSides, vSides=vSides, wSides=wSides,
489
+ direction=direction, placement=placement, tolerance=tolerance)
490
+
453
491
  @staticmethod
454
492
  def Decompose(cellComplex, tiltAngle: float = 10.0, tolerance: float = 0.0001) -> dict:
455
493
  """
topologicpy/Cluster.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2024
1
+ # Copyright (C) 2025
2
2
  # Wassim Jabi <wassim.jabi@gmail.com>
3
3
  #
4
4
  # This program is free software: you can redistribute it and/or modify it under
topologicpy/Color.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2024
1
+ # Copyright (C) 2025
2
2
  # Wassim Jabi <wassim.jabi@gmail.com>
3
3
  #
4
4
  # This program is free software: you can redistribute it and/or modify it under
topologicpy/Context.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2024
1
+ # Copyright (C) 2025
2
2
  # Wassim Jabi <wassim.jabi@gmail.com>
3
3
  #
4
4
  # This program is free software: you can redistribute it and/or modify it under
topologicpy/DGL.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2024
1
+ # Copyright (C) 2025
2
2
  # Wassim Jabi <wassim.jabi@gmail.com>
3
3
  #
4
4
  # This program is free software: you can redistribute it and/or modify it under
topologicpy/Dictionary.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2024
1
+ # Copyright (C) 2025
2
2
  # Wassim Jabi <wassim.jabi@gmail.com>
3
3
  #
4
4
  # This program is free software: you can redistribute it and/or modify it under
topologicpy/Edge.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2024
1
+ # Copyright (C) 2025
2
2
  # Wassim Jabi <wassim.jabi@gmail.com>
3
3
  #
4
4
  # This program is free software: you can redistribute it and/or modify it under
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2024
1
+ # Copyright (C) 2025
2
2
  # Wassim Jabi <wassim.jabi@gmail.com>
3
3
  #
4
4
  # This program is free software: you can redistribute it and/or modify it under
topologicpy/Face.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2024
1
+ # Copyright (C) 2025
2
2
  # Wassim Jabi <wassim.jabi@gmail.com>
3
3
  #
4
4
  # This program is free software: you can redistribute it and/or modify it under