topologicpy 0.7.61__py3-none-any.whl → 0.7.63__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/Cluster.py CHANGED
@@ -165,29 +165,50 @@ class Cluster():
165
165
  from topologicpy.Dictionary import Dictionary
166
166
  from topologicpy.Topology import Topology
167
167
  from topologicpy.Helper import Helper
168
+ import inspect
168
169
 
169
170
  if len(args) == 0:
170
- print("Cluster.ByTopologies - Error: The input topologies parameter is an empty list. Returning None.")
171
+ if not silent:
172
+ print("Cluster.ByTopologies - Error: The input topologies parameter is an empty list. Returning None.")
173
+ curframe = inspect.currentframe()
174
+ calframe = inspect.getouterframes(curframe, 2)
175
+ print('caller name:', calframe[1][3])
171
176
  return None
172
177
  if len(args) == 1:
173
178
  topologies = args[0]
174
179
  if isinstance(topologies, list):
175
180
  if len(topologies) == 0:
176
- print("Cluster.ByTopologies - Error: The input topologies parameter is an empty list. Returning None.")
181
+ if not silent:
182
+ print("Cluster.ByTopologies - Error: The input topologies parameter is an empty list. Returning None.")
183
+ curframe = inspect.currentframe()
184
+ calframe = inspect.getouterframes(curframe, 2)
185
+ print('caller name:', calframe[1][3])
177
186
  return None
178
187
  else:
179
188
  topologyList = [x for x in topologies if Topology.IsInstance(x, "Topology")]
180
189
  if len(topologies) == 0:
181
- print("Cluster.ByTopologies - Error: The input topologies parameter does not contain any valid topologies. Returning None.")
190
+ if not silent:
191
+ print("Cluster.ByTopologies - Error: The input topologies parameter does not contain any valid topologies. Returning None.")
192
+ curframe = inspect.currentframe()
193
+ calframe = inspect.getouterframes(curframe, 2)
194
+ print('caller name:', calframe[1][3])
182
195
  return None
183
196
  else:
184
- print("Cluster.ByTopologies - Warning: The input topologies parameter contains only one topology. Returning the same topology.")
197
+ if not silent:
198
+ print("Cluster.ByTopologies - Warning: The input topologies parameter contains only one topology. Returning the same topology.")
199
+ curframe = inspect.currentframe()
200
+ calframe = inspect.getouterframes(curframe, 2)
201
+ print('caller name:', calframe[1][3])
185
202
  return topologies
186
203
  else:
187
204
  topologyList = Helper.Flatten(list(args))
188
205
  topologyList = [x for x in topologyList if Topology.IsInstance(x, "Topology")]
189
206
  if len(topologyList) == 0:
190
- print("Cluster.ByTopologies - Error: The input parameters do not contain any valid topologies. Returning None.")
207
+ if not silent:
208
+ print("Cluster.ByTopologies - Error: The input parameters do not contain any valid topologies. Returning None.")
209
+ curframe = inspect.currentframe()
210
+ calframe = inspect.getouterframes(curframe, 2)
211
+ print('caller name:', calframe[1][3])
191
212
  return None
192
213
  cluster = topologic.Cluster.ByTopologies(topologyList, False) # Hook to Core
193
214
  dictionaries = []
topologicpy/Edge.py CHANGED
@@ -293,7 +293,8 @@ class Edge():
293
293
  import inspect
294
294
 
295
295
  if len(args) == 0:
296
- print("Edge.ByVertices - Error: The input vertices parameter is an empty list. Returning None.")
296
+ if not silent:
297
+ print("Edge.ByVertices - Error: The input vertices parameter is an empty list. Returning None.")
297
298
  return None
298
299
  if len(args) == 1:
299
300
  vertices = args[0]
@@ -1107,7 +1108,7 @@ class Edge():
1107
1108
  return Edge.Direction(normal_edge)
1108
1109
 
1109
1110
  @staticmethod
1110
- def NormalEdge(edge, length: float = 1.0, u: float = 0.5, angle: float = 0.0):
1111
+ def NormalEdge(edge, length: float = 1.0, u: float = 0.5, angle: float = 0.0, tolerance: float = 0.0001, silent: bool = False):
1111
1112
  """
1112
1113
  Returns the normal (perpendicular) vector to the input edge as an edge.
1113
1114
 
@@ -1125,7 +1126,11 @@ class Edge():
1125
1126
  angle : float , optional
1126
1127
  The desired rotational offset angle in degrees for the normal edge. This rotates the normal edge
1127
1128
  by the angle value around the axis defined by the input edge. The default is 0.0.
1128
-
1129
+ tolerance : float , optional
1130
+ The desired tolerance. The default is 0.0001.
1131
+ silent : bool , optional
1132
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
1133
+
1129
1134
  Returns
1130
1135
  -------
1131
1136
  topologic_core.Edge
@@ -1153,8 +1158,13 @@ class Edge():
1153
1158
  # Otherwise, calculate the normal by crossing with the Z-axis
1154
1159
  z_axis = np.array([0, 0, 1])
1155
1160
  normal_vector = np.cross(direction_vector, z_axis)
1161
+
1162
+ # Check if the normal vector is effectively zero before normalization
1163
+ if np.isclose(norm(normal_vector), 0):
1164
+ return normal_vector
1156
1165
 
1157
- normal_vector /= norm(normal_vector) # Normalize the normal vector
1166
+ # Normalize the normal vector
1167
+ normal_vector /= norm(normal_vector)
1158
1168
  return normal_vector
1159
1169
 
1160
1170
  def calculate_normal_line(start_vertex, end_vertex):
@@ -1168,10 +1178,12 @@ class Edge():
1168
1178
  return start_vertex, normal_end_vertex
1169
1179
 
1170
1180
  if not Topology.IsInstance(edge, "Edge"):
1171
- print("Edge.NormalEdge - Error: The input edge parameter is not a valid edge. Returning None.")
1181
+ if not silent:
1182
+ print("Edge.NormalEdge - Error: The input edge parameter is not a valid edge. Returning None.")
1172
1183
  return None
1173
1184
  if length <= 0.0:
1174
- print("Edge.NormalEdge - Error: The input length parameter is not a positive number greater than zero. Returning None.")
1185
+ if not silent:
1186
+ print("Edge.NormalEdge - Error: The input length parameter is not a positive number greater than zero. Returning None.")
1175
1187
  return None
1176
1188
 
1177
1189
  # Get start and end vertex coordinates
@@ -1186,10 +1198,13 @@ class Edge():
1186
1198
  ev = Vertex.ByCoordinates(list(normal_line_end))
1187
1199
 
1188
1200
  # Create an edge from the start to the end of the normal vector
1189
- normal_edge = Edge.ByVertices([sv, ev])
1190
-
1201
+ normal_edge = Edge.ByVertices([sv, ev], tolerance=tolerance, silent=silent)
1202
+ if normal_edge == None:
1203
+ if not silent:
1204
+ print("Edge.NormalEdge - Error: Could not create edge. Returning None.")
1205
+ return None
1191
1206
  # Set the length of the normal edge
1192
- normal_edge = Edge.SetLength(normal_edge, length, bothSides=False)
1207
+ normal_edge = Edge.SetLength(normal_edge, length, bothSides=False, tolerance=tolerance)
1193
1208
 
1194
1209
  # Rotate the normal edge around the input edge by the specified angle
1195
1210
  edge_direction = Edge.Direction(edge)
topologicpy/Face.py CHANGED
@@ -665,7 +665,7 @@ class Face():
665
665
  from topologicpy.Cluster import Cluster
666
666
  from topologicpy.Topology import Topology
667
667
  from topologicpy.Dictionary import Dictionary
668
- import random
668
+ import inspect
669
669
 
670
670
  def triangulateWire(wire):
671
671
  wire = Topology.RemoveCollinearEdges(wire)
@@ -678,10 +678,16 @@ class Face():
678
678
  if not Topology.IsInstance(wire, "Wire"):
679
679
  if not silent:
680
680
  print("Face.ByWire - Error: The input wire parameter is not a valid topologic wire. Returning None.")
681
+ curframe = inspect.currentframe()
682
+ calframe = inspect.getouterframes(curframe, 2)
683
+ print('caller name:', calframe[1][3])
681
684
  return None
682
685
  if not Wire.IsClosed(wire):
683
686
  if not silent:
684
687
  print("Face.ByWire - Error: The input wire parameter is not a closed topologic wire. Returning None.")
688
+ curframe = inspect.currentframe()
689
+ calframe = inspect.getouterframes(curframe, 2)
690
+ print('caller name:', calframe[1][3])
685
691
  return None
686
692
 
687
693
  edges = Wire.Edges(wire)
topologicpy/Graph.py CHANGED
@@ -8119,7 +8119,8 @@ class Graph:
8119
8119
  camera=[-1.25, -1.25, 1.25],
8120
8120
  center=[0, 0, 0], up=[0, 0, 1],
8121
8121
  projection="perspective",
8122
- tolerance=0.0001):
8122
+ tolerance=0.0001,
8123
+ silent=False):
8123
8124
  """
8124
8125
  Shows the graph using Plotly.
8125
8126
 
@@ -8218,9 +8219,10 @@ class Graph:
8218
8219
  The desired up vector. The default is [0, 0, 1].
8219
8220
  projection : str , optional
8220
8221
  The desired type of projection. The options are "orthographic" or "perspective". It is case insensitive. The default is "perspective"
8221
-
8222
8222
  tolerance : float , optional
8223
8223
  The desired tolerance. The default is 0.0001.
8224
+ silent : bool , optional
8225
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
8224
8226
 
8225
8227
  Returns
8226
8228
  -------
@@ -8234,7 +8236,7 @@ class Graph:
8234
8236
  print("Graph.Show - Error: The input graph is not a valid graph. Returning None.")
8235
8237
  return None
8236
8238
 
8237
- data= Plotly.DataByGraph(graph, sagitta=sagitta, absolute=absolute, sides=sides, vertexColor=vertexColor, vertexSize=vertexSize, vertexLabelKey=vertexLabelKey, vertexGroupKey=vertexGroupKey, vertexGroups=vertexGroups, showVertices=showVertices, showVertexLabels=showVertexLabels, showVertexLegend=showVertexLegend, edgeColor=edgeColor, edgeWidth=edgeWidth, edgeLabelKey=edgeLabelKey, edgeGroupKey=edgeGroupKey, edgeGroups=edgeGroups, showEdges=showEdges, showEdgeLabels=showEdgeLabels, showEdgeLegend=showEdgeLegend, colorScale=colorScale)
8239
+ data= Plotly.DataByGraph(graph, sagitta=sagitta, absolute=absolute, sides=sides, vertexColor=vertexColor, vertexSize=vertexSize, vertexLabelKey=vertexLabelKey, vertexGroupKey=vertexGroupKey, vertexGroups=vertexGroups, showVertices=showVertices, showVertexLabels=showVertexLabels, showVertexLegend=showVertexLegend, edgeColor=edgeColor, edgeWidth=edgeWidth, edgeLabelKey=edgeLabelKey, edgeGroupKey=edgeGroupKey, edgeGroups=edgeGroups, showEdges=showEdges, showEdgeLabels=showEdgeLabels, showEdgeLegend=showEdgeLegend, colorScale=colorScale, silent=silent)
8238
8240
  fig = Plotly.FigureByData(data, width=width, height=height, xAxis=xAxis, yAxis=yAxis, zAxis=zAxis, axisSize=axisSize, backgroundColor=backgroundColor,
8239
8241
  marginLeft=marginLeft, marginRight=marginRight, marginTop=marginTop, marginBottom=marginBottom, tolerance=tolerance)
8240
8242
  Plotly.Show(fig, renderer=renderer, camera=camera, center=center, up=up, projection=projection)
topologicpy/Neo4j.py CHANGED
@@ -42,8 +42,8 @@ class Neo4j:
42
42
 
43
43
  Parameters
44
44
  ----------
45
- neo4jGraph : neo4j._sync.driver.BoltDriver
46
- The input neo4j bolt driver.
45
+ neo4jGraph : neo4j._sync.driver.BoltDriver or neo4jGraph, neo4j._sync.driver.Neo4jDriver
46
+ The input neo4j driver.
47
47
  cypher : str, optional
48
48
  If set to a non-empty string, a Cypher query will be run on the neo4j graph database to return a sub-graph. Default is None.
49
49
  xMin : float, optional
@@ -164,8 +164,8 @@ class Neo4j:
164
164
 
165
165
  Returns
166
166
  -------
167
- neo4j._sync.driver.BoltDriver
168
- The returned neo4j bolt driver.
167
+ neo4j._sync.driver.BoltDriver or neo4jGraph, neo4j._sync.driver.Neo4jDriver
168
+ The returned neo4j driver.
169
169
 
170
170
  """
171
171
  return GraphDatabase.driver(url, auth=(username, password))
@@ -177,13 +177,13 @@ class Neo4j:
177
177
 
178
178
  Parameters
179
179
  ----------
180
- neo4jGraph : neo4j._sync.driver.BoltDriver
181
- The input neo4j bolt driver.
180
+ neo4jGraph : neo4j._sync.driver.BoltDriver or neo4jGraph, neo4j._sync.driver.Neo4jDriver
181
+ The input neo4j driver.
182
182
 
183
183
  Returns
184
184
  -------
185
- neo4j._sync.driver.BoltDriver
186
- The returned neo4j bolt driver.
185
+ neo4j._sync.driver.BoltDriver or neo4jGraph, neo4j._sync.driver.Neo4jDriver
186
+ The returned neo4j driver.
187
187
 
188
188
  """
189
189
  with neo4jGraph.session() as session:
@@ -223,8 +223,8 @@ class Neo4j:
223
223
 
224
224
  Parameters
225
225
  ----------
226
- neo4jGraph : neo4j._sync.driver.BoltDriver
227
- The input neo4j bolt driver.
226
+ neo4jGraph : neo4j._sync.driver.BoltDriver or neo4jGraph, neo4j._sync.driver.Neo4jDriver
227
+ The input neo4j driver.
228
228
  vertexLabelKey : str , optional
229
229
  The returned vertices are labelled according to the dictionary values stored under this key.
230
230
  If the vertexLabelKey does not exist, it will be created and the vertices are labelled numerically using the format defaultVertexLabel_XXX. The default is "label".
@@ -254,8 +254,8 @@ class Neo4j:
254
254
 
255
255
  Returns
256
256
  -------
257
- neo4j._sync.driver.BoltDriver
258
- The returned neo4j bolt driver.
257
+ neo4j._sync.driver.BoltDriver or neo4jGraph, neo4j._sync.driver.Neo4jDriver
258
+ The returned neo4j driver.
259
259
 
260
260
  """
261
261
  from topologicpy.Vertex import Vertex
@@ -279,7 +279,7 @@ class Neo4j:
279
279
 
280
280
  return sanitized
281
281
 
282
- if not isinstance(neo4jGraph, neo4j._sync.driver.BoltDriver):
282
+ if not isinstance(neo4jGraph, neo4j._sync.driver.BoltDriver) and not isinstance(neo4jGraph, neo4j._sync.driver.Neo4jDriver):
283
283
  if not silent:
284
284
  print("Neo4j.ByGraph - Error: The input neo4jGraph is not a valid neo4j graph. Returning None.")
285
285
  return None
topologicpy/Plotly.py CHANGED
@@ -293,7 +293,8 @@ class Plotly:
293
293
  showEdgeLabels: bool = False,
294
294
  showEdgeLegend: bool = False,
295
295
  colorScale: str = "viridis",
296
- mantissa: int = 6):
296
+ mantissa: int = 6,
297
+ silent: bool = False):
297
298
  """
298
299
  Creates plotly vertex and edge data from the input graph.
299
300
 
@@ -354,6 +355,9 @@ class Plotly:
354
355
  If set to True the edge legend will be drawn. Otherwise, it will not be drawn. The default is False.
355
356
  colorScale : str , optional
356
357
  The desired type of plotly color scales to use (e.g. "Viridis", "Plasma"). The default is "Viridis". For a full list of names, see https://plotly.com/python/builtin-colorscales/.
358
+ silent : bool , optional
359
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
360
+
357
361
  Returns
358
362
  -------
359
363
  list
@@ -446,11 +450,11 @@ class Plotly:
446
450
  if sagitta > 0:
447
451
  for edge in edges:
448
452
  d = Topology.Dictionary(edge)
449
- arc = Wire.ArcByEdge(edge, sagitta=sagitta, absolute=absolute, sides=sides, close=False)
453
+ arc = Wire.ArcByEdge(edge, sagitta=sagitta, absolute=absolute, sides=sides, close=False, silent=silent)
450
454
  if Topology.IsInstance(arc, "Wire"):
451
455
  arc_edges = Topology.Edges(arc)
452
456
  for arc_edge in arc_edges:
453
- arc_edge = Topology.SetDictionary(arc_edge, d)
457
+ arc_edge = Topology.SetDictionary(arc_edge, d, silent=silent)
454
458
  new_edges.append(arc_edge)
455
459
  else:
456
460
  new_edges.append(edge)
topologicpy/Topology.py CHANGED
@@ -6252,7 +6252,7 @@ class Topology():
6252
6252
  return newTopology
6253
6253
 
6254
6254
  @staticmethod
6255
- def RemoveCollinearEdges(topology, angTolerance=0.1, tolerance=0.0001):
6255
+ def RemoveCollinearEdges(topology, angTolerance: float = 0.1, tolerance: float = 0.0001, silent: bool = False):
6256
6256
  """
6257
6257
  Removes the collinear edges of the input topology
6258
6258
 
@@ -6264,6 +6264,8 @@ class Topology():
6264
6264
  The desired angular tolerance. The default is 0.1.
6265
6265
  tolerance : float , optional
6266
6266
  The desired tolerance. The default is 0.0001.
6267
+ silent : bool , optional
6268
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
6267
6269
 
6268
6270
  Returns
6269
6271
  -------
@@ -6284,7 +6286,7 @@ class Topology():
6284
6286
  if Topology.IsInstance(topology, "Vertex") or Topology.IsInstance(topology, "Edge"): #Vertex or Edge or Cluster, return the original topology
6285
6287
  return return_topology
6286
6288
  elif Topology.IsInstance(topology, "Wire"):
6287
- return_topology = Wire.RemoveCollinearEdges(topology, angTolerance=angTolerance, tolerance=tolerance)
6289
+ return_topology = Wire.RemoveCollinearEdges(topology, angTolerance=angTolerance, tolerance=tolerance, silent=silent)
6288
6290
  return return_topology
6289
6291
  elif Topology.IsInstance(topology, "Face"):
6290
6292
  return_topology = Face.RemoveCollinearEdges(topology, angTolerance=angTolerance, tolerance=tolerance)
topologicpy/Wire.py CHANGED
@@ -206,7 +206,11 @@ class Wire():
206
206
  length = sagitta
207
207
  else:
208
208
  length = Edge.Length(edge)*sagitta
209
- norm = Edge.NormalEdge(edge, length=length)
209
+ norm = Edge.NormalEdge(edge, length=length, silent=silent)
210
+ if norm == None:
211
+ if not silent:
212
+ print("Wire.ArcByEdge - Warning: Could not create an arc. Returning the original edge.")
213
+ return edge
210
214
  cv = Edge.EndVertex(norm)
211
215
  return Wire.Arc(sv, cv, ev, sides=sides, close=close)
212
216
 
@@ -467,15 +471,15 @@ class Wire():
467
471
  print("Wire.ByOffset - Error: The input wire parameter is not a valid wire. Returning None.")
468
472
  return None
469
473
 
470
- temp_face = Face.ByWire(wire)
471
- original_area = Face.Area(temp_face)
474
+ #temp_face = Face.ByWire(wire)
475
+ #original_area = Face.Area(temp_face)
472
476
  if reverse == True:
473
477
  fac = -1
474
478
  else:
475
479
  fac = 1
476
480
  origin = Topology.Centroid(wire)
477
481
  temp_vertices = [Topology.Vertices(wire)[0], Topology.Vertices(wire)[1], Topology.Centroid(wire)]
478
- temp_face = Face.ByWire(Wire.ByVertices(temp_vertices, close=True))
482
+ temp_face = Face.ByWire(Wire.ByVertices(temp_vertices, close=True), silent=silent)
479
483
  temp_normal = Face.Normal(temp_face)
480
484
  flat_wire = Topology.Flatten(wire, direction=temp_normal, origin=origin)
481
485
  normal = Face.Normal(temp_face)
@@ -644,38 +648,204 @@ class Wire():
644
648
  if not Wire.IsManifold(return_wire) and bisectors == False:
645
649
  if not silent:
646
650
  print("Wire.ByOffset - Warning: The resulting wire is non-manifold, please check your offsets.")
647
- print("Wire.ByOffset - Warning: Pursuing a workaround, but it might take a longer to complete.")
651
+ print("Wire.ByOffset - Warning: Pursuing a workaround, but it might take longer to complete.")
648
652
 
649
653
  #cycles = Wire.Cycles(return_wire, maxVertices = len(final_vertices))
650
654
  temp_wire = Topology.SelfMerge(Cluster.ByTopologies(wire_edges))
651
655
  cycles = Wire.Cycles(temp_wire, maxVertices = len(final_vertices))
652
- distances = []
653
- for cycle in cycles:
654
- cycle_centroid = Topology.Centroid(cycle)
655
- distance = Vertex.Distance(origin, cycle_centroid)
656
- distances.append(distance)
657
- cycles = Helper.Sort(cycles, distances)
658
- # Get the top three or less
659
- cycles = cycles[:min(3, len(cycles))]
660
- areas = [Face.Area(Face.ByWire(cycle)) for cycle in cycles]
661
- cycles = Helper.Sort(cycles, areas)
662
- return_cycle = Wire.Reverse(cycles[-1])
663
- return_cycle = Wire.Simplify(return_cycle, tolerance=epsilon)
664
- return_cycle = Wire.RemoveCollinearEdges(return_cycle)
665
- sel_edges = []
666
- for temp_edge in wire_edges:
667
- x = Topology.Centroid(temp_edge)
668
- d = Topology.Dictionary(temp_edge)
669
- x = Topology.SetDictionary(x, d, silent=True)
670
- sel_edges.append(x)
671
- return_cycle = Topology.TransferDictionariesBySelectors(return_cycle, Topology.Vertices(return_wire), tranVertices=True, tolerance=tolerance, numWorkers=numWorkers)
672
- return_cycle = Topology.TransferDictionariesBySelectors(return_cycle, sel_edges, tranEdges=True, tolerance=tolerance, numWorkers=numWorkers)
673
- return_wire = return_cycle
656
+ if len(cycles) > 0:
657
+ distances = []
658
+ for cycle in cycles:
659
+ cycle_centroid = Topology.Centroid(cycle)
660
+ distance = Vertex.Distance(origin, cycle_centroid)
661
+ distances.append(distance)
662
+ cycles = Helper.Sort(cycles, distances)
663
+ # Get the top three or less
664
+ cycles = cycles[:min(3, len(cycles))]
665
+ areas = [Face.Area(Face.ByWire(cycle)) for cycle in cycles]
666
+ cycles = Helper.Sort(cycles, areas)
667
+ return_cycle = Wire.Reverse(cycles[-1])
668
+ test_cycle = Wire.Simplify(return_cycle, tolerance=epsilon)
669
+ if Topology.IsInstance(test_cycle, "Wire"):
670
+ return_cycle = test_cycle
671
+ return_cycle = Wire.RemoveCollinearEdges(return_cycle, silent=silent)
672
+ sel_edges = []
673
+ for temp_edge in wire_edges:
674
+ x = Topology.Centroid(temp_edge)
675
+ d = Topology.Dictionary(temp_edge)
676
+ x = Topology.SetDictionary(x, d, silent=True)
677
+ sel_edges.append(x)
678
+ return_cycle = Topology.TransferDictionariesBySelectors(return_cycle, Topology.Vertices(return_wire), tranVertices=True, tolerance=tolerance, numWorkers=numWorkers)
679
+ return_cycle = Topology.TransferDictionariesBySelectors(return_cycle, sel_edges, tranEdges=True, tolerance=tolerance, numWorkers=numWorkers)
680
+ return_wire = return_cycle
681
+
674
682
  return_wire = Topology.Unflatten(return_wire, direction=normal, origin=origin)
675
683
  if transferDictionaries == True:
676
684
  return_wire = Topology.SetDictionary(return_wire, Topology.Dictionary(wire), silent=True)
677
685
  return return_wire
678
686
 
687
+ @staticmethod
688
+ def ByOffsetArea(wire,
689
+ area,
690
+ offsetKey="offset",
691
+ minOffsetKey="minOffset",
692
+ maxOffsetKey="maxOffset",
693
+ defaultMinOffset=0,
694
+ defaultMaxOffset=1,
695
+ maxIterations = 1,
696
+ tolerance=0.0001,
697
+ silent = False):
698
+ """
699
+ Creates an offset wire from the input wire based on the input area.
700
+
701
+ Parameters
702
+ ----------
703
+ wire : topologic_core.Wire
704
+ The input wire.
705
+ area : float
706
+ The desired area of the created wire.
707
+ offsetKey : str , optional
708
+ The edge dictionary key under which to store the offset value. The default is "offset".
709
+ minOffsetKey : str , optional
710
+ The edge dictionary key under which to find the desired minimum edge offset value. If a value cannot be found, the defaultMinOffset input parameter value is used instead. The default is "minOffset".
711
+ maxOffsetKey : str , optional
712
+ The edge dictionary key under which to find the desired maximum edge offset value. If a value cannot be found, the defaultMaxOffset input parameter value is used instead. The default is "maxOffset".
713
+ defaultMinOffset : float , optional
714
+ The desired minimum edge offset distance. The default is 0.
715
+ defaultMaxOffset : float , optional
716
+ The desired maximum edge offset distance. The default is 1.
717
+ maxIterations: int , optional
718
+ The desired maximum number of iterations to attempt to converge on a solution. The default is 1.
719
+ tolerance : float , optional
720
+ The desired tolerance. The default is 0.0001.
721
+ silent : bool , optional
722
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
723
+
724
+ Returns
725
+ -------
726
+ topologic_core.Wire
727
+ The created wire.
728
+
729
+ """
730
+ from topologicpy.Wire import Wire
731
+ from topologicpy.Face import Face
732
+ from topologicpy.Topology import Topology
733
+ from topologicpy.Dictionary import Dictionary
734
+ import numpy as np
735
+ from scipy.optimize import minimize
736
+
737
+ def compute_offset_amounts(wire,
738
+ area,
739
+ offsetKey="offset",
740
+ minOffsetKey="minOffset",
741
+ maxOffsetKey="maxOffset",
742
+ defaultMinOffset=0,
743
+ defaultMaxOffset=1,
744
+ maxIterations = 10000,
745
+ maxTime = 10,
746
+ tolerance=0.0001):
747
+
748
+ initial_offsets = []
749
+ bounds = []
750
+ for edge in edges:
751
+ d = Topology.Dictionary(edge)
752
+ minOffset = Dictionary.ValueAtKey(d, minOffsetKey) or defaultMinOffset
753
+ maxOffset = Dictionary.ValueAtKey(d, maxOffsetKey) or defaultMaxOffset
754
+ # Initial guess: small negative offsets to shrink the polygon, within the constraints
755
+ initial_offsets.append((minOffset + maxOffset) / 2)
756
+ # Bounds based on the constraints for each edge
757
+ bounds.append((minOffset, maxOffset))
758
+
759
+ # Convert initial_offsets to np.array for efficiency
760
+ initial_offsets = np.array(initial_offsets)
761
+ iteration_count = [0] # List to act as a mutable counter
762
+
763
+ def objective_function(offsets):
764
+ for i, edge in enumerate(edges):
765
+ d = Topology.Dictionary(edge)
766
+ d = Dictionary.SetValueAtKey(d, offsetKey, offsets[i])
767
+ edge = Topology.SetDictionary(edge, d)
768
+
769
+ # Offset the wire
770
+ new_wire = Wire.ByOffset(wire, offsetKey=offsetKey, silent=silent)
771
+ # Check for an illegal wire. In that case, return a very large loss value.
772
+ if not Topology.IsInstance(new_wire, "Wire"):
773
+ return (float("inf"))
774
+ if not Wire.IsManifold(new_wire):
775
+ return (float("inf"))
776
+ if not Wire.IsClosed(new_wire):
777
+ return (float("inf"))
778
+ new_face = Face.ByWire(new_wire)
779
+ # Calculate the area of the new wire/face
780
+ new_area = Face.Area(new_face)
781
+
782
+ # The objective is the difference between the target hole area and the actual hole area
783
+ # We want this difference to be as close to 0 as possible
784
+ loss = (new_area - area) ** 2
785
+ # If the loss is less than the tolerance, accept the result and return a loss of 0.
786
+ if loss < tolerance:
787
+ return 0
788
+ # Otherwise, return the actual loss value.
789
+ return loss
790
+
791
+ # Callback function to track and display iteration number
792
+ def iteration_callback(xk):
793
+ iteration_count[0] += 1 # Increment the counter
794
+ if not silent:
795
+ print(f"Wire.ByOffsetArea - Information: Iteration {iteration_count[0]}")
796
+
797
+ # Use scipy optimization/minimize to find the correct offsets, respecting the min/max bounds
798
+ result = minimize(objective_function,
799
+ initial_offsets,
800
+ method = "Powell",
801
+ bounds=bounds,
802
+ options={ 'maxiter': maxIterations},
803
+ callback=iteration_callback
804
+ )
805
+
806
+ # Return the offsets
807
+ return result.x
808
+
809
+ if not Topology.IsInstance(wire, "Wire"):
810
+ if not silent:
811
+ print("Wire.OffsetByArea - Error: The input wire parameter is not a valid wire. Returning None.")
812
+ return None
813
+
814
+ if not Wire.IsManifold(wire):
815
+ if not silent:
816
+ print("Wire.OffsetByArea - Error: The input wire parameter is not a manifold wire. Returning None.")
817
+ return None
818
+
819
+ if not Wire.IsClosed(wire):
820
+ if not silent:
821
+ print("Wire.OffsetByArea - Error: The input wire parameter is not a closed wire. Returning None.")
822
+ return None
823
+
824
+ edges = Topology.Edges(wire)
825
+ # Compute the offset amounts
826
+ offsets = compute_offset_amounts(wire,
827
+ area = area,
828
+ offsetKey = offsetKey,
829
+ minOffsetKey = minOffsetKey,
830
+ maxOffsetKey = maxOffsetKey,
831
+ defaultMinOffset = defaultMinOffset,
832
+ defaultMaxOffset = defaultMaxOffset,
833
+ maxIterations = maxIterations,
834
+ tolerance = tolerance)
835
+ # Set the edge dictionaries correctly according to the specified offsetKey
836
+ for i, edge in enumerate(edges):
837
+ d = Topology.Dictionary(edge)
838
+ d = Dictionary.SetValueAtKey(d, offsetKey, offsets[i])
839
+ edge = Topology.SetDictionary(edge, d)
840
+
841
+ # Offset the wire
842
+ return_wire = Wire.ByOffset(wire, offsetKey=offsetKey, silent=silent)
843
+ if not Topology.IsInstance(wire, "Wire"):
844
+ if not silent:
845
+ print("Wire.OffsetByArea - Error: Could not create the offset wire. Returning None.")
846
+ return None
847
+ return return_wire
848
+
679
849
  @staticmethod
680
850
  def ByVertices(vertices: list, close: bool = True, tolerance: float = 0.0001):
681
851
  """
@@ -1865,7 +2035,7 @@ class Wire():
1865
2035
  return status
1866
2036
 
1867
2037
  @staticmethod
1868
- def IsManifold(wire) -> bool:
2038
+ def IsManifold(wire, silent: bool = False) -> bool:
1869
2039
  """
1870
2040
  Returns True if the input wire is manifold. Returns False otherwise. A manifold wire is one where its vertices have a degree of 1 or 2.
1871
2041
 
@@ -1873,6 +2043,8 @@ class Wire():
1873
2043
  ----------
1874
2044
  wire : topologic_core.Wire
1875
2045
  The input wire.
2046
+ silent : bool , optional
2047
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
1876
2048
 
1877
2049
  Returns
1878
2050
  -------
@@ -1881,9 +2053,14 @@ class Wire():
1881
2053
 
1882
2054
  """
1883
2055
  from topologicpy.Vertex import Vertex
2056
+ import inspect
1884
2057
 
1885
2058
  if not Topology.IsInstance(wire, "Wire"):
1886
- print("Wire.IsManifold - Error: The input wire parameter is not a valid topologic wire. Returning None.")
2059
+ if not silent:
2060
+ print("Wire.IsManifold - Error: The input wire parameter is not a valid topologic wire. Returning None.")
2061
+ curframe = inspect.currentframe()
2062
+ calframe = inspect.getouterframes(curframe, 2)
2063
+ print('caller name:', calframe[1][3])
1887
2064
  return None
1888
2065
 
1889
2066
  vertices = Wire.Vertices(wire)
@@ -2574,7 +2751,7 @@ class Wire():
2574
2751
  return baseWire
2575
2752
 
2576
2753
  @staticmethod
2577
- def RemoveCollinearEdges(wire, angTolerance: float = 0.1, tolerance: float = 0.0001):
2754
+ def RemoveCollinearEdges(wire, angTolerance: float = 0.1, tolerance: float = 0.0001, silent: bool = False):
2578
2755
  """
2579
2756
  Removes any collinear edges in the input wire.
2580
2757
 
@@ -2586,6 +2763,8 @@ class Wire():
2586
2763
  The desired angular tolerance. The default is 0.1.
2587
2764
  tolerance : float , optional
2588
2765
  The desired tolerance. The default is 0.0001.
2766
+ silent : bool , optional
2767
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
2589
2768
 
2590
2769
  Returns
2591
2770
  -------
@@ -2598,6 +2777,7 @@ class Wire():
2598
2777
  from topologicpy.Wire import Wire
2599
2778
  from topologicpy.Cluster import Cluster
2600
2779
  from topologicpy.Topology import Topology
2780
+ import inspect
2601
2781
 
2602
2782
  def cleanup(wire, tolerance):
2603
2783
  vertices = Topology.Vertices(wire)
@@ -2611,8 +2791,9 @@ class Wire():
2611
2791
  ev = vertices[Vertex.Index(ev, vertices, tolerance=tolerance)]
2612
2792
  if Vertex.Distance(sv, ev) > tolerance:
2613
2793
  new_edges.append(Edge.ByVertices([sv,ev]))
2614
- new_wire = Topology.SelfMerge(Cluster.ByTopologies(new_edges), tolerance=tolerance)
2615
- return new_wire
2794
+ if len(new_edges) > 0:
2795
+ return Topology.SelfMerge(Cluster.ByTopologies(new_edges, silent=silent), tolerance=tolerance)
2796
+ return wire
2616
2797
 
2617
2798
  def rce(wire, angTolerance=0.1):
2618
2799
  if not Topology.IsInstance(wire, "Wire"):
@@ -2641,6 +2822,13 @@ class Wire():
2641
2822
  final_wire = Edge.ByStartVertexEndVertex(wire_verts[0], wire_verts[1], tolerance=tolerance, silent=True)
2642
2823
  return final_wire
2643
2824
 
2825
+ if not Topology.IsInstance(wire, "Wire"):
2826
+ if not silent:
2827
+ print("Wire.RemoveCollinearEdges - Error: The input wire parameter is not a valid wire. Returning None.")
2828
+ curframe = inspect.currentframe()
2829
+ calframe = inspect.getouterframes(curframe, 2)
2830
+ print('caller name:', calframe[1][3])
2831
+ return None
2644
2832
  new_wire = cleanup(wire, tolerance=tolerance)
2645
2833
  if not Wire.IsManifold(new_wire):
2646
2834
  wires = Wire.Split(new_wire)
@@ -2661,7 +2849,7 @@ class Wire():
2661
2849
  else:
2662
2850
  return wire
2663
2851
  elif len(returnWires) > 1:
2664
- returnWire = Topology.SelfMerge(Cluster.ByTopologies(returnWires))
2852
+ returnWire = Topology.SelfMerge(Cluster.ByTopologies(returnWires, silent=silent))
2665
2853
  if Topology.IsInstance(returnWire, "Edge"):
2666
2854
  return Wire.ByEdges([returnWire], tolerance=tolerance)
2667
2855
  elif Topology.IsInstance(returnWire, "Wire"):
topologicpy/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = '0.7.61'
1
+ __version__ = '0.7.63'
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: topologicpy
3
- Version: 0.7.61
4
- Summary: An Advanced Spatial Modelling and Analysis Software Library for Architecture, Engineering, and Construction.
3
+ Version: 0.7.63
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: MIT License
7
7
 
@@ -3,34 +3,34 @@ topologicpy/Aperture.py,sha256=p9pUzTQSBWoUaDiug1V1R1hnEIEwYSXFg2t7iRAmNRY,2723
3
3
  topologicpy/BVH.py,sha256=dfVQyn5OPN1_50nusS0AxAcmUfqTzpl0vWT7RBEz-WM,12870
4
4
  topologicpy/Cell.py,sha256=_vEYx1kNc3JZJUCCS8nhLM_j8EV4SyZYt3DjNHNRoDw,107707
5
5
  topologicpy/CellComplex.py,sha256=YNmNkH6IUplM9f7agZQd6IL2xnHf5ds3JkGQk4jiSXQ,48029
6
- topologicpy/Cluster.py,sha256=TZXuxzdaUr6OHSWnjWpjCOMlVj6YHBH8aUVbDVsncVA,54999
6
+ topologicpy/Cluster.py,sha256=P9hAtZQjPugnIRJcviQNFBbdDqpMFADJK87_tR0CIAI,56108
7
7
  topologicpy/Color.py,sha256=FrxX2yILqWvYrqD8kBaknfMfOR_phJOmhvTvFc07bY4,18065
8
8
  topologicpy/Context.py,sha256=ppApYKngZZCQBFWaxIMi2z2dokY23c935IDCBosxDAE,3055
9
9
  topologicpy/DGL.py,sha256=Dd6O08D-vSxpjHYgKm45JpKiaeGvWlg1BRMzYMAXGNc,138991
10
10
  topologicpy/Dictionary.py,sha256=cURg452wwk2WeSxWY46ncgAUo5XD1c2c5EtO6ESZHaY,27304
11
- topologicpy/Edge.py,sha256=7Mxu9w9AY8uEk-xJpwMlWkuAgK96YIvUo0jtS5xuPq0,66123
11
+ topologicpy/Edge.py,sha256=FACD8Bm2nL2uTHIeRMVXfRF0cYeqhZ-lCZPHAfjAIPg,66927
12
12
  topologicpy/EnergyModel.py,sha256=NM3_nAdY9_YDtbp9CaEZ0x0xVsetTqVDzm_VSjmq_mI,53746
13
- topologicpy/Face.py,sha256=YjU6TxxW2Mf5InumMvzXUXVVRdtjxyRGauRIhGXzkao,116411
14
- topologicpy/Graph.py,sha256=NoHGYCCoCFP2nDLrzkM7x4hpemo6KiVeOq5Cm0BR78c,411204
13
+ topologicpy/Face.py,sha256=OyzEWdXl8yScHlyG3jDqcI31d_QlAt3SDma8AtkLQFs,116746
14
+ topologicpy/Graph.py,sha256=4b8bzlyXmh0Ef9rjiEjl8wmh_9vAxoE5xiSiGvGjG9Q,411392
15
15
  topologicpy/Grid.py,sha256=3-sn7CHWGcXk18XCnHjsUttNJTWwmN63g_Insj__p04,18218
16
16
  topologicpy/Helper.py,sha256=i-AfI29NMsZXBaymjilfvxQbuS3wpYbpPw4RWu1YCHs,16358
17
17
  topologicpy/Honeybee.py,sha256=Oc8mfGBNSjs6wxkPzCKmEw1ZPQPbp9XtiYWaAF62oSk,21893
18
18
  topologicpy/Matrix.py,sha256=umgR7An919-wGInXJ1wpqnoQ2jCPdyMe2rcWTZ16upk,8079
19
- topologicpy/Neo4j.py,sha256=BezQ-sdpU8B0W4X_kaF7alZrlN0-h4779HFrB3Fsn-w,22033
20
- topologicpy/Plotly.py,sha256=FeRneK3QfMdWZzwnr2lLWvFLQIjwuyq9ms-DghvuZnI,108134
19
+ topologicpy/Neo4j.py,sha256=t52hgE9cVsqkGc7m7fjRsLnyfRHakVHwdvF4ms7ow78,22342
20
+ topologicpy/Plotly.py,sha256=k-gluqX4Na3zlYCDl_6A0gqeFoGR-0Oy6fLiT-Zqlyk,108362
21
21
  topologicpy/Polyskel.py,sha256=EFsuh2EwQJGPLiFUjvtXmAwdX-A4r_DxP5hF7Qd3PaU,19829
22
22
  topologicpy/PyG.py,sha256=LU9LCCzjxGPUM31qbaJXZsTvniTtgugxJY7y612t4A4,109757
23
23
  topologicpy/Shell.py,sha256=joahFtpRQTWJpQOmi3qU4Xe0Sx2XXeayHlXTNx8CzMk,87610
24
24
  topologicpy/Speckle.py,sha256=rUS6PCaxIjEF5_fUruxvMH47FMKg-ohcoU0qAUb-yNM,14267
25
25
  topologicpy/Sun.py,sha256=42tDWMYpwRG7Z2Qjtp94eRgBuqySq7k8TgNUZDK7QxQ,36837
26
- topologicpy/Topology.py,sha256=5Vnn9oMEQ__ta_ew9XhnZExMPbHsj9QN91v-6pxdrJg,397934
26
+ topologicpy/Topology.py,sha256=DsRPV0iWUY2NIlxdvLLDBkkDY3xw8oYvmzQEEfJUusQ,398136
27
27
  topologicpy/Vector.py,sha256=A1g83zDHep58iVPY8WQ8iHNrSOfGWFEzvVeDuMnjDNY,33078
28
28
  topologicpy/Vertex.py,sha256=bLY60YWoMsgCgHk7F7k9F93Sq2FJ6AzUcTfJ83NZfHA,71107
29
- topologicpy/Wire.py,sha256=rGLAwjd5p80rxvkOkcrE1MPXkU9oZ1iRK7n_wOTLbd0,162382
29
+ topologicpy/Wire.py,sha256=x2ut3Pej3fTBv1nPzQMsJ8asWFy4WKIjrAUPA0boesU,171628
30
30
  topologicpy/__init__.py,sha256=D7ky87CAQMiS2KE6YLvcTLkTgA2PY7rASe6Z23pjp9k,872
31
- topologicpy/version.py,sha256=tiB10UwHe_W_etMVnPuXlVWgr8S2BBgwOuFocOXK4Nk,23
32
- topologicpy-0.7.61.dist-info/LICENSE,sha256=BRNw73R2WdDBICtwhI3wm3cxsaVqLTAGuRwrTltcfxs,1068
33
- topologicpy-0.7.61.dist-info/METADATA,sha256=Dcv499yRgltilHs2CTCeo3Ktv7lA4HOZWL8wsVT5Tpg,10918
34
- topologicpy-0.7.61.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
35
- topologicpy-0.7.61.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
36
- topologicpy-0.7.61.dist-info/RECORD,,
31
+ topologicpy/version.py,sha256=MZJ0DcglO3LqgTS4xIW_NWvmDzvvzxd1VdjKOHudVIk,23
32
+ topologicpy-0.7.63.dist-info/LICENSE,sha256=BRNw73R2WdDBICtwhI3wm3cxsaVqLTAGuRwrTltcfxs,1068
33
+ topologicpy-0.7.63.dist-info/METADATA,sha256=EsGaYJ2wOBoqKTUUzAmNE_G0taxa5TW-Eg7UJzZqyYs,10920
34
+ topologicpy-0.7.63.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
35
+ topologicpy-0.7.63.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
36
+ topologicpy-0.7.63.dist-info/RECORD,,