topologicpy 0.8.21__py3-none-any.whl → 0.8.23__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
@@ -773,7 +773,7 @@ class Dictionary():
773
773
  return None
774
774
 
775
775
  @staticmethod
776
- def ValueAtKey(dictionary, key, silent=False):
776
+ def ValueAtKey(dictionary, key, defaultValue=None, silent=False):
777
777
  """
778
778
  Returns the value of the input key in the input dictionary.
779
779
 
@@ -783,6 +783,8 @@ class Dictionary():
783
783
  The input dictionary.
784
784
  key : string
785
785
  The input key.
786
+ defaultValue : any , optional
787
+ The default value to return if the key or value are not found. The default is None.
786
788
  silent : bool , optional
787
789
  If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
788
790
 
@@ -804,10 +806,10 @@ class Dictionary():
804
806
  return None
805
807
  if Topology.IsInstance(dictionary, "Dictionary"):
806
808
  dic = Dictionary.PythonDictionary(dictionary)
807
- return dic.get(key, None)
809
+ return dic.get(key, defaultValue)
808
810
  elif isinstance(dictionary, dict):
809
- return dictionary.get(key, None)
810
- return None
811
+ return dictionary.get(key, defaultValue)
812
+ return defaultValue
811
813
 
812
814
  # if isinstance(dictionary, dict):
813
815
  # attr = dictionary[key]
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
topologicpy/Graph.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
@@ -4379,12 +4379,17 @@ class Graph:
4379
4379
 
4380
4380
  @staticmethod
4381
4381
  def Compare(graphA, graphB,
4382
- weightAttributes: float = 1.0,
4383
- weightGeometry: float = 1.0,
4384
- weightMetrics: float = 1.0,
4385
- weightStructure: float = 1.0,
4386
- weightWL: float = 1.0,
4387
- weightJaccard: float = 1.0,
4382
+ weightAttributes: float = 0.0,
4383
+ weightGeometry: float = 0.0,
4384
+ weightBetwennessCentrality: float = 0.0,
4385
+ weightClosenessCentrality: float = 0.0,
4386
+ weightDegreeCentrality: float = 0.0,
4387
+ weightDiameter: float = 0.0,
4388
+ weightGlobalClusteringCoefficient: float = 0.0,
4389
+ weightPageRank: float = 0.0,
4390
+ weightStructure: float = 0.0,
4391
+ weightWeisfeilerLehman: float = 0.0,
4392
+ weightJaccard: float = 0.0,
4388
4393
  vertexIDKey: str = "id",
4389
4394
  edgeWeightKey: str = None,
4390
4395
  iterations: int = 3,
@@ -4402,18 +4407,28 @@ class Graph:
4402
4407
  graphB : topologic Graph
4403
4408
  The second input graph.
4404
4409
  weightAttributes : float , optional
4405
- The desired weight for attribute similarity (dictionary key overlap at vertices). Default is 1.0.
4410
+ The desired weight for attribute similarity (dictionary key overlap at vertices). Default is 0.0.
4411
+ weightBetwennessCentrality : float , optional
4412
+ The desired weight for betweenness centrality similarity (graph-level and node-level). Default is 0.0.
4413
+ weightClosenessCentrality : float , optional
4414
+ The desired weight for closeness centrality similarity (graph-level and node-level). Default is 0.0.
4415
+ weightDegreeCentrality : float , optional
4416
+ The desired weight for degree centrality similarity (graph-level and node-level). Default is 0.0.
4417
+ weightDiameter : float , optional
4418
+ The desired weight for diameter similarity (graph-level and node-level). Default is 0.0.
4406
4419
  weightGeometry : float , optional
4407
- The desired weight for geometric similarity (vertex positions). Default is 1.0.
4408
- weightMetrics : float , optional
4409
- The desired weight for metric similarity (graph-level and node-level). Default is 1.0.
4410
- The compared metrics are: betweenness centrality, closeness centrality, clustering coefficient, degree, diameter, and pagerank.
4411
- weightStructure : float , optional
4412
- The desired weight for structural similarity (number of vertices and edges). Default is 1.0.
4413
- weightWL : float , optional
4414
- The desired weight for Weisfeiler-Lehman kernel similarity (iterative label propagation). Default is 1.0.
4420
+ The desired weight for geometric similarity (vertex positions). Default is 0.0.
4421
+ weightGlobalClusteringCoefficient : float , optional
4422
+ The desired weight for global clustering coefficient similarity (graph-level and node-level). Default is 0.0.
4415
4423
  weightJaccard: float , optional
4416
- The desired weight for the Weighted Jaccard similarity. Default is 1.0.
4424
+ The desired weight for the Weighted Jaccard similarity. Default is 0.0.
4425
+ weightPageRank : float , optional
4426
+ The desired weight for PageRank similarity (graph-level and node-level). Default is 0.0.
4427
+ weightStructure : float , optional
4428
+ The desired weight for structural similarity (number of vertices and edges). Default is 0.0.
4429
+ weightWeisfeilerLehman : float , optional
4430
+ The desired weight for Weisfeiler-Lehman kernel similarity (iterative label propagation). Default is 0.0.
4431
+
4417
4432
  vertexIDKey: str , optional
4418
4433
  The dictionary key under which to find the unique vertex ID. The default is "id".
4419
4434
  edgeWeightKey: str , optional
@@ -4433,12 +4448,16 @@ class Graph:
4433
4448
  A dictionary of similarity scores between 0 (completely dissimilar) and 1 (identical), based on weighted components.
4434
4449
  The keys in the dictionary are:
4435
4450
  "attribute"
4451
+ "betwenness_centrality"
4452
+ "closeness_centrality"
4453
+ "degree_centrality"
4436
4454
  "geometry"
4437
- "metrics"
4455
+ "global_clustering_coefficient"
4456
+ "jaccard"
4457
+ "pagerank"
4438
4458
  "structure"
4439
- "wl"
4459
+ "weisfeiler_lehman"
4440
4460
  "overall"
4441
-
4442
4461
  """
4443
4462
 
4444
4463
  import hashlib
@@ -4551,40 +4570,56 @@ class Graph:
4551
4570
 
4552
4571
  return numerator / denominator if denominator > 0 else 0.0
4553
4572
 
4554
- def metrics_similarity(graphA, graphB, mantissa=6):
4555
- # Example using global metrics + mean of node metrics
4556
- def safe_mean(lst):
4573
+ def safe_mean(lst):
4557
4574
  return sum(lst)/len(lst) if lst else 0
4558
-
4559
- metrics1 = {
4560
- "closeness": safe_mean(Graph.ClosenessCentrality(graphA)),
4561
- "betweenness": safe_mean(Graph.BetweennessCentrality(graphA)),
4562
- "degree": safe_mean(Graph.DegreeCentrality(graphA)),
4563
- "pagerank": safe_mean(Graph.PageRank(graphA)),
4564
- "clustering": Graph.GlobalClusteringCoefficient(graphA),
4565
- "diameter": Graph.Diameter(graphA)
4566
- }
4567
-
4568
- metrics2 = {
4569
- "closeness": safe_mean(Graph.ClosenessCentrality(graphB)),
4570
- "betweenness": safe_mean(Graph.BetweennessCentrality(graphB)),
4571
- "degree": safe_mean(Graph.DegreeCentrality(graphB)),
4572
- "pagerank": safe_mean(Graph.PageRank(graphB)),
4573
- "clustering": Graph.GlobalClusteringCoefficient(graphB),
4574
- "diameter": Graph.Diameter(graphB)
4575
- }
4576
-
4577
- # Compute similarity as 1 - normalized absolute difference
4578
- similarities = []
4579
- for key in metrics1:
4580
- v1, v2 = metrics1[key], metrics2[key]
4581
- if v1 == 0 and v2 == 0:
4582
- similarities.append(1)
4583
- else:
4584
- diff = abs(v1 - v2) / max(abs(v1), abs(v2), 1e-6)
4585
- similarities.append(1 - diff)
4586
-
4587
- return round(sum(similarities) / len(similarities), mantissa)
4575
+
4576
+ def betweenness_centrality_similarity(graphA, graphB, mantissa=6):
4577
+ v1 = safe_mean(Graph.BetweennessCentrality(graphA))
4578
+ v2 = safe_mean(Graph.BetweennessCentrality(graphB))
4579
+ if v1 == 0 and v2 == 0:
4580
+ return 1
4581
+ diff = abs(v1 - v2) / max(abs(v1), abs(v2), 1e-6)
4582
+ return round((1 - diff), mantissa)
4583
+
4584
+ def closeness_centrality_similarity(graphA, graphB, mantissa=6):
4585
+ v1 = safe_mean(Graph.ClosenessCentrality(graphA))
4586
+ v2 = safe_mean(Graph.ClosenessCentrality(graphB))
4587
+ if v1 == 0 and v2 == 0:
4588
+ return 1
4589
+ diff = abs(v1 - v2) / max(abs(v1), abs(v2), 1e-6)
4590
+ return round((1 - diff), mantissa)
4591
+
4592
+ def degree_centrality_similarity(graphA, graphB, mantissa=6):
4593
+ v1 = safe_mean(Graph.DegreeCentrality(graphA))
4594
+ v2 = safe_mean(Graph.DegreeCentrality(graphB))
4595
+ if v1 == 0 and v2 == 0:
4596
+ return 1
4597
+ diff = abs(v1 - v2) / max(abs(v1), abs(v2), 1e-6)
4598
+ return round((1 - diff), mantissa)
4599
+
4600
+ def diameter_similarity(graphA, graphB, mantissa=6):
4601
+ v1 = Graph.Diameter(graphA)
4602
+ v2 = Graph.Diameter(graphB)
4603
+ if v1 == 0 and v2 == 0:
4604
+ return 1
4605
+ diff = abs(v1 - v2) / max(abs(v1), abs(v2), 1e-6)
4606
+ return round((1 - diff), mantissa)
4607
+
4608
+ def global_clustering_coefficient_similarity(graphA, graphB, mantissa=6):
4609
+ v1 = Graph.GlobalClusteringCoefficient(graphA)
4610
+ v2 = Graph.GlobalClusteringCoefficient(graphB)
4611
+ if v1 == 0 and v2 == 0:
4612
+ return 1
4613
+ diff = abs(v1 - v2) / max(abs(v1), abs(v2), 1e-6)
4614
+ return round((1 - diff), mantissa)
4615
+
4616
+ def pagerank_similarity(graphA, graphB, mantissa=6):
4617
+ v1 = safe_mean(Graph.PageRank(graphA))
4618
+ v2 = safe_mean(Graph.PageRank(graphB))
4619
+ if v1 == 0 and v2 == 0:
4620
+ return 1
4621
+ diff = abs(v1 - v2) / max(abs(v1), abs(v2), 1e-6)
4622
+ return round((1 - diff), mantissa)
4588
4623
 
4589
4624
  def structure_similarity(graphA, graphB, mantissa=6):
4590
4625
  v1 = Graph.Vertices(graphA)
@@ -4640,22 +4675,42 @@ class Graph:
4640
4675
  print("Graph.Compare - Error: The graphB input parameter is not a valid topologic graph. Returning None.")
4641
4676
  return
4642
4677
 
4643
- total_weight = weightAttributes + weightGeometry + weightMetrics + weightStructure + weightWL + weightJaccard
4678
+ total_weight = sum([weightAttributes,
4679
+ weightGeometry,
4680
+ weightBetwennessCentrality,
4681
+ weightClosenessCentrality,
4682
+ weightDegreeCentrality,
4683
+ weightDiameter,
4684
+ weightGlobalClusteringCoefficient,
4685
+ weightPageRank,
4686
+ weightStructure,
4687
+ weightWeisfeilerLehman,
4688
+ weightJaccard])
4644
4689
 
4645
4690
  attribute_score = attribute_similarity(graphA, graphB, mantissa=mantissa) if weightAttributes else 0
4691
+ betweenness_centrality_score = betweenness_centrality_similarity(graphA, graphB, mantissa=mantissa) if weightBetwennessCentrality else 0
4692
+ closeness_centrality_score = closeness_centrality_similarity(graphA, graphB, mantissa=mantissa) if weightClosenessCentrality else 0
4693
+ degree_centrality_score = degree_centrality_similarity(graphA, graphB, mantissa=mantissa) if weightDegreeCentrality else 0
4694
+ diameter_score = diameter_similarity(graphA, graphB, mantissa=mantissa) if weightDiameter else 0
4695
+ global_clustering_coefficient_score = global_clustering_coefficient_similarity(graphA, graphB, mantissa=mantissa) if weightGlobalClusteringCoefficient else 0
4646
4696
  geometry_score = geometry_similarity(graphA, graphB, mantissa=mantissa) if weightGeometry else 0
4647
4697
  jaccard_score = weighted_jaccard_similarity(graphA, graphB, vertexIDKey=vertexIDKey, edgeWeightKey=edgeWeightKey, mantissa=mantissa) if weightJaccard else 0
4648
- metrics_score = metrics_similarity(graphA, graphB, mantissa=mantissa) if weightMetrics else 0
4698
+ pagerank_score = pagerank_similarity(graphA, graphB, mantissa=mantissa) if weightPageRank else 0
4649
4699
  structure_score = structure_similarity(graphA, graphB, mantissa=mantissa) if weightStructure else 0
4650
- wl_score = weisfeiler_lehman_similarity(graphA, graphB, iterations, mantissa=mantissa) if weightWL else 0
4700
+ weisfeiler_lehman_score = weisfeiler_lehman_similarity(graphA, graphB, iterations, mantissa=mantissa) if weightWeisfeilerLehman else 0
4651
4701
 
4652
4702
  weighted_sum = (
4653
4703
  attribute_score * weightAttributes +
4704
+ betweenness_centrality_score * weightBetwennessCentrality +
4705
+ closeness_centrality_score * weightClosenessCentrality +
4706
+ degree_centrality_score * weightDegreeCentrality +
4707
+ diameter_score * weightDiameter +
4654
4708
  geometry_score * weightGeometry +
4709
+ global_clustering_coefficient_score * weightGlobalClusteringCoefficient +
4655
4710
  jaccard_score * weightJaccard +
4656
- metrics_score * weightMetrics +
4711
+ pagerank_score * weightPageRank +
4657
4712
  structure_score * weightStructure +
4658
- wl_score * weightWL
4713
+ weisfeiler_lehman_score * weightWeisfeilerLehman
4659
4714
  )
4660
4715
 
4661
4716
  if total_weight <= 0:
@@ -4665,11 +4720,15 @@ class Graph:
4665
4720
 
4666
4721
  return {
4667
4722
  "attribute": round(attribute_score, mantissa),
4723
+ "betwenness_centrality": round(betweenness_centrality_score, mantissa),
4724
+ "closeness_centrality": round(closeness_centrality_score, mantissa),
4725
+ "degree_centrality": round(degree_centrality_score, mantissa),
4668
4726
  "geometry": round(geometry_score, mantissa),
4727
+ "global_clustering_coefficient": round(global_clustering_coefficient_score, mantissa),
4669
4728
  "jaccard": round(jaccard_score, mantissa),
4670
- "metrics": round(metrics_score, mantissa),
4729
+ "pagerank": round(pagerank_score, mantissa),
4671
4730
  "structure": round(structure_score, mantissa),
4672
- "wl": round(wl_score, mantissa),
4731
+ "weisfeiler_lehman": round(weisfeiler_lehman_score, mantissa),
4673
4732
  "overall": round(overall_score, mantissa)
4674
4733
  }
4675
4734
 
topologicpy/Grid.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/Helper.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/Honeybee.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/Matrix.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/Neo4j.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/Plotly.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
@@ -511,7 +511,7 @@ class Plotly:
511
511
  borderSizes = []
512
512
  for i in range(len(vertices)):
513
513
  sizes.append(size)
514
- labels.append("Vertex_"+str(i+1).zfill(n))
514
+ labels.append("")
515
515
  colors.append(Color.AnyToHex(color))
516
516
  borderColors.append(borderColor)
517
517
  borderSizes.append(size+borderWidth*2)
@@ -543,7 +543,7 @@ class Plotly:
543
543
  if not temp_color == None:
544
544
  colors[m] = Color.AnyToHex(temp_color)
545
545
  if not labelKey == None:
546
- labels[m] = str(Dictionary.ValueAtKey(d, key=labelKey)) or labels[m]
546
+ labels[m] = Dictionary.ValueAtKey(d, key=labelKey, defaultValue=" ")
547
547
  if not sizeKey == None:
548
548
  sizes[m] = Dictionary.ValueAtKey(d, key=sizeKey)
549
549
  if sizes[m] == None:
topologicpy/Polyskel.py CHANGED
@@ -210,7 +210,7 @@ def skeletonize(polygon, holes=None):
210
210
 
211
211
 
212
212
 
213
- # # Copyright (C) 2024
213
+ # # Copyright (C) 2025
214
214
  # # Wassim Jabi <wassim.jabi@gmail.com>
215
215
  # #
216
216
  # # This program is free software: you can redistribute it and/or modify it under
topologicpy/PyG.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/Shell.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/Speckle.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/Sun.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/Topology.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
@@ -846,7 +846,7 @@ class Topology():
846
846
  return apTopologies
847
847
 
848
848
  @staticmethod
849
- def Union(topologyA, topologyB, tranDict=False, tolerance=0.0001):
849
+ def Union(topologyA, topologyB, tranDict: bool = False, tolerance: float = 0.0001, silent: bool = False):
850
850
  """
851
851
  See Topology.Boolean()
852
852
 
@@ -879,15 +879,15 @@ class Topology():
879
879
  else:
880
880
  internal_boundaries.append(eb)
881
881
  return Face.ByWires(external_boundary, internal_boundaries)
882
- return Topology.Boolean(topologyA, topologyB, operation="union", tranDict=tranDict, tolerance=tolerance)
882
+ return Topology.Boolean(topologyA, topologyB, operation="union", tranDict=tranDict, tolerance=tolerance, silent=silent)
883
883
 
884
884
  @staticmethod
885
- def Difference(topologyA, topologyB, tranDict=False, tolerance=0.0001):
885
+ def Difference(topologyA, topologyB, tranDict: bool = False, tolerance: float = 0.0001, silent: bool = False):
886
886
  """
887
887
  See Topology.Boolean().
888
888
 
889
889
  """
890
- return Topology.Boolean(topologyA=topologyA, topologyB=topologyB, operation="difference", tranDict=tranDict, tolerance=tolerance)
890
+ return Topology.Boolean(topologyA=topologyA, topologyB=topologyB, operation="difference", tranDict=tranDict, tolerance=tolerance, silent=silent)
891
891
 
892
892
  @staticmethod
893
893
  def ExternalBoundary(topology):
@@ -1006,63 +1006,63 @@ class Topology():
1006
1006
  return Topology.SelfMerge(Topology.SelfMerge(Cluster.ByTopologies(results)))
1007
1007
 
1008
1008
  @staticmethod
1009
- def SymmetricDifference(topologyA, topologyB, tranDict=False, tolerance=0.0001):
1009
+ def SymmetricDifference(topologyA, topologyB, tranDict: bool = False, tolerance: float = 0.0001, silent: bool = False):
1010
1010
  """
1011
1011
  See Topology.Boolean().
1012
1012
 
1013
1013
  """
1014
- return Topology.Boolean(topologyA=topologyA, topologyB=topologyB, operation="symdif", tranDict=tranDict, tolerance=tolerance)
1014
+ return Topology.Boolean(topologyA=topologyA, topologyB=topologyB, operation="symdif", tranDict=tranDict, tolerance=tolerance, silent=silent)
1015
1015
 
1016
1016
  @staticmethod
1017
- def SymDif(topologyA, topologyB, tranDict=False, tolerance=0.0001):
1017
+ def SymDif(topologyA, topologyB, tranDict: bool = False, tolerance: float = 0.0001, silent: bool = False):
1018
1018
  """
1019
1019
  See Topology.Boolean().
1020
1020
 
1021
1021
  """
1022
- return Topology.Boolean(topologyA=topologyA, topologyB=topologyB, operation="symdif", tranDict=tranDict, tolerance=tolerance)
1022
+ return Topology.Boolean(topologyA=topologyA, topologyB=topologyB, operation="symdif", tranDict=tranDict, tolerance=tolerance, silent=silent)
1023
1023
 
1024
1024
  @staticmethod
1025
- def XOR(topologyA, topologyB, tranDict=False, tolerance=0.0001):
1025
+ def XOR(topologyA, topologyB, tranDict: bool = False, tolerance: float = 0.0001, silent: bool = False):
1026
1026
  """
1027
1027
  See Topology.Boolean().
1028
1028
 
1029
1029
  """
1030
- return Topology.Boolean(topologyA=topologyA, topologyB=topologyB, operation="symdif", tranDict=tranDict, tolerance=tolerance)
1030
+ return Topology.Boolean(topologyA=topologyA, topologyB=topologyB, operation="symdif", tranDict=tranDict, tolerance=tolerance, silent=silent)
1031
1031
 
1032
1032
  @staticmethod
1033
- def Merge(topologyA, topologyB, tranDict=False, tolerance=0.0001):
1033
+ def Merge(topologyA, topologyB, tranDict: bool = False, tolerance: float = 0.0001, silent: bool = False):
1034
1034
  """
1035
1035
  See Topology.Boolean().
1036
1036
 
1037
1037
  """
1038
- return Topology.Boolean(topologyA=topologyA, topologyB=topologyB, operation="merge", tranDict=tranDict, tolerance=tolerance)
1038
+ return Topology.Boolean(topologyA=topologyA, topologyB=topologyB, operation="merge", tranDict=tranDict, tolerance=tolerance, silent=silent)
1039
1039
 
1040
1040
  @staticmethod
1041
- def Slice(topologyA, topologyB, tranDict=False, tolerance=0.0001):
1041
+ def Slice(topologyA, topologyB, tranDict: bool = False, tolerance: float = 0.0001, silent: bool = False):
1042
1042
  """
1043
1043
  See Topology.Boolean().
1044
1044
 
1045
1045
  """
1046
- return Topology.Boolean(topologyA=topologyA, topologyB=topologyB, operation="slice", tranDict=tranDict, tolerance=tolerance)
1046
+ return Topology.Boolean(topologyA=topologyA, topologyB=topologyB, operation="slice", tranDict=tranDict, tolerance=tolerance, silent=silent)
1047
1047
 
1048
1048
  @staticmethod
1049
- def Impose(topologyA, topologyB, tranDict=False, tolerance=0.0001):
1049
+ def Impose(topologyA, topologyB, tranDict: bool = False, tolerance: float = 0.0001, silent: bool = False):
1050
1050
  """
1051
1051
  See Topology.Boolean().
1052
1052
 
1053
1053
  """
1054
- return Topology.Boolean(topologyA=topologyA, topologyB=topologyB, operation="impose", tranDict=tranDict, tolerance=tolerance)
1054
+ return Topology.Boolean(topologyA=topologyA, topologyB=topologyB, operation="impose", tranDict=tranDict, tolerance=tolerance, silent=silent)
1055
1055
 
1056
1056
  @staticmethod
1057
- def Imprint(topologyA, topologyB, tranDict=False, tolerance=0.0001):
1057
+ def Imprint(topologyA, topologyB, tranDict: bool = False, tolerance: float = 0.0001, silent: bool = False):
1058
1058
  """
1059
1059
  See Topology.Boolean().
1060
1060
 
1061
1061
  """
1062
- return Topology.Boolean(topologyA=topologyA, topologyB=topologyB, operation="imprint", tranDict=tranDict, tolerance=tolerance)
1062
+ return Topology.Boolean(topologyA=topologyA, topologyB=topologyB, operation="imprint", tranDict=tranDict, tolerance=tolerance, silent=silent)
1063
1063
 
1064
1064
  @staticmethod
1065
- def Boolean(topologyA, topologyB, operation="union", tranDict=False, tolerance=0.0001):
1065
+ def Boolean(topologyA, topologyB, operation: str = "union", tranDict: bool = False, tolerance: float = 0.0001, silent: bool = False):
1066
1066
  """
1067
1067
  Execute the input boolean operation type on the input operand topologies and return the result. See https://en.wikipedia.org/wiki/Boolean_operation.
1068
1068
 
@@ -1078,6 +1078,8 @@ class Topology():
1078
1078
  If set to True the dictionaries of the operands are merged and transferred to the result. The default is False.
1079
1079
  tolerance : float , optional
1080
1080
  The desired tolerance. The default is 0.0001.
1081
+ silent : bool , optional
1082
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
1081
1083
 
1082
1084
  Returns
1083
1085
  -------
@@ -1087,23 +1089,26 @@ class Topology():
1087
1089
  """
1088
1090
  from topologicpy.Dictionary import Dictionary
1089
1091
  if not Topology.IsInstance(topologyA, "Topology"):
1090
- print("Topology.Boolean - Error: the input topologyA parameter is not a valid topology. Returning None.")
1092
+ if not silent:
1093
+ print("Topology.Boolean - Error: the input topologyA parameter is not a valid topology. Returning None.")
1091
1094
  return None
1092
1095
  if not Topology.IsInstance(topologyB, "Topology"):
1093
- print("Topology.Boolean - Error: the input topologyB parameter is not a valid topology. Returning None.")
1096
+ if not silent:
1097
+ print("Topology.Boolean - Error: the input topologyB parameter is not a valid topology. Returning None.")
1094
1098
  return None
1095
1099
  if not isinstance(operation, str):
1096
- print("Topology.Boolean - Error: the input operation parameter is not a valid string. Returning None.")
1100
+ if not silent:
1101
+ print("Topology.Boolean - Error: the input operation parameter is not a valid string. Returning None.")
1097
1102
  return None
1098
1103
  if not operation.lower() in ["union", "difference", "intersect", "symdif", "merge", "slice", "impose", "imprint"]:
1099
- print("Topology.Boolean - Error: the input operation parameter is not a recognized operation. Returning None.")
1104
+ if not silent:
1105
+ print("Topology.Boolean - Error: the input operation parameter is not a recognized operation. Returning None.")
1100
1106
  return None
1101
1107
  if not isinstance(tranDict, bool):
1102
- print("Topology.Boolean - Error: the input tranDict parameter is not a valid boolean. Returning None.")
1108
+ if not silent:
1109
+ print("Topology.Boolean - Error: the input tranDict parameter is not a valid boolean. Returning None.")
1103
1110
  return None
1104
1111
  topologyC = None
1105
- #topologyC = Topology.Intersect(topologyA, topologyB)
1106
- #try:
1107
1112
  if operation.lower() == "union":
1108
1113
  topologyC = topologyA.Union(topologyB, False)
1109
1114
  elif operation.lower() == "difference":
topologicpy/Vector.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/Vertex.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/Wire.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/__init__.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/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = '0.8.21'
1
+ __version__ = '0.8.23'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: topologicpy
3
- Version: 0.8.21
3
+ Version: 0.8.23
4
4
  Summary: An AI-Powered Spatial Modelling and Analysis Software Library for Architecture, Engineering, and Construction.
5
5
  Author-email: Wassim Jabi <wassim.jabi@gmail.com>
6
6
  License: AGPL v3 License
@@ -0,0 +1,37 @@
1
+ topologicpy/ANN.py,sha256=DrNAhNHp-jSvsPc1fb7KVPU46cYmejAvghhknOM430Y,47932
2
+ topologicpy/Aperture.py,sha256=wNn5miB_IrGCBYuQ18HXQYRva20dUC3id4AJCulL7to,2723
3
+ topologicpy/BVH.py,sha256=PAezG_P_W5_FUjUGB-HRRb4bzesE9DW89G2Vm-6ChWw,12932
4
+ topologicpy/CSG.py,sha256=6vawdxs0_r7i9ElnwDO1Mouv_yPOTBr_4IhBnfVsBHk,15680
5
+ topologicpy/Cell.py,sha256=F8ifMa-3NI5GYRx86J0cokgdoLduBiaw5DIKQFMHtCs,119904
6
+ topologicpy/CellComplex.py,sha256=5O15NirkK2M1AvNcq9Z8N5lNb-hD9kGNXIJXWJJuj7k,59931
7
+ topologicpy/Cluster.py,sha256=BSaLcHe4TinFTIwjFo4660rPX2y9Lqly2hv48WCq6fE,58606
8
+ topologicpy/Color.py,sha256=ZVVQRKGjebY9aOU1gpN_AbssdRRiVKlZV3f8TrsTNgg,20307
9
+ topologicpy/Context.py,sha256=G3CwMvN8Jw2rnQRwB-n4MaQq_wLS0vPimbXKwsdMJ80,3055
10
+ topologicpy/DGL.py,sha256=HQXy9iDnrvWGDxaBfe5pRbweQ2zLBvAf6UdjfhKkQYI,139041
11
+ topologicpy/Dictionary.py,sha256=Lf24WHW8q_RCq0l8VpT3XJTn6UuStY66JI4Lb4W08jI,34126
12
+ topologicpy/Edge.py,sha256=pu4tZbRbK8qx2oqRbwHAeKuwU2X8JFGPSJjJMTJw8Q0,71418
13
+ topologicpy/EnergyModel.py,sha256=Pyb28gDDwhzlQIH0xqAygqS0P3SJxWyyV7OWS_AAfRs,53856
14
+ topologicpy/Face.py,sha256=7K46gB_UIKjKEKyzyY0JqGarqjwjH0ggS-JQTpDtWC4,184847
15
+ topologicpy/Graph.py,sha256=29oDBceDUoZVbwZ4ajBJWfQD4JIPYGUgDeEqmm7CBX0,527640
16
+ topologicpy/Grid.py,sha256=qRnFUvs079zMOZ6COWzBX6408niI7HyNz-BM0VRguXY,18245
17
+ topologicpy/Helper.py,sha256=JdvC30WMrla46mTj5TdwCV_bRv-6y8vK5Bkx0prluy4,29100
18
+ topologicpy/Honeybee.py,sha256=yctkwfdupKnp7bAOjP1Z4YaYpRrWoMEb4gz9Z5zaWwE,21751
19
+ topologicpy/Matrix.py,sha256=BHGDRkBn1pf5DkRoY8feAhDGHTF3bjFM4jluiEb_A0w,22779
20
+ topologicpy/Neo4j.py,sha256=vNMaqTWerwr-3luLjYEXNhf8T97aFee6x5sIKBHY73s,22392
21
+ topologicpy/Plotly.py,sha256=eHTKT8vOHJDLwH4xypj52atGLR0dGic45d-BiPA55Q8,119219
22
+ topologicpy/Polyskel.py,sha256=oVfM4lqSMPTjnkHfsRU9VI8Blt6Vf0LVPkD9ebz7Wmw,27082
23
+ topologicpy/PyG.py,sha256=zvV6jtnol_aFiN6JRoMpYwBVfOU2aFs9gdWSdEo6mtU,109757
24
+ topologicpy/Shell.py,sha256=3zUSf0VfkAF73ZyTYNWYW21x4-vyfi7JkYu_r5GTVIc,87983
25
+ topologicpy/Speckle.py,sha256=-eiTqJugd7pHiHpD3pDUcDO6CGhVyPV14HFRzaqEoaw,18187
26
+ topologicpy/Sun.py,sha256=_VBBAUIDhvpkp72JBZlv7k9qx9jYubm3yM56UZ1Nc6c,36837
27
+ topologicpy/Topology.py,sha256=BKiVJ-R5FfwXJ2FYp1V8pjaPoMxRm83MGe2RzBx0_tQ,479205
28
+ topologicpy/Vector.py,sha256=mx7fgABdioikPWM9HzXKzmqfx3u_XBcU_jlLD4qK2x8,42407
29
+ topologicpy/Vertex.py,sha256=PIwfbA7_TxK_dSGlSeM5mson97TRr4dYrfZyOLgO150,80913
30
+ topologicpy/Wire.py,sha256=eRs4PM7h4yU5v6umPh0oBJR4cN8BwsqlVroaFdnvK4w,228499
31
+ topologicpy/__init__.py,sha256=RMftibjgAnHB1vdL-muo71RwMS4972JCxHuRHOlU428,928
32
+ topologicpy/version.py,sha256=x1ObPzSnRGxtlZbyDdxO7Th6BX-tC0y4aFEdmCRhvqg,23
33
+ topologicpy-0.8.23.dist-info/licenses/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
34
+ topologicpy-0.8.23.dist-info/METADATA,sha256=vQCjkz3HxMfw9u7K87Q8eiY5cvWggKY1QuZWIgm8fBE,10535
35
+ topologicpy-0.8.23.dist-info/WHEEL,sha256=wXxTzcEDnjrTwFYjLPcsW_7_XihufBwmpiBeiXNBGEA,91
36
+ topologicpy-0.8.23.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
37
+ topologicpy-0.8.23.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (78.1.0)
2
+ Generator: setuptools (80.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,36 +0,0 @@
1
- topologicpy/ANN.py,sha256=m_WxD1lgQqDhUpaM20Lia6TmJACDYaAE96wigsi-99U,47932
2
- topologicpy/Aperture.py,sha256=p9pUzTQSBWoUaDiug1V1R1hnEIEwYSXFg2t7iRAmNRY,2723
3
- topologicpy/BVH.py,sha256=1q2lR5eDs7Wnwv7M-Kr7Cj3GG_iy7d1ddaZqWGHdX-w,12932
4
- topologicpy/Cell.py,sha256=BN6hjYpN6Fse0mn7d7oHh65D7c6Mt1Yu7iK3lEp_Cj0,118262
5
- topologicpy/CellComplex.py,sha256=udDzbMub3d2QYn1XGMzt3F9Su2VXuAGvn0eoTtOIn3g,58207
6
- topologicpy/Cluster.py,sha256=o5jdMRpcGfSGGiXQdFg-e9XcnBF5AqTj3xb1nSpwJWE,58606
7
- topologicpy/Color.py,sha256=q9xsGmxFMz7sQKmygwSVS12GaTRB-OT0-_i6t3-cthE,20307
8
- topologicpy/Context.py,sha256=ppApYKngZZCQBFWaxIMi2z2dokY23c935IDCBosxDAE,3055
9
- topologicpy/DGL.py,sha256=M_znFtyPBJJz-iXLYZs2wwBj24fhevIo739dGha0chM,139041
10
- topologicpy/Dictionary.py,sha256=7h-Gszgnt2OEOvOSADJ4pa-mTNlhQ9cuIiB5WHEW6aY,33949
11
- topologicpy/Edge.py,sha256=yxkCVDYBflJNEYxnjMmlyvbkpg8TNy7y5bSH3yQ4jzs,71418
12
- topologicpy/EnergyModel.py,sha256=UoQ9Jm-hYsN383CbcLKw-y6BKitRHj0uyh84yQ-8ACg,53856
13
- topologicpy/Face.py,sha256=SlhB8L7BpDjd4a9YZE4UJ3zoGuF1oq9MSpuesEWro_Q,184847
14
- topologicpy/Graph.py,sha256=Iq2UI-CdW3usxpgxRi-caxQeUmkFfGhD37c6mPT2Gd8,523908
15
- topologicpy/Grid.py,sha256=2s9cSlWldivn1i9EUz4OOokJyANveqmRe_vR93CAndI,18245
16
- topologicpy/Helper.py,sha256=4H5KPiv_eiEs489UOOyGLe9RaeoZIfmMh3mk_YCHmXg,29100
17
- topologicpy/Honeybee.py,sha256=uDVtDbloydNoaBFcSNukKL_2PLyD6XKkCp1VHz1jtaU,21751
18
- topologicpy/Matrix.py,sha256=i22RLP5ebUAMuU7V1tZ__Z4lf1pg9fzq9nENsDZUV74,22779
19
- topologicpy/Neo4j.py,sha256=BKOF29fRgXmdpMGkrNzuYbyqgCJ6ElPPMYlfTxXiVbc,22392
20
- topologicpy/Plotly.py,sha256=gSe3tWLgF75-rPDjqw7riUIJpv0j_K7hedoxi3-OptE,119244
21
- topologicpy/Polyskel.py,sha256=ro5in--VT_uag55r5xymU5ufyAahsovIiJwyiqG_qH8,27082
22
- topologicpy/PyG.py,sha256=LU9LCCzjxGPUM31qbaJXZsTvniTtgugxJY7y612t4A4,109757
23
- topologicpy/Shell.py,sha256=--dJoSdz6BapxVEyG2DI0W5apO_xwLORj5qmR15yl2Y,87983
24
- topologicpy/Speckle.py,sha256=AlsGlSDuKRtX5jhVsPNSSjjbZis079HbUchDH_5RJmE,18187
25
- topologicpy/Sun.py,sha256=42tDWMYpwRG7Z2Qjtp94eRgBuqySq7k8TgNUZDK7QxQ,36837
26
- topologicpy/Topology.py,sha256=QY195KZ5EostlGRnsPGSFglRIU0tunD2CiwsFPfpOqY,478447
27
- topologicpy/Vector.py,sha256=GkGt-aJ591IJ2IPffMAudvITLDPi2qZibZc4UAav6m8,42407
28
- topologicpy/Vertex.py,sha256=q99IrWwdNlvVfUXz6iP8icmP8NzP2nsiqtgnF9Jnpx8,80913
29
- topologicpy/Wire.py,sha256=IVPzBKsckuxC-rHvwxmvtVF7PpApz2lhPHomtQMo_kg,228499
30
- topologicpy/__init__.py,sha256=vlPCanUbxe5NifC4pHcnhSzkmmYcs_UrZrTlVMsxcFs,928
31
- topologicpy/version.py,sha256=zZ7tNz8qiJpD43ZZY6gDLsQEo04V3AXFbZKZwwi4Pww,23
32
- topologicpy-0.8.21.dist-info/licenses/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
33
- topologicpy-0.8.21.dist-info/METADATA,sha256=uin9-ogEkyhyjro0BGZPrFB4abynjZPuv7G3lTk6XzA,10535
34
- topologicpy-0.8.21.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
35
- topologicpy-0.8.21.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
36
- topologicpy-0.8.21.dist-info/RECORD,,