topologicpy 0.8.93__py3-none-any.whl → 0.8.95__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
topologicpy/Cell.py CHANGED
@@ -331,12 +331,19 @@ class Cell():
331
331
  return None
332
332
  return cell
333
333
 
334
+
334
335
  @staticmethod
335
- def ByThickenedFace(face, thickness: float = 1.0, bothSides: bool = True, reverse: bool = False,
336
- planarize: bool = False, tolerance: float = 0.0001, silent: bool = False):
336
+ def ByThickenedFace(face, thickness: float = 1.0, bothSides: bool = True, wSides: int = 1,
337
+ reverse: bool = False, tolerance: float = 0.0001, silent: bool = False):
337
338
  """
338
339
  Creates a cell by thickening the input face.
339
340
 
341
+ Behaviour:
342
+ - Only the bottom and top faces are used as horizontal faces.
343
+ - wSides controls the number of vertical segments along the thickness.
344
+ Intermediate offset layers are used only to build side faces and are
345
+ not included as horizontal faces in Cell.ByFaces.
346
+
340
347
  Parameters
341
348
  ----------
342
349
  face : topologic_core.Face
@@ -344,11 +351,17 @@ class Cell():
344
351
  thickness : float , optional
345
352
  The desired thickness. Default is 1.0.
346
353
  bothSides : bool
347
- If True, the cell will be lofted to each side of the face. Otherwise, it will be lofted in the direction of the normal to the input face. Default is True.
354
+ If True, the thickening is symmetric about the original face
355
+ (i.e. from -thickness/2 to +thickness/2).
356
+ If False, the thickening is from 0 to +thickness along the face normal.
357
+ Default is True.
348
358
  reverse : bool
349
- If True, the cell will be lofted in the opposite direction of the normal to the face. Default is False.
350
- planarize : bool, optional
351
- If set to True, the input faces of the input shell are planarized before building the cell. Otherwise, they are not. Default is False.
359
+ If True, the extrusion direction is flipped (normal is negated).
360
+ Default is False.
361
+ wSides: int, optional
362
+ The number of segments along the thickness direction.
363
+ This is the same definition regardless of bothSides.
364
+ Default is 1.
352
365
  tolerance : float , optional
353
366
  The desired tolerance. Default is 0.0001.
354
367
  silent : bool , optional
@@ -357,60 +370,161 @@ class Cell():
357
370
  Returns
358
371
  -------
359
372
  topologic_core.Cell
360
- The created cell.
361
-
373
+ The created cell, or None on failure.
362
374
  """
375
+ import math
376
+ from topologicpy.Topology import Topology
377
+ from topologicpy.Face import Face
363
378
  from topologicpy.Edge import Edge
364
379
  from topologicpy.Wire import Wire
365
- from topologicpy.Face import Face
366
- from topologicpy.Cluster import Cluster
367
- from topologicpy.Topology import Topology
380
+ from topologicpy.Cell import Cell
368
381
 
382
+ # -----------------------------
383
+ # Validation
384
+ # -----------------------------
369
385
  if not Topology.IsInstance(face, "Face"):
370
- print("Cell.ByThickenedFace - Error: The input face parameter is not a valid topologic face. Returning None.")
386
+ if not silent:
387
+ print("Cell.ByThickenedFace - Error: Input is not a valid Face. Returning None.")
371
388
  return None
372
- if reverse == True and bothSides == False:
373
- thickness = -thickness
374
- faceNormal = Face.Normal(face)
389
+
390
+ if thickness <= tolerance:
391
+ if not silent:
392
+ print("Cell.ByThickenedFace - Error: Thickness is less than or equal to the tolerance. Returning None.")
393
+ return None
394
+
395
+ if wSides < 1:
396
+ if not silent:
397
+ print("Cell.ByThickenedFace - Error: wSides is less than 1. Returning None.")
398
+ return None
399
+
400
+ if thickness/float(wSides) <= tolerance:
401
+ if not silent:
402
+ print("Cell.ByThickenedFace - Error: The distance between layers is less than or equal to the tolerance. Returning None.")
403
+ return None
404
+
405
+ # -----------------------------
406
+ # Face normal (normalized)
407
+ # -----------------------------
408
+ normal = Face.Normal(face)
409
+ if not isinstance(normal, (list, tuple)) or len(normal) != 3:
410
+ if not silent:
411
+ print("Cell.ByThickenedFace - Error: Could not compute face normal.")
412
+ return None
413
+
414
+ nx, ny, nz = normal
415
+ L = math.sqrt(nx*nx + ny*ny + nz*nz)
416
+ if L <= tolerance:
417
+ if not silent:
418
+ print("Cell.ByThickenedFace - Error: Degenerate face normal.")
419
+ return None
420
+
421
+ nx, ny, nz = nx/L, ny/L, nz/L
422
+
423
+ if reverse:
424
+ nx, ny, nz = -nx, -ny, -nz
425
+
426
+ # -----------------------------
427
+ # Build offset layers
428
+ # NOTE: We will only keep the min/max offset faces as bottom/top.
429
+ # Intermediate layers are used only for building side faces.
430
+ # -----------------------------
431
+ step = thickness / float(wSides)
432
+ layers = []
433
+
375
434
  if bothSides:
376
- bottomFace = Topology.Translate(face,
377
- x=-faceNormal[0]*0.5*thickness,
378
- y=-faceNormal[1]*0.5*thickness,
379
- z=-faceNormal[2]*0.5*thickness,
380
- transferDictionaries=False,
381
- silent=True)
382
- topFace = Topology.Translate(face,
383
- x=faceNormal[0]*0.5*thickness,
384
- y=faceNormal[1]*0.5*thickness,
385
- z=faceNormal[2]*0.5*thickness,
386
- transferDictionaries=False,
387
- silent=True)
435
+ # Symmetric: [-thickness/2, ..., +thickness/2]
436
+ start = -0.5 * thickness
437
+ for i in range(wSides + 1):
438
+ offset = start + step * i
439
+ f = Topology.Translate(face, nx*offset, ny*offset, nz*offset)
440
+ layers.append((offset, f))
388
441
  else:
389
- bottomFace = face
390
- topFace = Topology.Translate(face,
391
- x=faceNormal[0]*thickness,
392
- y=faceNormal[1]*thickness,
393
- z=faceNormal[2]*thickness,
394
- transferDictionaries=False,
395
- silent=True)
442
+ # One-sided: [0, ..., thickness]
443
+ for i in range(wSides + 1):
444
+ offset = step * i
445
+ f = Topology.Translate(face, nx*offset, ny*offset, nz*offset)
446
+ layers.append((offset, f))
396
447
 
397
- cellFaces = [Face.Invert(bottomFace), topFace]
398
- bottomEdges = Topology.Edges(bottomFace, silent=True)
448
+ if len(layers) < 2:
449
+ if not silent:
450
+ print("Cell.ByThickenedFace - Error: Not enough layers to form a volume.")
451
+ return None
399
452
 
400
- for bottomEdge in bottomEdges:
401
- topEdge = Topology.Translate(bottomEdge,
402
- x=faceNormal[0]*thickness,
403
- y=faceNormal[1]*thickness,
404
- z=faceNormal[2]*thickness,
405
- transferDictionaries=False,
406
- silent=True)
407
- sideEdge1 = Edge.ByVertices([Edge.StartVertex(bottomEdge), Edge.StartVertex(topEdge)], tolerance=tolerance, silent=silent)
408
- sideEdge2 = Edge.ByVertices([Edge.EndVertex(bottomEdge), Edge.EndVertex(topEdge)], tolerance=tolerance, silent=silent)
409
- cellWire = Topology.SelfMerge(Cluster.ByTopologies([bottomEdge, sideEdge1, topEdge, sideEdge2]), tolerance=tolerance)
410
- if Topology.IsInstance(cellWire, "wire"):
411
- if Wire.IsClosed(cellWire):
412
- cellFaces.append(Face.ByWire(cellWire, tolerance=tolerance))
413
- return Cell.ByFaces(cellFaces, planarize=planarize, tolerance=tolerance)
453
+ layers.sort(key=lambda x: x[0])
454
+
455
+ # Bottom and top faces only
456
+ bottom_face = layers[0][1]
457
+ top_face = layers[-1][1]
458
+
459
+ faces_all = [bottom_face, top_face]
460
+
461
+ # -----------------------------
462
+ # Build side faces between each consecutive pair of layers
463
+ # These are the vertical segmentation faces controlled by wSides.
464
+ # -----------------------------
465
+ for i in range(len(layers) - 1):
466
+ _, faceA = layers[i]
467
+ _, faceB = layers[i + 1]
468
+
469
+ edgesA = Topology.Edges(faceA)
470
+ edgesB = Topology.Edges(faceB)
471
+
472
+ if not edgesA or not edgesB or len(edgesA) != len(edgesB):
473
+ if not silent:
474
+ print("Cell.ByThickenedFace - Warning: Edge mismatch between layers. "
475
+ "Side faces may be incomplete.")
476
+ # We try to continue with min length
477
+ count = min(len(edgesA), len(edgesB))
478
+
479
+ for j in range(count):
480
+ eA = edgesA[j]
481
+ eB = edgesB[j]
482
+
483
+ vA = Topology.Vertices(eA)
484
+ vB = Topology.Vertices(eB)
485
+
486
+ if not vA or not vB or len(vA) != 2 or len(vB) != 2:
487
+ continue
488
+
489
+ vA1, vA2 = vA
490
+ vB1, vB2 = vB
491
+
492
+ try:
493
+ e1 = Edge.ByStartVertexEndVertex(vA1, vA2)
494
+ e2 = Edge.ByStartVertexEndVertex(vA2, vB2)
495
+ e3 = Edge.ByStartVertexEndVertex(vB2, vB1)
496
+ e4 = Edge.ByStartVertexEndVertex(vB1, vA1)
497
+
498
+ if not (e1 and e2 and e3 and e4):
499
+ continue
500
+
501
+ side_wire = Wire.ByEdges([e1, e2, e3, e4])
502
+ if not side_wire:
503
+ continue
504
+
505
+ side_face = Face.ByWire(side_wire)
506
+ if side_face:
507
+ faces_all.append(side_face)
508
+ except Exception:
509
+ # Skip problematic quads but continue
510
+ continue
511
+
512
+ # -----------------------------
513
+ # Build final cell
514
+ # -----------------------------
515
+ try:
516
+ cell = Cell.ByFaces(faces_all, tolerance=tolerance)
517
+ except Exception:
518
+ if not silent:
519
+ print("Cell.ByThickenedFace - Error: Cell.ByFaces failed.")
520
+ return None
521
+
522
+ if not Topology.IsInstance(cell, "Cell"):
523
+ if not silent:
524
+ print("Cell.ByThickenedFace - Error: Cell.ByFaces did not return a valid Cell.")
525
+ return None
526
+
527
+ return cell
414
528
 
415
529
  @staticmethod
416
530
  def ByThickenedShell(shell, direction: list = [0, 0, 1], thickness: float = 1.0, bothSides: bool = True, reverse: bool = False,
@@ -1158,7 +1272,7 @@ class Cell():
1158
1272
  tolerance=tolerance,
1159
1273
  silent=silent)
1160
1274
  return_cell = Cell.ByThickenedFace(cross_shape_face, thickness=height, bothSides=True, reverse=False,
1161
- planarize = False, tolerance=tolerance, silent=silent)
1275
+ tolerance=tolerance, silent=silent)
1162
1276
  xOffset = 0
1163
1277
  yOffset = 0
1164
1278
  zOffset = 0
@@ -1456,7 +1570,7 @@ class Cell():
1456
1570
 
1457
1571
  baseWire = Wire.Circle(origin=circle_origin, radius=radius, sides=uSides, fromAngle=0, toAngle=360, close=True, direction=[0, 0, 1], placement="center", tolerance=tolerance)
1458
1572
  baseFace = Face.ByWire(baseWire, tolerance=tolerance)
1459
- cylinder = Cell.ByThickenedFace(face=baseFace, thickness=height, bothSides=False, tolerance=tolerance)
1573
+ cylinder = Cell.ByThickenedFace(face=baseFace, thickness=height, bothSides=False, reverse=False, tolerance=tolerance)
1460
1574
  if vSides > 1:
1461
1575
  cutting_planes = []
1462
1576
  baseX = Vertex.X(origin, mantissa=mantissa) + xOffset
@@ -2972,15 +3086,16 @@ class Cell():
2972
3086
  elif placement.lower() == "lowerleft":
2973
3087
  xOffset = width*0.5
2974
3088
  yOffset = length*0.5
2975
- vb1 = Vertex.ByCoordinates(Vertex.X(origin, mantissa=mantissa)-width*0.5+xOffset,Vertex.Y(origin, mantissa=mantissa)-length*0.5+yOffset,Vertex.Z(origin, mantissa=mantissa)+zOffset)
2976
- vb2 = Vertex.ByCoordinates(Vertex.X(origin, mantissa=mantissa)+width*0.5+xOffset,Vertex.Y(origin, mantissa=mantissa)-length*0.5+yOffset,Vertex.Z(origin, mantissa=mantissa)+zOffset)
2977
- vb3 = Vertex.ByCoordinates(Vertex.X(origin, mantissa=mantissa)+width*0.5+xOffset,Vertex.Y(origin, mantissa=mantissa)+length*0.5+yOffset,Vertex.Z(origin, mantissa=mantissa)+zOffset)
2978
- vb4 = Vertex.ByCoordinates(Vertex.X(origin, mantissa=mantissa)-width*0.5+xOffset,Vertex.Y(origin, mantissa=mantissa)+length*0.5+yOffset,Vertex.Z(origin, mantissa=mantissa)+zOffset)
3089
+
3090
+ vb1 = Vertex.ByCoordinates(Vertex.X(origin, mantissa=mantissa)-width*0.5+xOffset,Vertex.Y(origin, mantissa=mantissa)+length*0.5+yOffset,Vertex.Z(origin, mantissa=mantissa)+zOffset)
3091
+ vb2 = Vertex.ByCoordinates(Vertex.X(origin, mantissa=mantissa)+width*0.5+xOffset,Vertex.Y(origin, mantissa=mantissa)+length*0.5+yOffset,Vertex.Z(origin, mantissa=mantissa)+zOffset)
3092
+ vb3 = Vertex.ByCoordinates(Vertex.X(origin, mantissa=mantissa)+width*0.5+xOffset,Vertex.Y(origin, mantissa=mantissa)-length*0.5+yOffset,Vertex.Z(origin, mantissa=mantissa)+zOffset)
3093
+ vb4 = Vertex.ByCoordinates(Vertex.X(origin, mantissa=mantissa)-width*0.5+xOffset,Vertex.Y(origin, mantissa=mantissa)-length*0.5+yOffset,Vertex.Z(origin, mantissa=mantissa)+zOffset)
2979
3094
 
2980
3095
  baseWire = Wire.ByVertices([vb1, vb2, vb3, vb4], close=True)
2981
3096
  baseFace = Face.ByWire(baseWire, tolerance=tolerance)
2982
3097
 
2983
- prism = Cell.ByThickenedFace(baseFace, thickness=height, bothSides = False)
3098
+ prism = Cell.ByThickenedFace(baseFace, thickness=height, bothSides = False, reverse=True)
2984
3099
 
2985
3100
  if uSides > 1 or vSides > 1 or wSides > 1:
2986
3101
  prism = sliceCell(prism, width, length, height, uSides, vSides, wSides)
@@ -3092,7 +3207,7 @@ class Cell():
3092
3207
  origin = Vertex.Origin()
3093
3208
  bottom_face = Face.RHS(origin = Vertex.Origin(), width=width, length=length, thickness=thickness, outerFillet=outerFillet, innerFillet=innerFillet, sides=sides, direction=[0,0,1], placement="center", tolerance=tolerance, silent=silent)
3094
3209
  return_cell = Cell.ByThickenedFace(bottom_face, thickness=height, bothSides=True, reverse=False,
3095
- planarize = False, tolerance=tolerance, silent=silent)
3210
+ tolerance=tolerance, silent=silent)
3096
3211
  xOffset = 0
3097
3212
  yOffset = 0
3098
3213
  zOffset = 0
topologicpy/Edge.py CHANGED
@@ -412,12 +412,12 @@ class Edge():
412
412
 
413
413
  if not Topology.IsInstance(origin, "vertex"):
414
414
  if not silent:
415
- print("Edge.ByVertexDirectionLength - Error: The input vertex parameter is not a valid vertex. Returning None.")
415
+ print("Edge.ByOriginDirectionLength - Error: The input vertex parameter is not a valid vertex. Returning None.")
416
416
  return None
417
417
 
418
418
  if length <= tolerance:
419
419
  if not silent:
420
- print("Edge.ByVertexDirectionLength - Error: The input edge parameter must not be less than the input tolerance parameter. Returning None.")
420
+ print("Edge.ByOriginDirectionLength - Error: The input edge parameter must not be less than the input tolerance parameter. Returning None.")
421
421
  return None
422
422
 
423
423
  endVertex = Topology.TranslateByDirectionDistance(origin, direction=direction[:3], distance=length)
@@ -942,7 +942,7 @@ class Edge():
942
942
  return Vertex.ByCoordinates(x,y,0)
943
943
 
944
944
  @staticmethod
945
- def IsCollinear(edgeA, edgeB, mantissa: int = 6, tolerance: float = 0.0001) -> bool:
945
+ def IsCollinear(edgeA, edgeB, mantissa: int = 6, tolerance: float = 0.0001):
946
946
  """
947
947
  Return True if the two input edges are collinear. Returns False otherwise.
948
948
  This code is based on a contribution by https://github.com/gaoxipeng
@@ -1082,7 +1082,7 @@ class Edge():
1082
1082
  return np.isclose(scalar_triple_product, 0, atol=tolerance)
1083
1083
 
1084
1084
  @staticmethod
1085
- def IsParallel(edgeA, edgeB, mantissa: int = 6, tolerance: float = 0.0001) -> bool:
1085
+ def IsParallel(edgeA, edgeB, mantissa: int = 6, tolerance: float = 0.0001):
1086
1086
  """
1087
1087
  Return True if the two input edges are parallel. Returns False otherwise.
1088
1088
 
@@ -1456,6 +1456,37 @@ class Edge():
1456
1456
  return None #Return silently because topologic C++ returns a runtime error if point is not on curve.
1457
1457
  return round(parameter, mantissa)
1458
1458
 
1459
+
1460
+ @staticmethod
1461
+ def Quadrance(edge, mantissa: int = 6) -> float:
1462
+ """
1463
+ Returns the quadrance of the input edge. See: https://en.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance
1464
+
1465
+ Parameters
1466
+ ----------
1467
+ edge : topologic_core.Edge
1468
+ The input edge.
1469
+ mantissa : int , optional
1470
+ The number of decimal places to round the result to. Default is 6.
1471
+
1472
+ Returns
1473
+ -------
1474
+ float
1475
+ The quadrance of the input edge.
1476
+
1477
+ """
1478
+ from topologicpy.Vertex import Vertex
1479
+ from topologicpy.Topology import Topology
1480
+
1481
+ if not Topology.IsInstance(edge, "Edge"):
1482
+ print("Edge.Quadrance - Error: The input edge parameter is not a valid topologic edge. Returning None.")
1483
+ return None
1484
+ sv = Edge.StartVertex(edge)
1485
+ ev = Edge.EndVertex(edge)
1486
+
1487
+ return Vertex.Quadrance(sv, ev, mantissa = mantissa)
1488
+
1489
+
1459
1490
  @staticmethod
1460
1491
  def Reverse(edge, tolerance: float = 0.0001, silent: bool = False):
1461
1492
  """
@@ -1518,6 +1549,54 @@ class Edge():
1518
1549
  return Edge.Extend(edge=edge, distance=distance, bothSides=bothSides, reverse=reverse, tolerance=tolerance)
1519
1550
  return Edge.Trim(edge=edge, distance=distance, bothSides=bothSides, reverse=reverse, tolerance=tolerance)
1520
1551
 
1552
+ @staticmethod
1553
+ def Spread(edgeA, edgeB, mantissa: int = 6, bracket: bool = False) -> float:
1554
+ """
1555
+ Returns the spread between the two input edges.
1556
+
1557
+ Spread is the rational trigonometry equivalent of angle and is defined as:
1558
+ spread = sin^2(theta)
1559
+
1560
+ Properties
1561
+ ----------
1562
+ - spread = 0 : edges are parallel
1563
+ - spread = 1 : edges are perpendicular
1564
+ - 0 <= spread <= 1
1565
+ - No trigonometric functions are used
1566
+
1567
+ Parameters
1568
+ ----------
1569
+ edgeA : topologic_core.Edge
1570
+ The first input edge.
1571
+ edgeB : topologic_core.Edge
1572
+ The second input edge.
1573
+ mantissa : int , optional
1574
+ The number of decimal places to round the result to. Default is 6.
1575
+ bracket : bool
1576
+ If set to True, the spread is bracketed to represent the acute case
1577
+ (i.e. invariant under edge reversal). Default is False.
1578
+
1579
+ Returns
1580
+ -------
1581
+ float
1582
+ The spread between the two input edges.
1583
+ """
1584
+ from topologicpy.Topology import Topology
1585
+ from topologicpy.Vector import Vector
1586
+
1587
+ if not Topology.IsInstance(edgeA, "Edge"):
1588
+ print("Edge.Spread - Error: The input edgeA parameter is not a valid topologic edge. Returning None.")
1589
+ return None
1590
+ if not Topology.IsInstance(edgeB, "Edge"):
1591
+ print("Edge.Spread - Error: The input edgeB parameter is not a valid topologic edge. Returning None.")
1592
+ return None
1593
+
1594
+ # Direction vectors
1595
+ u = Edge.Direction(edgeA, mantissa=15)
1596
+ v = Edge.Direction(edgeB, mantissa=15)
1597
+
1598
+ return Vector.Spread(u, v, mantissa = mantissa, bracket = bracket)
1599
+
1521
1600
  @staticmethod
1522
1601
  def StartVertex(edge, silent: bool = False):
1523
1602
  """
@@ -1672,7 +1751,7 @@ class Edge():
1672
1751
  intVertex = Topology.Intersect(edgeA, edgeB)
1673
1752
  if intVertex and (Vertex.IsInternal(intVertex, edgeA)):
1674
1753
  if reverse:
1675
- return Edge.ByVertices([eva, intVertex], tolerance=tolerance, silent=slient)
1754
+ return Edge.ByVertices([eva, intVertex], tolerance=tolerance, silent=silent)
1676
1755
  else:
1677
1756
  return Edge.ByVertices([sva, intVertex], tolerance=tolerance, silent=silent)
1678
1757
  return edgeA
topologicpy/Face.py CHANGED
@@ -960,7 +960,43 @@ class Face():
960
960
  calframe = inspect.getouterframes(curframe, 2)
961
961
  print('caller name:', calframe[1][3])
962
962
  return None
963
- ibList = [x for x in internalBoundaries if Topology.IsInstance(x, "Wire") and Wire.IsClosed(x)]
963
+ if not isinstance(internalBoundaries, list):
964
+ if not silent:
965
+ print("Face.ByWires - Error: The input internalBoundaries parameter is not a list. Returning None.")
966
+ curframe = inspect.currentframe()
967
+ calframe = inspect.getouterframes(curframe, 2)
968
+ print('caller name:', calframe[1][3])
969
+ return None
970
+ eb_face= Face.ByWire(externalBoundary)
971
+ eb_area = Face.Area(eb_face)
972
+ # Make sure all internal wires are actually inside the external wire.
973
+ ibList = []
974
+ for ib in internalBoundaries:
975
+ if not Topology.IsInstance(ib, "Wire") and Wire.IsClosed(ib):
976
+ if not silent:
977
+ print("Face.ByWires - Warning: One of the internal wires is not a valid closed wire. Ignoring.")
978
+ curframe = inspect.currentframe()
979
+ calframe = inspect.getouterframes(curframe, 2)
980
+ print('caller name:', calframe[1][3])
981
+ continue
982
+ ib_face = Face.ByWire(ib)
983
+ ib_area = Face.Area(ib_face)
984
+ if ib_area >= eb_area:
985
+ if not silent:
986
+ print("Face.ByWires - Warning: One of the iinternal wires has an area greater than that of the external wire. Ignoring.")
987
+ curframe = inspect.currentframe()
988
+ calframe = inspect.getouterframes(curframe, 2)
989
+ print('caller name:', calframe[1][3])
990
+ continue
991
+ sp = Topology.SpatialRelationship(ib_face, eb_face)
992
+ if not sp.lower() == "within":
993
+ if not silent:
994
+ print("Face.ByWires - Warning: One of the internal wires is not within the external wires. Ignoring.")
995
+ curframe = inspect.currentframe()
996
+ calframe = inspect.getouterframes(curframe, 2)
997
+ print('caller name:', calframe[1][3])
998
+ continue
999
+ ibList.append(ib)
964
1000
  face = None
965
1001
  try:
966
1002
  face = topologic.Face.ByExternalInternalBoundaries(externalBoundary, ibList, tolerance) # Hook to Core