topologicpy 0.6.3__py3-none-any.whl → 0.7.0__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/Face.py CHANGED
@@ -15,9 +15,6 @@
15
15
  # this program. If not, see <https://www.gnu.org/licenses/>.
16
16
 
17
17
  import topologic_core as topologic
18
- from topologicpy.Vector import Vector
19
- from topologicpy.Wire import Wire
20
- from topologicpy.Topology import Topology
21
18
  import math
22
19
  import os
23
20
  import warnings
@@ -36,33 +33,34 @@ except:
36
33
  except:
37
34
  warnings.warn("Face - Error: Could not import numpy.")
38
35
 
39
- class Face(Topology):
36
+ class Face():
40
37
  @staticmethod
41
- def AddInternalBoundaries(face: topologic.Face, wires: list) -> topologic.Face:
38
+ def AddInternalBoundaries(face, wires: list):
42
39
  """
43
40
  Adds internal boundaries (closed wires) to the input face. Internal boundaries are considered holes in the input face.
44
41
 
45
42
  Parameters
46
43
  ----------
47
- face : topologic.Face
44
+ face : topologic_core.Face
48
45
  The input face.
49
46
  wires : list
50
47
  The input list of internal boundaries (closed wires).
51
48
 
52
49
  Returns
53
50
  -------
54
- topologic.Face
51
+ topologic_core.Face
55
52
  The created face with internal boundaries added to it.
56
53
 
57
54
  """
55
+ from topologicpy.Topology import Topology
58
56
 
59
- if not isinstance(face, topologic.Face):
57
+ if not Topology.IsInstance(face, "Face"):
60
58
  print("Face.AddInternalBoundaries - Error: The input face parameter is not a valid topologic face. Returning None.")
61
59
  return None
62
60
  if not isinstance(wires, list):
63
61
  print("Face.AddInternalBoundaries - Warning: The input wires parameter is not a valid list. Returning the input face.")
64
62
  return face
65
- wireList = [w for w in wires if isinstance(w, topologic.Wire)]
63
+ wireList = [w for w in wires if Topology.IsInstance(w, "Wire")]
66
64
  if len(wireList) < 1:
67
65
  print("Face.AddInternalBoundaries - Warning: The input wires parameter does not contain any valid wires. Returning the input face.")
68
66
  return face
@@ -71,47 +69,49 @@ class Face(Topology):
71
69
  _ = face.InternalBoundaries(faceibList)
72
70
  for wire in wires:
73
71
  faceibList.append(wire)
74
- return topologic.Face.ByExternalInternalBoundaries(faceeb, faceibList)
72
+ return Face.ByWires(faceeb, faceibList)
75
73
 
76
74
  @staticmethod
77
- def AddInternalBoundariesCluster(face: topologic.Face, cluster: topologic.Cluster) -> topologic.Face:
75
+ def AddInternalBoundariesCluster(face, cluster):
78
76
  """
79
77
  Adds the input cluster of internal boundaries (closed wires) to the input face. Internal boundaries are considered holes in the input face.
80
78
 
81
79
  Parameters
82
80
  ----------
83
- face : topologic.Face
81
+ face : topologic_core.Face
84
82
  The input face.
85
- cluster : topologic.Cluster
83
+ cluster : topologic_core.Cluster
86
84
  The input cluster of internal boundaries (topologic wires).
87
85
 
88
86
  Returns
89
87
  -------
90
- topologic.Face
88
+ topologic_core.Face
91
89
  The created face with internal boundaries added to it.
92
90
 
93
91
  """
94
- if not isinstance(face, topologic.Face):
92
+ from topologicpy.Topology import Topology
93
+
94
+ if not Topology.IsInstance(face, "Face"):
95
95
  print("Face.AddInternalBoundariesCluster - Warning: The input cluster parameter is not a valid cluster. Returning None.")
96
96
  return None
97
97
  if not cluster:
98
98
  return face
99
- if not isinstance(cluster, topologic.Cluster):
99
+ if not Topology.IsInstance(cluster, "Cluster"):
100
100
  return face
101
101
  wires = []
102
102
  _ = cluster.Wires(None, wires)
103
103
  return Face.AddInternalBoundaries(face, wires)
104
104
 
105
105
  @staticmethod
106
- def Angle(faceA: topologic.Face, faceB: topologic.Face, mantissa: int = 6) -> float:
106
+ def Angle(faceA, faceB, mantissa: int = 6) -> float:
107
107
  """
108
108
  Returns the angle in degrees between the two input faces.
109
109
 
110
110
  Parameters
111
111
  ----------
112
- faceA : topologic.Face
112
+ faceA : topologic_core.Face
113
113
  The first input face.
114
- faceB : topologic.Face
114
+ faceB : topologic_core.Face
115
115
  The second input face.
116
116
  mantissa : int , optional
117
117
  The desired length of the mantissa. The default is 6.
@@ -123,10 +123,12 @@ class Face(Topology):
123
123
 
124
124
  """
125
125
  from topologicpy.Vector import Vector
126
- if not isinstance(faceA, topologic.Face):
126
+ from topologicpy.Topology import Topology
127
+
128
+ if not Topology.IsInstance(faceA, "Face"):
127
129
  print("Face.Angle - Warning: The input faceA parameter is not a valid topologic face. Returning None.")
128
130
  return None
129
- if not isinstance(faceB, topologic.Face):
131
+ if not Topology.IsInstance(faceB, "Face"):
130
132
  print("Face.Angle - Warning: The input faceB parameter is not a valid topologic face. Returning None.")
131
133
  return None
132
134
  dirA = Face.NormalAtParameters(faceA, 0.5, 0.5, "xyz", 3)
@@ -134,13 +136,13 @@ class Face(Topology):
134
136
  return round((Vector.Angle(dirA, dirB)), mantissa)
135
137
 
136
138
  @staticmethod
137
- def Area(face: topologic.Face, mantissa: int = 6) -> float:
139
+ def Area(face, mantissa: int = 6) -> float:
138
140
  """
139
141
  Returns the area of the input face.
140
142
 
141
143
  Parameters
142
144
  ----------
143
- face : topologic.Face
145
+ face : topologic_core.Face
144
146
  The input face.
145
147
  mantissa : int , optional
146
148
  The desired length of the mantissa. The default is 6.
@@ -151,24 +153,26 @@ class Face(Topology):
151
153
  The area of the input face.
152
154
 
153
155
  """
154
- if not isinstance(face, topologic.Face):
156
+ from topologicpy.Topology import Topology
157
+
158
+ if not Topology.IsInstance(face, "Face"):
155
159
  print("Face.Area - Warning: The input face parameter is not a valid topologic face. Returning None.")
156
160
  return None
157
161
  area = None
158
162
  try:
159
- area = round(topologic.FaceUtility.Area(face), mantissa)
163
+ area = round(topologic.FaceUtility.Area(face), mantissa) # Hook to Core
160
164
  except:
161
165
  area = None
162
166
  return area
163
167
 
164
168
  @staticmethod
165
- def BoundingRectangle(topology: topologic.Topology, optimize: int = 0, tolerance: float = 0.0001) -> topologic.Face:
169
+ def BoundingRectangle(topology, optimize: int = 0, tolerance: float = 0.0001):
166
170
  """
167
171
  Returns a face representing a bounding rectangle of the input topology. The returned face contains a dictionary with key "zrot" that represents rotations around the Z axis. If applied the resulting face will become axis-aligned.
168
172
 
169
173
  Parameters
170
174
  ----------
171
- topology : topologic.Topology
175
+ topology : topologic_core.Topology
172
176
  The input topology.
173
177
  optimize : int , optional
174
178
  If set to an integer from 1 (low optimization) to 10 (high optimization), the method will attempt to optimize the bounding rectangle so that it reduces its surface area. The default is 0 which will result in an axis-aligned bounding rectangle. The default is 0.
@@ -177,93 +181,20 @@ class Face(Topology):
177
181
 
178
182
  Returns
179
183
  -------
180
- topologic.Face
184
+ topologic_core.Face
181
185
  The bounding rectangle of the input topology.
182
186
 
183
187
  """
184
188
  from topologicpy.Wire import Wire
185
- from topologicpy.Face import Face
186
- from topologicpy.Cluster import Cluster
187
189
  from topologicpy.Topology import Topology
188
- from topologicpy.Dictionary import Dictionary
189
- def bb(topology):
190
- vertices = []
191
- _ = topology.Vertices(None, vertices)
192
- x = []
193
- y = []
194
- for aVertex in vertices:
195
- x.append(aVertex.X())
196
- y.append(aVertex.Y())
197
- minX = min(x)
198
- minY = min(y)
199
- maxX = max(x)
200
- maxY = max(y)
201
- return [minX, minY, maxX, maxY]
202
-
203
- if not isinstance(topology, topologic.Topology):
204
- print("Face.BoundingRectangle - Warning: The input topology parameter is not a valid topologic topology. Returning None.")
205
- return None
206
- vertices = Topology.SubTopologies(topology, subTopologyType="vertex")
207
- topology = Cluster.ByTopologies(vertices)
208
- boundingBox = bb(topology)
209
- minX = boundingBox[0]
210
- minY = boundingBox[1]
211
- maxX = boundingBox[2]
212
- maxY = boundingBox[3]
213
- w = abs(maxX - minX)
214
- l = abs(maxY - minY)
215
- best_area = l*w
216
- orig_area = best_area
217
- best_z = 0
218
- best_bb = boundingBox
219
- origin = Topology.Centroid(topology)
220
- optimize = min(max(optimize, 0), 10)
221
- if optimize > 0:
222
- factor = (round(((11 - optimize)/30 + 0.57), 2))
223
- flag = False
224
- for n in range(10,0,-1):
225
- if flag:
226
- break
227
- za = n
228
- zb = 90+n
229
- zc = n
230
- for z in range(za,zb,zc):
231
- if flag:
232
- break
233
- t = Topology.Rotate(topology, origin=origin, axis=[0, 0, 1], angle=z)
234
- minX, minY, maxX, maxY = bb(t)
235
- w = abs(maxX - minX)
236
- l = abs(maxY - minY)
237
- area = l*w
238
- if area < orig_area*factor:
239
- best_area = area
240
- best_z = z
241
- best_bb = [minX, minY, maxX, maxY]
242
- flag = True
243
- break
244
- if area < best_area:
245
- best_area = area
246
- best_z = z
247
- best_bb = [minX, minY, maxX, maxY]
248
-
249
- else:
250
- best_bb = boundingBox
251
-
252
- minX, minY, maxX, maxY = best_bb
253
- vb1 = topologic.Vertex.ByCoordinates(minX, minY, 0)
254
- vb2 = topologic.Vertex.ByCoordinates(maxX, minY, 0)
255
- vb3 = topologic.Vertex.ByCoordinates(maxX, maxY, 0)
256
- vb4 = topologic.Vertex.ByCoordinates(minX, maxY, 0)
257
-
258
- baseWire = Wire.ByVertices([vb1, vb2, vb3, vb4], close=True)
259
- baseFace = Face.ByWire(baseWire, tolerance=tolerance)
260
- baseFace = Topology.Rotate(baseFace, origin=origin, axis=[0, 0, 1], angle=-best_z)
261
- dictionary = Dictionary.ByKeysValues(["zrot"], [best_z])
262
- baseFace = Topology.SetDictionary(baseFace, dictionary)
263
- return baseFace
190
+
191
+ br_wire = Wire.BoundingRectangle(topology=topology, optimize=optimize, tolerance=tolerance)
192
+ br_face = Face.ByWire(br_wire)
193
+ br_face = Topology.SetDictionary(br_face, Topology.Dictionary(br_wire))
194
+ return br_face
264
195
 
265
196
  @staticmethod
266
- def ByEdges(edges: list, tolerance : float = 0.0001) -> topologic.Face:
197
+ def ByEdges(edges: list, tolerance : float = 0.0001):
267
198
  """
268
199
  Creates a face from the input list of edges.
269
200
 
@@ -276,44 +207,48 @@ class Face(Topology):
276
207
 
277
208
  Returns
278
209
  -------
279
- face : topologic.Face
210
+ face : topologic_core.Face
280
211
  The created face.
281
212
 
282
213
  """
283
214
  from topologicpy.Wire import Wire
215
+ from topologicpy.Topology import Topology
216
+
284
217
  if not isinstance(edges, list):
285
218
  print("Face.ByEdges - Error: The input edges parameter is not a valid list. Returning None.")
286
219
  return None
287
- edges = [e for e in edges if isinstance(e, topologic.Edge)]
220
+ edges = [e for e in edges if Topology.IsInstance(e, "Edge")]
288
221
  if len(edges) < 1:
289
222
  print("Face.ByEdges - Error: The input edges parameter does not contain any valid edges. Returning None.")
290
223
  return None
291
224
  wire = Wire.ByEdges(edges, tolerance=tolerance)
292
- if not isinstance(wire, topologic.Wire):
225
+ if not Topology.IsInstance(wire, "Wire"):
293
226
  print("Face.ByEdges - Error: Could not create the required wire. Returning None.")
294
227
  return None
295
228
  return Face.ByWire(wire, tolerance=tolerance)
296
229
 
297
230
  @staticmethod
298
- def ByEdgesCluster(cluster: topologic.Cluster, tolerance: float = 0.0001) -> topologic.Face:
231
+ def ByEdgesCluster(cluster, tolerance: float = 0.0001):
299
232
  """
300
233
  Creates a face from the input cluster of edges.
301
234
 
302
235
  Parameters
303
236
  ----------
304
- cluster : topologic.Cluster
237
+ cluster : topologic_core.Cluster
305
238
  The input cluster of edges.
306
239
  tolerance : float , optional
307
240
  The desired tolerance. The default is 0.0001.
308
241
 
309
242
  Returns
310
243
  -------
311
- face : topologic.Face
244
+ face : topologic_core.Face
312
245
  The created face.
313
246
 
314
247
  """
315
248
  from topologicpy.Cluster import Cluster
316
- if not isinstance(cluster, topologic.Cluster):
249
+ from topologicpy.Topology import Topology
250
+
251
+ if not Topology.IsInstance(cluster, "Cluster"):
317
252
  print("Face.ByEdgesCluster - Warning: The input cluster parameter is not a valid topologic cluster. Returning None.")
318
253
  return None
319
254
  edges = Cluster.Edges(cluster)
@@ -323,15 +258,15 @@ class Face(Topology):
323
258
  return Face.ByEdges(edges, tolerance=tolerance)
324
259
 
325
260
  @staticmethod
326
- def ByOffset(face: topologic.Face, offset: float = 1.0, miter: bool = False,
261
+ def ByOffset(face, offset: float = 1.0, miter: bool = False,
327
262
  miterThreshold: float = None, offsetKey: str = None,
328
- miterThresholdKey: str = None, step: bool = True, tolerance: float = 0.0001) -> topologic.Face:
263
+ miterThresholdKey: str = None, step: bool = True, tolerance: float = 0.0001):
329
264
  """
330
265
  Creates an offset face from the input face.
331
266
 
332
267
  Parameters
333
268
  ----------
334
- face : topologic.Face
269
+ face : topologic_core.Face
335
270
  The input face.
336
271
  offset : float , optional
337
272
  The desired offset distance. The default is 1.0.
@@ -350,12 +285,14 @@ class Face(Topology):
350
285
 
351
286
  Returns
352
287
  -------
353
- topologic.Face
288
+ topologic_core.Face
354
289
  The created face.
355
290
 
356
291
  """
357
292
  from topologicpy.Wire import Wire
358
- if not isinstance(face, topologic.Face):
293
+ from topologicpy.Topology import Topology
294
+
295
+ if not Topology.IsInstance(face, "Face"):
359
296
  print("Face.ByOffset - Warning: The input face parameter is not a valid toplogic face. Returning None.")
360
297
  return None
361
298
  eb = Face.Wire(face)
@@ -367,13 +304,13 @@ class Face(Topology):
367
304
  return Face.ByWires(offset_external_boundary, offset_internal_boundaries, tolerance=tolerance)
368
305
 
369
306
  @staticmethod
370
- def ByShell(shell: topologic.Shell, origin: topologic.Vertex = None, angTolerance: float = 0.1, tolerance: float = 0.0001)-> topologic.Face:
307
+ def ByShell(shell, origin= None, angTolerance: float = 0.1, tolerance: float = 0.0001):
371
308
  """
372
309
  Creates a face by merging the faces of the input shell.
373
310
 
374
311
  Parameters
375
312
  ----------
376
- shell : topologic.Shell
313
+ shell : topologic_core.Shell
377
314
  The input shell.
378
315
  angTolerance : float , optional
379
316
  The desired angular tolerance. The default is 0.1.
@@ -382,7 +319,7 @@ class Face(Topology):
382
319
 
383
320
  Returns
384
321
  -------
385
- topologic.Face
322
+ topologic_core.Face
386
323
  The created face.
387
324
 
388
325
  """
@@ -400,29 +337,29 @@ class Face(Topology):
400
337
  returnList.append(Wire.Planarize(aWire))
401
338
  return returnList
402
339
 
403
- if not isinstance(shell, topologic.Shell):
340
+ if not Topology.IsInstance(shell, "Shell"):
404
341
  print("Face.ByShell - Error: The input shell parameter is not a valid toplogic shell. Returning None.")
405
342
  return None
406
343
 
407
344
  if origin == None:
408
345
  origin = Topology.Centroid(shell)
409
- if not isinstance(origin, topologic.Vertex):
346
+ if not Topology.IsInstance(origin, "Vertex"):
410
347
  print("Face.ByShell - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
411
348
  return None
412
349
 
413
350
  # Try the simple method first
414
351
  face = None
415
352
  ext_boundary = Wire.RemoveCollinearEdges(Shell.ExternalBoundary(shell))
416
- if isinstance(ext_boundary, topologic.Wire):
353
+ if Topology.IsInstance(ext_boundary, "Wire"):
417
354
  face = Face.ByWire(ext_boundary)
418
- elif isinstance(ext_boundary, topologic.Cluster):
355
+ elif Topology.IsInstance(ext_boundary, "Cluster"):
419
356
  wires = Topology.Wires(ext_boundary)
420
357
  faces = [Face.ByWire(w) for w in wires]
421
358
  areas = [Face.Area(f) for f in faces]
422
359
  wires = Helper.Sort(wires, areas, reverseFlags=[True])
423
360
  face = Face.ByWires(wires[0], wires[1:])
424
361
 
425
- if isinstance(face, topologic.Face):
362
+ if Topology.IsInstance(face, "Face"):
426
363
  return face
427
364
  world_origin = Vertex.Origin()
428
365
  planar_shell = Shell.Planarize(shell)
@@ -437,11 +374,11 @@ class Face(Topology):
437
374
  planar_shell = Topology.SelfMerge(Topology.ReplaceVertices(planar_shell, verticesA=vertices, verticesB=new_vertices), tolerance=tolerance)
438
375
  ext_boundary = Shell.ExternalBoundary(planar_shell, tolerance=tolerance)
439
376
  ext_boundary = Topology.RemoveCollinearEdges(ext_boundary, angTolerance)
440
- if not isinstance(ext_boundary, topologic.Topology):
377
+ if not Topology.IsInstance(ext_boundary, "Topology"):
441
378
  print("Face.ByShell - Error: Could not derive the external boundary of the input shell parameter. Returning None.")
442
379
  return None
443
380
 
444
- if isinstance(ext_boundary, topologic.Wire):
381
+ if Topology.IsInstance(ext_boundary, "Wire"):
445
382
  if not Topology.IsPlanar(ext_boundary, tolerance=tolerance):
446
383
  ext_boundary = Wire.Planarize(ext_boundary, origin=origin, tolerance=tolerance)
447
384
  ext_boundary = Topology.RemoveCollinearEdges(ext_boundary, angTolerance)
@@ -452,14 +389,14 @@ class Face(Topology):
452
389
  except:
453
390
  print("Face.ByShell - Error: The operation failed. Returning None.")
454
391
  return None
455
- elif isinstance(ext_boundary, topologic.Cluster): # The shell has holes.
392
+ elif Topology.IsInstance(ext_boundary, "Cluster"): # The shell has holes.
456
393
  wires = []
457
394
  _ = ext_boundary.Wires(None, wires)
458
395
  faces = []
459
396
  areas = []
460
397
  for wire in wires:
461
398
  aFace = Face.ByWire(wire, tolerance=tolerance)
462
- if not isinstance(aFace, topologic.Face):
399
+ if not Topology.IsInstance(aFace, "Face"):
463
400
  print("Face.ByShell - Error: The operation failed. Returning None.")
464
401
  return None
465
402
  anArea = abs(Face.Area(aFace))
@@ -484,7 +421,7 @@ class Face(Topology):
484
421
  return None
485
422
 
486
423
  @staticmethod
487
- def ByVertices(vertices: list, tolerance: float = 0.0001) -> topologic.Face:
424
+ def ByVertices(vertices: list, tolerance: float = 0.0001):
488
425
 
489
426
  """
490
427
  Creates a face from the input list of vertices.
@@ -498,7 +435,7 @@ class Face(Topology):
498
435
 
499
436
  Returns
500
437
  -------
501
- topologic.Face
438
+ topologic_core.Face
502
439
  The created face.
503
440
 
504
441
  """
@@ -507,7 +444,7 @@ class Face(Topology):
507
444
 
508
445
  if not isinstance(vertices, list):
509
446
  return None
510
- vertexList = [x for x in vertices if isinstance(x, topologic.Vertex)]
447
+ vertexList = [x for x in vertices if Topology.IsInstance(x, "Vertex")]
511
448
  if len(vertexList) < 3:
512
449
  return None
513
450
 
@@ -516,37 +453,39 @@ class Face(Topology):
516
453
  return f
517
454
 
518
455
  @staticmethod
519
- def ByVerticesCluster(cluster: topologic.Cluster, tolerance: float = 0.0001) -> topologic.Face:
456
+ def ByVerticesCluster(cluster, tolerance: float = 0.0001):
520
457
  """
521
458
  Creates a face from the input cluster of vertices.
522
459
 
523
460
  Parameters
524
461
  ----------
525
- cluster : topologic.Cluster
462
+ cluster : topologic_core.Cluster
526
463
  The input cluster of vertices.
527
464
  tolerance : float , optional
528
465
  The desired tolerance. The default is 0.0001.
529
466
 
530
467
  Returns
531
468
  -------
532
- topologic.Face
533
- The crearted face.
469
+ topologic_core.Face
470
+ The created face.
534
471
 
535
472
  """
536
473
  from topologicpy.Cluster import Cluster
537
- if not isinstance(cluster, topologic.Cluster):
474
+ from topologicpy.Topology import Topology
475
+
476
+ if not Topology.IsInstance(cluster, "Cluster"):
538
477
  return None
539
478
  vertices = Cluster.Vertices(cluster)
540
479
  return Face.ByVertices(vertices, tolerance=tolerance)
541
480
 
542
481
  @staticmethod
543
- def ByWire(wire: topologic.Wire, tolerance: float = 0.0001, silent=False) -> topologic.Face:
482
+ def ByWire(wire, tolerance: float = 0.0001, silent=False):
544
483
  """
545
484
  Creates a face from the input closed wire.
546
485
 
547
486
  Parameters
548
487
  ----------
549
- wire : topologic.Wire
488
+ wire : topologic_core.Wire
550
489
  The input wire.
551
490
  tolerance : float , optional
552
491
  The desired tolerance. The default is 0.0001.
@@ -555,7 +494,7 @@ class Face(Topology):
555
494
 
556
495
  Returns
557
496
  -------
558
- topologic.Face or list
497
+ topologic_core.Face or list
559
498
  The created face. If the wire is non-planar, the method will attempt to triangulate the wire and return a list of faces.
560
499
 
561
500
  """
@@ -571,11 +510,11 @@ class Face(Topology):
571
510
  wire = Topology.RemoveCollinearEdges(wire)
572
511
  vertices = Topology.Vertices(wire)
573
512
  shell = Shell.Delaunay(vertices)
574
- if isinstance(shell, topologic.Topology):
513
+ if Topology.IsInstance(shell, "Topology"):
575
514
  return Topology.Faces(shell)
576
515
  else:
577
516
  return []
578
- if not isinstance(wire, topologic.Wire):
517
+ if not Topology.IsInstance(wire, "Wire"):
579
518
  if not silent:
580
519
  print("Face.ByWire - Error: The input wire parameter is not a valid topologic wire. Returning None.")
581
520
  return None
@@ -588,9 +527,9 @@ class Face(Topology):
588
527
  wire = Topology.SelfMerge(Cluster.ByTopologies(edges), tolerance=tolerance)
589
528
  vertices = Topology.Vertices(wire)
590
529
  fList = []
591
- if isinstance(wire, topologic.Wire):
530
+ if Topology.IsInstance(wire, "Wire"):
592
531
  try:
593
- fList = topologic.Face.ByExternalBoundary(wire)
532
+ fList = topologic.Face.ByExternalBoundary(wire) # Hook to Core
594
533
  except:
595
534
  if not silent:
596
535
  print("Face.ByWire - Warning: Could not create face by external boundary. Trying other methods.")
@@ -608,7 +547,7 @@ class Face(Topology):
608
547
  wire = Face.ExternalBoundary(f)
609
548
  wire = Wire.Invert(wire)
610
549
  try:
611
- f = topologic.Face.ByExternalBoundary(wire)
550
+ f = topologic.Face.ByExternalBoundary(wire) # Hook to Core
612
551
  returnList.append(f)
613
552
  except:
614
553
  pass
@@ -626,13 +565,13 @@ class Face(Topology):
626
565
  return returnList
627
566
 
628
567
  @staticmethod
629
- def ByWires(externalBoundary: topologic.Wire, internalBoundaries: list = [], tolerance: float = 0.0001, silent: bool = False) -> topologic.Face:
568
+ def ByWires(externalBoundary, internalBoundaries: list = [], tolerance: float = 0.0001, silent: bool = False):
630
569
  """
631
570
  Creates a face from the input external boundary (closed wire) and the input list of internal boundaries (closed wires).
632
571
 
633
572
  Parameters
634
573
  ----------
635
- externalBoundary : topologic.Wire
574
+ externalBoundary : topologic_core.Wire
636
575
  The input external boundary.
637
576
  internalBoundaries : list , optional
638
577
  The input list of internal boundaries (closed wires). The default is an empty list.
@@ -643,11 +582,14 @@ class Face(Topology):
643
582
 
644
583
  Returns
645
584
  -------
646
- topologic.Face
585
+ topologic_core.Face
647
586
  The created face.
648
587
 
649
588
  """
650
- if not isinstance(externalBoundary, topologic.Wire):
589
+ from topologicpy.Wire import Wire
590
+ from topologicpy.Topology import Topology
591
+
592
+ if not Topology.IsInstance(externalBoundary, "Wire"):
651
593
  if not silent:
652
594
  print("Face.ByWires - Error: The input externalBoundary parameter is not a valid topologic wire. Returning None.")
653
595
  return None
@@ -655,10 +597,10 @@ class Face(Topology):
655
597
  if not silent:
656
598
  print("Face.ByWires - Error: The input externalBoundary parameter is not a closed topologic wire. Returning None.")
657
599
  return None
658
- ibList = [x for x in internalBoundaries if isinstance(x, topologic.Wire) and Wire.IsClosed(x)]
600
+ ibList = [x for x in internalBoundaries if Topology.IsInstance(x, "Wire") and Wire.IsClosed(x)]
659
601
  face = None
660
602
  try:
661
- face = topologic.Face.ByExternalInternalBoundaries(externalBoundary, ibList, tolerance)
603
+ face = topologic.Face.ByExternalInternalBoundaries(externalBoundary, ibList, tolerance) # Hook to Core
662
604
  except:
663
605
  if not silent:
664
606
  print("Face.ByWires - Error: The operation failed. Returning None.")
@@ -667,15 +609,15 @@ class Face(Topology):
667
609
 
668
610
 
669
611
  @staticmethod
670
- def ByWiresCluster(externalBoundary: topologic.Wire, internalBoundariesCluster: topologic.Cluster = None, tolerance: float = 0.0001, silent: bool = False) -> topologic.Face:
612
+ def ByWiresCluster(externalBoundary, internalBoundariesCluster = None, tolerance: float = 0.0001, silent: bool = False):
671
613
  """
672
614
  Creates a face from the input external boundary (closed wire) and the input cluster of internal boundaries (closed wires).
673
615
 
674
616
  Parameters
675
617
  ----------
676
- externalBoundary : topologic.Wire
618
+ externalBoundary topologic_core.Wire
677
619
  The input external boundary (closed wire).
678
- internalBoundariesCluster : topologic.Cluster
620
+ internalBoundariesCluster : topologic_core.Cluster
679
621
  The input cluster of internal boundaries (closed wires). The default is None.
680
622
  tolerance : float , optional
681
623
  The desired tolerance. The default is 0.0001.
@@ -684,13 +626,15 @@ class Face(Topology):
684
626
 
685
627
  Returns
686
628
  -------
687
- topologic.Face
629
+ topologic_core.Face
688
630
  The created face.
689
631
 
690
632
  """
691
633
  from topologicpy.Wire import Wire
692
634
  from topologicpy.Cluster import Cluster
693
- if not isinstance(externalBoundary, topologic.Wire):
635
+ from topologicpy.Topology import Topology
636
+
637
+ if not Topology.IsInstance(externalBoundary, "Wire"):
694
638
  if not silent:
695
639
  print("Face.ByWiresCluster - Error: The input externalBoundary parameter is not a valid topologic wire. Returning None.")
696
640
  return None
@@ -700,7 +644,7 @@ class Face(Topology):
700
644
  return None
701
645
  if not internalBoundariesCluster:
702
646
  internalBoundaries = []
703
- elif not isinstance(internalBoundariesCluster, topologic.Cluster):
647
+ elif not Topology.IsInstance(internalBoundariesCluster, "Cluster"):
704
648
  if not silent:
705
649
  print("Face.ByWiresCluster - Error: The input internalBoundariesCluster parameter is not a valid topologic cluster. Returning None.")
706
650
  return None
@@ -709,14 +653,14 @@ class Face(Topology):
709
653
  return Face.ByWires(externalBoundary, internalBoundaries, tolerance=tolerance, silent=silent)
710
654
 
711
655
  @staticmethod
712
- def NorthArrow(origin: topologic.Vertex = None, radius: float = 0.5, sides: int = 16, direction: list = [0, 0, 1], northAngle: float = 0.0,
713
- placement: str = "center", tolerance: float = 0.0001) -> topologic.Face:
656
+ def NorthArrow(origin= None, radius: float = 0.5, sides: int = 16, direction: list = [0, 0, 1], northAngle: float = 0.0,
657
+ placement: str = "center", tolerance: float = 0.0001):
714
658
  """
715
659
  Creates a north arrow.
716
660
 
717
661
  Parameters
718
662
  ----------
719
- origin : topologic.Vertex, optional
663
+ origin : topologic_core.Vertex, optional
720
664
  The location of the origin of the circle. The default is None which results in the circle being placed at (0, 0, 0).
721
665
  radius : float , optional
722
666
  The radius of the circle. The default is 1.
@@ -733,7 +677,7 @@ class Face(Topology):
733
677
 
734
678
  Returns
735
679
  -------
736
- topologic.Face
680
+ topologic_core.Face
737
681
  The created circle.
738
682
 
739
683
  """
@@ -760,14 +704,14 @@ class Face(Topology):
760
704
  return arrow
761
705
 
762
706
  @staticmethod
763
- def Circle(origin: topologic.Vertex = None, radius: float = 0.5, sides: int = 16, fromAngle: float = 0.0, toAngle: float = 360.0, direction: list = [0, 0, 1],
764
- placement: str = "center", tolerance: float = 0.0001) -> topologic.Face:
707
+ def Circle(origin= None, radius: float = 0.5, sides: int = 16, fromAngle: float = 0.0, toAngle: float = 360.0, direction: list = [0, 0, 1],
708
+ placement: str = "center", tolerance: float = 0.0001):
765
709
  """
766
710
  Creates a circle.
767
711
 
768
712
  Parameters
769
713
  ----------
770
- origin : topologic.Vertex, optional
714
+ origin : topologic_core.Vertex, optional
771
715
  The location of the origin of the circle. The default is None which results in the circle being placed at (0, 0, 0).
772
716
  radius : float , optional
773
717
  The radius of the circle. The default is 1.
@@ -786,24 +730,26 @@ class Face(Topology):
786
730
 
787
731
  Returns
788
732
  -------
789
- topologic.Face
733
+ topologic_core.Face
790
734
  The created circle.
791
735
 
792
736
  """
793
737
  from topologicpy.Wire import Wire
738
+ from topologicpy.Topology import Topology
739
+
794
740
  wire = Wire.Circle(origin=origin, radius=radius, sides=sides, fromAngle=fromAngle, toAngle=toAngle, close=True, direction=direction, placement=placement, tolerance=tolerance)
795
- if not isinstance(wire, topologic.Wire):
741
+ if not Topology.IsInstance(wire, "Wire"):
796
742
  return None
797
743
  return Face.ByWire(wire, tolerance=tolerance)
798
744
 
799
745
  @staticmethod
800
- def Compactness(face: topologic.Face, mantissa: int = 6) -> float:
746
+ def Compactness(face, mantissa: int = 6) -> float:
801
747
  """
802
748
  Returns the compactness measure of the input face. See https://en.wikipedia.org/wiki/Compactness_measure_of_a_shape
803
749
 
804
750
  Parameters
805
751
  ----------
806
- face : topologic.Face
752
+ face : topologic_core.Face
807
753
  The input face.
808
754
  mantissa : int , optional
809
755
  The desired length of the mantissa. The default is 6.
@@ -814,12 +760,14 @@ class Face(Topology):
814
760
  The compactness measure of the input face.
815
761
 
816
762
  """
763
+ from topologicpy.Edge import Edge
764
+
817
765
  exb = face.ExternalBoundary()
818
766
  edges = []
819
767
  _ = exb.Edges(None, edges)
820
768
  perimeter = 0.0
821
769
  for anEdge in edges:
822
- perimeter = perimeter + abs(topologic.EdgeUtility.Length(anEdge))
770
+ perimeter = perimeter + abs(Edge.Length(anEdge))
823
771
  area = abs(Face.Area(face))
824
772
  compactness = 0
825
773
  #From https://en.wikipedia.org/wiki/Compactness_measure_of_a_shape
@@ -832,13 +780,13 @@ class Face(Topology):
832
780
  return round(compactness, mantissa)
833
781
 
834
782
  @staticmethod
835
- def CompassAngle(face: topologic.Face, north: list = None, mantissa: int = 6) -> float:
783
+ def CompassAngle(face, north: list = None, mantissa: int = 6) -> float:
836
784
  """
837
785
  Returns the horizontal compass angle in degrees between the normal vector of the input face and the input vector. The angle is measured in counter-clockwise fashion. Only the first two elements of the vectors are considered.
838
786
 
839
787
  Parameters
840
788
  ----------
841
- face : topologic.Face
789
+ face : topologic_core.Face
842
790
  The input face.
843
791
  north : list , optional
844
792
  The second vector representing the north direction. The default is the positive YAxis ([0,1,0]).
@@ -854,7 +802,9 @@ class Face(Topology):
854
802
 
855
803
  """
856
804
  from topologicpy.Vector import Vector
857
- if not isinstance(face, topologic.Face):
805
+ from topologicpy.Topology import Topology
806
+
807
+ if not Topology.IsInstance(face, "Face"):
858
808
  return None
859
809
  if not north:
860
810
  north = Vector.North()
@@ -862,13 +812,13 @@ class Face(Topology):
862
812
  return Vector.CompassAngle(vectorA=dirA, vectorB=north, mantissa=mantissa)
863
813
 
864
814
  @staticmethod
865
- def Edges(face: topologic.Face) -> list:
815
+ def Edges(face) -> list:
866
816
  """
867
817
  Returns the edges of the input face.
868
818
 
869
819
  Parameters
870
820
  ----------
871
- face : topologic.Face
821
+ face : topologic_core.Face
872
822
  The input face.
873
823
 
874
824
  Returns
@@ -877,21 +827,23 @@ class Face(Topology):
877
827
  The list of edges.
878
828
 
879
829
  """
880
- if not isinstance(face, topologic.Face):
830
+ from topologicpy.Topology import Topology
831
+
832
+ if not Topology.IsInstance(face, "Face"):
881
833
  return None
882
834
  edges = []
883
835
  _ = face.Edges(None, edges)
884
836
  return edges
885
837
 
886
838
  @staticmethod
887
- def Einstein(origin: topologic.Vertex = None, radius: float = 0.5, direction: list = [0, 0, 1],
888
- placement: str = "center", tolerance: float = 0.0001) -> topologic.Face:
839
+ def Einstein(origin= None, radius: float = 0.5, direction: list = [0, 0, 1],
840
+ placement: str = "center", tolerance: float = 0.0001):
889
841
  """
890
842
  Creates an aperiodic monotile, also called an 'einstein' tile (meaning one tile in German, not the name of the famous physist). See https://arxiv.org/abs/2303.10798
891
843
 
892
844
  Parameters
893
845
  ----------
894
- origin : topologic.Vertex , optional
846
+ origin : topologic_core.Vertex , optional
895
847
  The location of the origin of the tile. The default is None which results in the tiles first vertex being placed at (0, 0, 0).
896
848
  radius : float , optional
897
849
  The radius of the hexagon determining the size of the tile. The default is 0.5.
@@ -904,26 +856,27 @@ class Face(Topology):
904
856
 
905
857
  Returns
906
858
  --------
907
- topologic.Face
859
+ topologic_core.Face
908
860
  The created Einstein tile.
909
861
 
910
862
  """
911
863
  from topologicpy.Wire import Wire
864
+ from topologicpy.Topology import Topology
912
865
 
913
866
  wire = Wire.Einstein(origin=origin, radius=radius, direction=direction, placement=placement)
914
- if not isinstance(wire, topologic.Wire):
867
+ if not Topology.IsInstance(wire, "Wire"):
915
868
  print("Face.Einstein - Error: Could not create base wire for the Einstein tile. Returning None.")
916
869
  return None
917
870
  return Face.ByWire(wire, tolerance=tolerance)
918
871
 
919
872
  @staticmethod
920
- def ExteriorAngles(face: topologic.Face, includeInternalBoundaries=False, mantissa: int = 6) -> list:
873
+ def ExteriorAngles(face, includeInternalBoundaries=False, mantissa: int = 6) -> list:
921
874
  """
922
875
  Returns the exterior angles of the input face in degrees. The face must be planar.
923
876
 
924
877
  Parameters
925
878
  ----------
926
- face : topologic.Face
879
+ face : topologic_core.Face
927
880
  The input face.
928
881
  includeInternalBoundaries : bool , optional
929
882
  If set to True and if the face has internal boundaries (holes), the returned list will be a nested list where the first list is the list
@@ -940,8 +893,9 @@ class Face(Topology):
940
893
  """
941
894
 
942
895
  from topologicpy.Wire import Wire
896
+ from topologicpy.Topology import Topology
943
897
 
944
- if not isinstance(face, topologic.Face):
898
+ if not Topology.IsInstance(face, "Face"):
945
899
  print("Face.ExteriorAngles - Error: The input face parameter is not a valid face. Returning None.")
946
900
  return None
947
901
  eb = Face.ExternalBoundary(face)
@@ -958,31 +912,31 @@ class Face(Topology):
958
912
  return return_list
959
913
 
960
914
  @staticmethod
961
- def ExternalBoundary(face: topologic.Face) -> topologic.Wire:
915
+ def ExternalBoundary(face):
962
916
  """
963
917
  Returns the external boundary (closed wire) of the input face.
964
918
 
965
919
  Parameters
966
920
  ----------
967
- face : topologic.Face
921
+ face : topologic_core.Face
968
922
  The input face.
969
923
 
970
924
  Returns
971
925
  -------
972
- topologic.Wire
926
+ topologic_core.Wire
973
927
  The external boundary of the input face.
974
928
 
975
929
  """
976
930
  return face.ExternalBoundary()
977
931
 
978
932
  @staticmethod
979
- def FacingToward(face: topologic.Face, direction: list = [0,0,-1], asVertex: bool = False, tolerance: float = 0.0001) -> bool:
933
+ def FacingToward(face, direction: list = [0,0,-1], asVertex: bool = False, tolerance: float = 0.0001) -> bool:
980
934
  """
981
935
  Returns True if the input face is facing toward the input direction.
982
936
 
983
937
  Parameters
984
938
  ----------
985
- face : topologic.Face
939
+ face : topologic_core.Face
986
940
  The input face.
987
941
  direction : list , optional
988
942
  The input direction. The default is [0,0,-1].
@@ -997,8 +951,10 @@ class Face(Topology):
997
951
  True if the face is facing toward the direction. False otherwise.
998
952
 
999
953
  """
1000
- faceNormal = topologic.FaceUtility.NormalAtParameters(face,0.5, 0.5)
1001
- faceCenter = topologic.FaceUtility.VertexAtParameters(face,0.5,0.5)
954
+ from topologicpy.Vector import Vector
955
+
956
+ faceNormal = Face.Normal(face)
957
+ faceCenter = Face.VertexByParameters(face,0.5,0.5)
1002
958
  cList = [faceCenter.X(), faceCenter.Y(), faceCenter.Z()]
1003
959
  try:
1004
960
  vList = [direction.X(), direction.Y(), direction.Z()]
@@ -1017,21 +973,80 @@ class Face(Topology):
1017
973
  return False
1018
974
  return True
1019
975
 
976
+
1020
977
  @staticmethod
1021
- def Harmonize(face: topologic.Face, tolerance: float = 0.0001) -> topologic.Face:
978
+ def Fillet(face, radius: float = 0, radiusKey: str = None, tolerance: float = 0.0001, silent: bool = False):
979
+ """
980
+ Fillets (rounds) the interior and exterior corners of the input face given the input radius. See https://en.wikipedia.org/wiki/Fillet_(mechanics)
981
+
982
+ Parameters
983
+ ----------
984
+ face : topologic_core.Face
985
+ The input face.
986
+ radius : float
987
+ The desired radius of the fillet.
988
+ radiusKey : str , optional
989
+ If specified, the dictionary of the vertices will be queried for this key to specify the desired fillet radius. The default is None.
990
+ tolerance : float , optional
991
+ The desired tolerance. The default is 0.0001.
992
+ silent : bool , optional
993
+ If set to False, error and warning messages are printed. Otherwise, they are not. The default is False.
994
+
995
+ Returns
996
+ -------
997
+ topologic_core.Face
998
+ The filleted face.
999
+
1000
+ """
1001
+ from topologicpy.Wire import Wire
1002
+ from topologicpy.Topology import Topology
1003
+
1004
+ if not Topology.IsInstance(face, "Face"):
1005
+ if not silent:
1006
+ print("Face.Fillet - Error: The input face parameter is not a valid face. Returning None.")
1007
+ return None
1008
+
1009
+ eb = Topology.Copy(Face.ExternalBoundary(face))
1010
+ ib_list = Face.InternalBoundaries(face)
1011
+ ib_list = [Topology.Copy(ib) for ib in ib_list]
1012
+ f_vertices = Face.Vertices(face)
1013
+ if isinstance(radiusKey, str):
1014
+ eb = Topology.TransferDictionariesBySelectors(eb, selectors=f_vertices, tranVertices=True)
1015
+ eb = Wire.Fillet(eb, radius=radius, radiusKey=radiusKey, tolerance=tolerance)
1016
+ if not Topology.IsInstance(eb, "Wire"):
1017
+ if not silent:
1018
+ print("Face.Fillet - Error: The operation failed. Returning None.")
1019
+ return None
1020
+ ib_wires = []
1021
+ for ib in ib_list:
1022
+ ib = Wire.ByVertices(Topology.Vertices(ib))
1023
+ ib = Wire.Reverse(ib)
1024
+ if isinstance(radiusKey, str):
1025
+ ib = Topology.TransferDictionariesBySelectors(ib, selectors=f_vertices, tranVertices=True)
1026
+
1027
+ ib_wire = Wire.Fillet(ib, radius=radius, radiusKey=radiusKey, tolerance=tolerance, silent=silent)
1028
+ if Topology.IsInstance(ib, "Wire"):
1029
+ ib_wires.append(ib_wire)
1030
+ else:
1031
+ if not silent:
1032
+ print("Face.Fillet - Error: The operation for one of the interior boundaries failed. Skipping.")
1033
+ return Face.ByWires(eb, ib_wires)
1034
+
1035
+ @staticmethod
1036
+ def Harmonize(face, tolerance: float = 0.0001):
1022
1037
  """
1023
1038
  Returns a harmonized version of the input face such that the *u* and *v* origins are always in the upperleft corner.
1024
1039
 
1025
1040
  Parameters
1026
1041
  ----------
1027
- face : topologic.Face
1042
+ face : topologic_core.Face
1028
1043
  The input face.
1029
1044
  tolerance : float , optional
1030
1045
  The desired tolerance. The default is 0.0001.
1031
1046
 
1032
1047
  Returns
1033
1048
  -------
1034
- topologic.Face
1049
+ topologic_core.Face
1035
1050
  The harmonized face.
1036
1051
 
1037
1052
  """
@@ -1040,7 +1055,7 @@ class Face(Topology):
1040
1055
  from topologicpy.Topology import Topology
1041
1056
  from topologicpy.Dictionary import Dictionary
1042
1057
 
1043
- if not isinstance(face, topologic.Face):
1058
+ if not Topology.IsInstance(face, "Face"):
1044
1059
  print("Face.Harmonize - Error: The input face parameter is not a valid face. Returning None.")
1045
1060
  return None
1046
1061
  normal = Face.Normal(face)
@@ -1059,13 +1074,13 @@ class Face(Topology):
1059
1074
  return harmonizedFace
1060
1075
 
1061
1076
  @staticmethod
1062
- def InteriorAngles(face: topologic.Face, includeInternalBoundaries: bool = False, mantissa: int = 6) -> list:
1077
+ def InteriorAngles(face, includeInternalBoundaries: bool = False, mantissa: int = 6) -> list:
1063
1078
  """
1064
1079
  Returns the interior angles of the input face in degrees. The face must be planar.
1065
1080
 
1066
1081
  Parameters
1067
1082
  ----------
1068
- face : topologic.Face
1083
+ face : topologic_core.Face
1069
1084
  The input face.
1070
1085
  includeInternalBoundaries : bool , optional
1071
1086
  If set to True and if the face has internal boundaries (holes), the returned list will be a nested list where the first list is the list
@@ -1080,10 +1095,10 @@ class Face(Topology):
1080
1095
  list
1081
1096
  The list of interior angles.
1082
1097
  """
1083
-
1084
1098
  from topologicpy.Wire import Wire
1099
+ from topologicpy.Topology import Topology
1085
1100
 
1086
- if not isinstance(face, topologic.Face):
1101
+ if not Topology.IsInstance(face, "Face"):
1087
1102
  print("Face.InteriorAngles - Error: The input face parameter is not a valid face. Returning None.")
1088
1103
  return None
1089
1104
  eb = Face.ExternalBoundary(face)
@@ -1100,13 +1115,13 @@ class Face(Topology):
1100
1115
  return return_list
1101
1116
 
1102
1117
  @staticmethod
1103
- def InternalBoundaries(face: topologic.Face) -> list:
1118
+ def InternalBoundaries(face) -> list:
1104
1119
  """
1105
1120
  Returns the internal boundaries (closed wires) of the input face.
1106
1121
 
1107
1122
  Parameters
1108
1123
  ----------
1109
- face : topologic.Face
1124
+ face : topologic_core.Face
1110
1125
  The input face.
1111
1126
 
1112
1127
  Returns
@@ -1115,33 +1130,36 @@ class Face(Topology):
1115
1130
  The list of internal boundaries (closed wires).
1116
1131
 
1117
1132
  """
1118
- if not isinstance(face, topologic.Face):
1133
+ from topologicpy.Topology import Topology
1134
+
1135
+ if not Topology.IsInstance(face, "Face"):
1119
1136
  return None
1120
1137
  wires = []
1121
1138
  _ = face.InternalBoundaries(wires)
1122
1139
  return list(wires)
1123
1140
 
1124
1141
  @staticmethod
1125
- def InternalVertex(face: topologic.Face, tolerance: float = 0.0001) -> topologic.Vertex:
1142
+ def InternalVertex(face, tolerance: float = 0.0001):
1126
1143
  """
1127
1144
  Creates a vertex guaranteed to be inside the input face.
1128
1145
 
1129
1146
  Parameters
1130
1147
  ----------
1131
- face : topologic.Face
1148
+ face : topologic_core.Face
1132
1149
  The input face.
1133
1150
  tolerance : float , optional
1134
1151
  The desired tolerance. The default is 0.0001.
1135
1152
 
1136
1153
  Returns
1137
1154
  -------
1138
- topologic.Vertex
1155
+ topologic_core.Vertex
1139
1156
  The created vertex.
1140
1157
 
1141
1158
  """
1142
1159
  from topologicpy.Vertex import Vertex
1143
1160
  from topologicpy.Topology import Topology
1144
- if not isinstance(face, topologic.Face):
1161
+
1162
+ if not Topology.IsInstance(face, "Face"):
1145
1163
  return None
1146
1164
  v = Topology.Centroid(face)
1147
1165
  if Vertex.IsInternal(v, face, tolerance=tolerance):
@@ -1152,30 +1170,31 @@ class Face(Topology):
1152
1170
  v = Face.VertexByParameters(face, u, v)
1153
1171
  if Vertex.IsInternal(v, face, tolerance=tolerance):
1154
1172
  return v
1155
- v = topologic.FaceUtility.InternalVertex(face, tolerance)
1173
+ v = topologic.FaceUtility.InternalVertex(face, tolerance) # Hook to Core
1156
1174
  return v
1157
1175
 
1158
1176
  @staticmethod
1159
- def Invert(face: topologic.Face, tolerance: float = 0.0001) -> topologic.Face:
1177
+ def Invert(face, tolerance: float = 0.0001):
1160
1178
  """
1161
1179
  Creates a face that is an inverse (mirror) of the input face.
1162
1180
 
1163
1181
  Parameters
1164
1182
  ----------
1165
- face : topologic.Face
1183
+ face : topologic_core.Face
1166
1184
  The input face.
1167
1185
  tolerance : float , optional
1168
1186
  The desired tolerance. The default is 0.0001.
1169
1187
 
1170
1188
  Returns
1171
1189
  -------
1172
- topologic.Face
1190
+ topologic_core.Face
1173
1191
  The inverted face.
1174
1192
 
1175
1193
  """
1176
1194
  from topologicpy.Wire import Wire
1195
+ from topologicpy.Topology import Topology
1177
1196
 
1178
- if not isinstance(face, topologic.Face):
1197
+ if not Topology.IsInstance(face, "Face"):
1179
1198
  return None
1180
1199
  eb = Face.ExternalBoundary(face)
1181
1200
  vertices = Wire.Vertices(eb)
@@ -1189,15 +1208,15 @@ class Face(Topology):
1189
1208
  return inverted_face
1190
1209
 
1191
1210
  @staticmethod
1192
- def IsCoplanar(faceA: topologic.Face, faceB: topologic.Face, tolerance: float = 0.0001) -> bool:
1211
+ def IsCoplanar(faceA, faceB, tolerance: float = 0.0001) -> bool:
1193
1212
  """
1194
1213
  Returns True if the two input faces are coplanar. Returns False otherwise.
1195
1214
 
1196
1215
  Parameters
1197
1216
  ----------
1198
- faceA : topologic.Face
1217
+ faceA : topologic_core.Face
1199
1218
  The first input face.
1200
- faceB : topologic.Face
1219
+ faceB : topologic_core.Face
1201
1220
  The second input face
1202
1221
  tolerance : float , optional
1203
1222
  The desired tolerance. The deafault is 0.0001.
@@ -1213,24 +1232,112 @@ class Face(Topology):
1213
1232
  True if the two input faces are coplanar. False otherwise.
1214
1233
 
1215
1234
  """
1216
- if not isinstance(faceA, topologic.Face):
1235
+ from topologicpy.Vector import Vector
1236
+ from topologicpy.Topology import Topology
1237
+
1238
+ if not Topology.IsInstance(faceA, "Face"):
1217
1239
  print("Face.IsInide - Error: The input faceA parameter is not a valid topologic face. Returning None.")
1218
1240
  return None
1219
- if not isinstance(faceB, topologic.Face):
1241
+ if not Topology.IsInstance(faceB, "Face"):
1220
1242
  print("Face.IsInide - Error: The input faceB parameter is not a valid topologic face. Returning None.")
1221
1243
  return None
1222
1244
  dirA = Face.NormalAtParameters(faceA, 0.5, 0.5, "xyz", 3)
1223
1245
  dirB = Face.NormalAtParameters(faceB, 0.5, 0.5, "xyz", 3)
1224
1246
  return Vector.IsCollinear(dirA, dirB)
1225
-
1247
+
1248
+ @staticmethod
1249
+ def Isovist(face, vertex, obstacles = None, fromAngle=0, toAngle=360, tolerance: float = 0.0001):
1250
+ """
1251
+ Returns the face representing the isovist projection from the input viewpoint.
1252
+ This method assumes all input is in 2D. Z coordinates are ignored.
1253
+
1254
+ Parameters
1255
+ ----------
1256
+ face : topologic_core.Face
1257
+ The face representing the boundary of the isovist.
1258
+ vertex : topologic_core.Vertex
1259
+ The vertex representing the location of the viewpoint of the isovist.
1260
+ obstacles : list , optional
1261
+ A list of wires representing the obstacles within the face. All obstacles are assumed to be within the
1262
+ boundary of the face.
1263
+ fromAngle : float , optional
1264
+ The angle in degrees from which to start creating the arc of the circle. The default is 0.
1265
+ 0 is considered to be in the positive X-axis direction. 90 is considered to be in the
1266
+ positive Y-axis direction.
1267
+ toAngle : float , optional
1268
+ The angle in degrees at which to end creating the arc of the circle. The default is 360.
1269
+ Angles are measured in an anti-clockwise fashion.
1270
+ tolerance : float , optional:
1271
+ The desired tolerance. The default is 0.0001.
1272
+
1273
+ Returns
1274
+ -------
1275
+ topologic_core.Face
1276
+ The face representing the isovist projection from the input viewpoint.
1277
+
1278
+ """
1279
+ from topologicpy.Vertex import Vertex
1280
+ from topologicpy.Edge import Edge
1281
+ from topologicpy.Wire import Wire
1282
+ from topologicpy.Face import Face
1283
+ from topologicpy.Shell import Shell
1284
+ from topologicpy.Cluster import Cluster
1285
+ from topologicpy.Topology import Topology
1286
+
1287
+ def vertexPartofFace(vertex, face, tolerance):
1288
+ vertices = []
1289
+ _ = face.Vertices(None, vertices)
1290
+ for v in vertices:
1291
+ if Vertex.Distance(vertex, v) < tolerance:
1292
+ return True
1293
+ return False
1294
+
1295
+ if not Topology.IsInstance(face, "Face"):
1296
+ print("Face.Isovist - Error: The input boundary parameter is not a valid Face. Returning None")
1297
+ return None
1298
+ if not Topology.IsInstance(vertex, "Vertex"):
1299
+ print("Face.Isovist - Error: The input viewPoint parameter is not a valid Vertex. Returning None")
1300
+ return None
1301
+ if isinstance(obstacles, list):
1302
+ obstacles = [obs for obs in obstacles if Topology.IsInstance(obs, "Wire")]
1303
+ for obs in obstacles:
1304
+ face = Topology.Difference(face, Face.ByWire(obs))
1305
+ targets = Topology.Vertices(face)
1306
+ distances = []
1307
+ for target in targets:
1308
+ distances.append(Vertex.Distance(vertex, target))
1309
+ distances.sort()
1310
+ max_d = distances[-1]*1.05
1311
+ edges = []
1312
+ for target in targets:
1313
+ e = Edge.ByVertices(vertex, target)
1314
+ e = Edge.SetLength(e, length=max_d, bothSides=False)
1315
+ edges.append(e)
1316
+ shell = Topology.Slice(face, Cluster.ByTopologies(edges))
1317
+ faces = Topology.Faces(shell)
1318
+ final_faces = []
1319
+ for face in faces:
1320
+ if vertexPartofFace(vertex, face, tolerance=0.001):
1321
+ final_faces.append(face)
1322
+ shell = Shell.ByFaces(final_faces)
1323
+ return_face = Topology.RemoveCoplanarFaces(shell)
1324
+ if abs(360 - toAngle - fromAngle) > tolerance:
1325
+ c = Wire.Circle(origin= vertex, radius=max_d, sides=180, fromAngle=fromAngle, toAngle=toAngle, close = False)
1326
+ e1 = Edge.ByVertices(Wire.StartVertex(c), vertex)
1327
+ e2 = Edge.ByVertices(Wire.EndVertex(c), vertex)
1328
+ edges = Topology.Edges(c) + [e1,e2]
1329
+ pie = Face.ByWire(Topology.SelfMerge(Cluster.ByTopologies(edges)))
1330
+ return_face = Topology.Intersect(pie, return_face)
1331
+ return return_face
1332
+
1226
1333
  @staticmethod
1227
- def MedialAxis(face: topologic.Face, resolution: int = 0, externalVertices: bool = False, internalVertices: bool = False, toLeavesOnly: bool = False, angTolerance: float = 0.1, tolerance: float = 0.0001) -> topologic.Wire:
1334
+ def MedialAxis(face, resolution: int = 0, externalVertices: bool = False, internalVertices: bool = False, toLeavesOnly: bool = False, angTolerance: float = 0.1, tolerance: float = 0.0001):
1228
1335
  """
1229
1336
  Returns a wire representing an approximation of the medial axis of the input topology. See https://en.wikipedia.org/wiki/Medial_axis.
1230
1337
 
1231
1338
  Parameters
1232
1339
  ----------
1233
- face : topologic.Face
1340
+ face : topologic_core.Face
1234
1341
  The input face.
1235
1342
  resolution : int , optional
1236
1343
  The desired resolution of the solution (range is 0: standard resolution to 10: high resolution). This determines the density of the sampling along each edge. The default is 0.
@@ -1247,7 +1354,7 @@ class Face(Topology):
1247
1354
 
1248
1355
  Returns
1249
1356
  -------
1250
- topologic.Wire
1357
+ topologic_core.Wire
1251
1358
  The medial axis of the input face.
1252
1359
 
1253
1360
  """
@@ -1260,7 +1367,7 @@ class Face(Topology):
1260
1367
  from topologicpy.Dictionary import Dictionary
1261
1368
 
1262
1369
  def touchesEdge(vertex,edges, tolerance=0.0001):
1263
- if not isinstance(vertex, topologic.Vertex):
1370
+ if not Topology.IsInstance(vertex, "Vertex"):
1264
1371
  return False
1265
1372
  for edge in edges:
1266
1373
  u = Edge.ParameterAtVertex(edge, vertex, mantissa=6)
@@ -1313,13 +1420,13 @@ class Face(Topology):
1313
1420
  theVertices = theVertices+extVertices
1314
1421
 
1315
1422
  tempWire = Topology.SelfMerge(Cluster.ByTopologies(medialAxisEdges), tolerance=tolerance)
1316
- if isinstance(tempWire, topologic.Wire) and angTolerance > 0:
1423
+ if Topology.IsInstance(tempWire, "Wire") and angTolerance > 0:
1317
1424
  tempWire = Wire.RemoveCollinearEdges(tempWire, angTolerance=angTolerance)
1318
1425
  medialAxisEdges = Wire.Edges(tempWire)
1319
1426
  for v in theVertices:
1320
1427
  nv = Vertex.NearestVertex(v, tempWire, useKDTree=False)
1321
1428
 
1322
- if isinstance(nv, topologic.Vertex):
1429
+ if Topology.IsInstance(nv, "Vertex"):
1323
1430
  if toLeavesOnly:
1324
1431
  adjVertices = Topology.AdjacentTopologies(nv, tempWire)
1325
1432
  if len(adjVertices) < 2:
@@ -1327,19 +1434,19 @@ class Face(Topology):
1327
1434
  else:
1328
1435
  medialAxisEdges.append(Edge.ByVertices([nv, v], tolerance=tolerance))
1329
1436
  medialAxis = Topology.SelfMerge(Cluster.ByTopologies(medialAxisEdges), tolerance=tolerance)
1330
- if isinstance(medialAxis, topologic.Wire) and angTolerance > 0:
1437
+ if Topology.IsInstance(medialAxis, "Wire") and angTolerance > 0:
1331
1438
  medialAxis = Topology.RemoveCollinearEdges(medialAxis, angTolerance=angTolerance)
1332
1439
  medialAxis = Topology.Unflatten(medialAxis, origin=origin,direction=normal)
1333
1440
  return medialAxis
1334
1441
 
1335
1442
  @staticmethod
1336
- def Normal(face: topologic.Face, outputType: str = "xyz", mantissa: int = 6) -> list:
1443
+ def Normal(face, outputType: str = "xyz", mantissa: int = 6) -> list:
1337
1444
  """
1338
1445
  Returns the normal vector to the input face. A normal vector of a face is a vector perpendicular to it.
1339
1446
 
1340
1447
  Parameters
1341
1448
  ----------
1342
- face : topologic.Face
1449
+ face : topologic_core.Face
1343
1450
  The input face.
1344
1451
  outputType : string , optional
1345
1452
  The string defining the desired output. This can be any subset or permutation of "xyz". It is case insensitive. The default is "xyz".
@@ -1355,13 +1462,13 @@ class Face(Topology):
1355
1462
  return Face.NormalAtParameters(face, u=0.5, v=0.5, outputType=outputType, mantissa=mantissa)
1356
1463
 
1357
1464
  @staticmethod
1358
- def NormalAtParameters(face: topologic.Face, u: float = 0.5, v: float = 0.5, outputType: str = "xyz", mantissa: int = 6) -> list:
1465
+ def NormalAtParameters(face, u: float = 0.5, v: float = 0.5, outputType: str = "xyz", mantissa: int = 6) -> list:
1359
1466
  """
1360
1467
  Returns the normal vector to the input face. A normal vector of a face is a vector perpendicular to it.
1361
1468
 
1362
1469
  Parameters
1363
1470
  ----------
1364
- face : topologic.Face
1471
+ face : topologic_core.Face
1365
1472
  The input face.
1366
1473
  u : float , optional
1367
1474
  The *u* parameter at which to compute the normal to the input face. The default is 0.5.
@@ -1380,7 +1487,7 @@ class Face(Topology):
1380
1487
  """
1381
1488
  returnResult = []
1382
1489
  try:
1383
- coords = topologic.FaceUtility.NormalAtParameters(face, u, v)
1490
+ coords = topologic.FaceUtility.NormalAtParameters(face, u, v) # Hook to Core
1384
1491
  x = round(coords[0], mantissa)
1385
1492
  y = round(coords[1], mantissa)
1386
1493
  z = round(coords[2], mantissa)
@@ -1397,13 +1504,13 @@ class Face(Topology):
1397
1504
  return returnResult
1398
1505
 
1399
1506
  @staticmethod
1400
- def NormalEdge(face: topologic.Face, length: float = 1.0, tolerance: float = 0.0001, silent: bool = False) -> topologic.Edge:
1507
+ def NormalEdge(face, length: float = 1.0, tolerance: float = 0.0001, silent: bool = False):
1401
1508
  """
1402
1509
  Returns the normal vector to the input face as an edge with the desired input length. A normal vector of a face is a vector perpendicular to it.
1403
1510
 
1404
1511
  Parameters
1405
1512
  ----------
1406
- face : topologic.Face
1513
+ face : topologic_core.Face
1407
1514
  The input face.
1408
1515
  length : float , optional
1409
1516
  The desired length of the normal edge. The default is 1.
@@ -1412,13 +1519,14 @@ class Face(Topology):
1412
1519
 
1413
1520
  Returns
1414
1521
  -------
1415
- topologic.Edge
1522
+ topologic_core.Edge
1416
1523
  The created normal edge to the input face. This is computed at the approximate center of the face.
1417
1524
 
1418
1525
  """
1419
1526
  from topologicpy.Edge import Edge
1527
+ from topologicpy.Topology import Topology
1420
1528
 
1421
- if not isinstance(face, topologic.Face):
1529
+ if not Topology.IsInstance(face, "Face"):
1422
1530
  if not silent:
1423
1531
  print("Face.NormalEdge - Error: The input face parameter is not a valid face. Retuning None.")
1424
1532
  return None
@@ -1433,13 +1541,13 @@ class Face(Topology):
1433
1541
  return Edge.ByVertices([iv, ev], tolerance=tolerance, silent=silent)
1434
1542
 
1435
1543
  @staticmethod
1436
- def NormalEdgeAtParameters(face: topologic.Face, u: float = 0.5, v: float = 0.5, length: float = 1.0, tolerance: float = 0.0001) -> topologic.Edge:
1544
+ def NormalEdgeAtParameters(face, u: float = 0.5, v: float = 0.5, length: float = 1.0, tolerance: float = 0.0001):
1437
1545
  """
1438
1546
  Returns the normal vector to the input face as an edge with the desired input length. A normal vector of a face is a vector perpendicular to it.
1439
1547
 
1440
1548
  Parameters
1441
1549
  ----------
1442
- face : topologic.Face
1550
+ face : topologic_core.Face
1443
1551
  The input face.
1444
1552
  u : float , optional
1445
1553
  The *u* parameter at which to compute the normal to the input face. The default is 0.5.
@@ -1452,13 +1560,13 @@ class Face(Topology):
1452
1560
 
1453
1561
  Returns
1454
1562
  -------
1455
- topologic.Edge
1563
+ topologic_core.Edge
1456
1564
  The created normal edge to the input face. This is computed at the approximate center of the face.
1457
1565
 
1458
1566
  """
1459
1567
  from topologicpy.Edge import Edge
1460
1568
  from topologicpy.Topology import Topology
1461
- if not isinstance(face, topologic.Face):
1569
+ if not Topology.IsInstance(face, "Face"):
1462
1570
  return None
1463
1571
  sv = Face.VertexByParameters(face=face, u=u, v=v)
1464
1572
  vec = Face.NormalAtParameters(face, u=u, v=v)
@@ -1466,13 +1574,13 @@ class Face(Topology):
1466
1574
  return Edge.ByVertices([sv, ev], tolerance=tolerance, silent=True)
1467
1575
 
1468
1576
  @staticmethod
1469
- def PlaneEquation(face: topologic.Face, mantissa: int = 6) -> dict:
1577
+ def PlaneEquation(face, mantissa: int = 6) -> dict:
1470
1578
  """
1471
1579
  Returns the a, b, c, d coefficients of the plane equation of the input face. The input face is assumed to be planar.
1472
1580
 
1473
1581
  Parameters
1474
1582
  ----------
1475
- face : topologic.Face
1583
+ face : topologic_core.Face
1476
1584
  The input face.
1477
1585
 
1478
1586
  Returns
@@ -1486,7 +1594,7 @@ class Face(Topology):
1486
1594
  import random
1487
1595
  import time
1488
1596
 
1489
- if not isinstance(face, topologic.Face):
1597
+ if not Topology.IsInstance(face, "Face"):
1490
1598
  print("Face.PlaneEquation - Error: The input face is not a valid topologic face. Returning None.")
1491
1599
  return None
1492
1600
  vertices = Topology.Vertices(face)
@@ -1496,23 +1604,23 @@ class Face(Topology):
1496
1604
  return Vertex.PlaneEquation(vertices, mantissa=mantissa)
1497
1605
 
1498
1606
  @staticmethod
1499
- def Planarize(face: topologic.Face, origin: topologic.Vertex = None,
1500
- tolerance: float = 0.0001) -> topologic.Face:
1607
+ def Planarize(face, origin= None,
1608
+ tolerance: float = 0.0001):
1501
1609
  """
1502
1610
  Planarizes the input face such that its center of mass is located at the input origin and its normal is pointed in the input direction.
1503
1611
 
1504
1612
  Parameters
1505
1613
  ----------
1506
- face : topologic.Face
1614
+ face : topologic_core.Face
1507
1615
  The input face.
1508
- origin : topologic.Vertex , optional
1616
+ origin : topologic_core.Vertex , optional
1509
1617
  The desired vertex to use as the origin of the plane to project the face unto. If set to None, the centroidof the input face is used. The default is None.
1510
1618
  tolerance : float , optional
1511
1619
  The desired tolerance. The default is 0.0001.
1512
1620
 
1513
1621
  Returns
1514
1622
  -------
1515
- topologic.Face
1623
+ topologic_core.Face
1516
1624
  The planarized face.
1517
1625
 
1518
1626
  """
@@ -1520,9 +1628,9 @@ class Face(Topology):
1520
1628
  from topologicpy.Wire import Wire
1521
1629
  from topologicpy.Topology import Topology
1522
1630
 
1523
- if not isinstance(face, topologic.Face):
1631
+ if not Topology.IsInstance(face, "Face"):
1524
1632
  return None
1525
- if not isinstance(origin, topologic.Vertex):
1633
+ if not Topology.IsInstance(origin, "Vertex"):
1526
1634
  origin = Topology.Centroid(face)
1527
1635
  eb = Face.ExternalBoundary(face)
1528
1636
  plan_eb = Wire.Planarize(eb, origin=origin)
@@ -1534,16 +1642,16 @@ class Face(Topology):
1534
1642
  return plan_face
1535
1643
 
1536
1644
  @staticmethod
1537
- def Project(faceA: topologic.Face, faceB: topologic.Face, direction : list = None,
1538
- mantissa: int = 6, tolerance: float = 0.0001) -> topologic.Face:
1645
+ def Project(faceA, faceB, direction : list = None,
1646
+ mantissa: int = 6, tolerance: float = 0.0001):
1539
1647
  """
1540
1648
  Creates a projection of the first input face unto the second input face.
1541
1649
 
1542
1650
  Parameters
1543
1651
  ----------
1544
- faceA : topologic.Face
1652
+ faceA : topologic_core.Face
1545
1653
  The face to be projected.
1546
- faceB : topologic.Face
1654
+ faceB : topologic_core.Face
1547
1655
  The face unto which the first input face will be projected.
1548
1656
  direction : list, optional
1549
1657
  The vector direction of the projection. If None, the reverse vector of the receiving face normal will be used. The default is None.
@@ -1554,20 +1662,20 @@ class Face(Topology):
1554
1662
 
1555
1663
  Returns
1556
1664
  -------
1557
- topologic.Face
1665
+ topologic_core.Face
1558
1666
  The projected Face.
1559
1667
 
1560
1668
  """
1561
-
1562
1669
  from topologicpy.Wire import Wire
1670
+ from topologicpy.Topology import Topology
1563
1671
 
1564
1672
  if not faceA:
1565
1673
  return None
1566
- if not isinstance(faceA, topologic.Face):
1674
+ if not Topology.IsInstance(faceA, "Face"):
1567
1675
  return None
1568
1676
  if not faceB:
1569
1677
  return None
1570
- if not isinstance(faceB, topologic.Face):
1678
+ if not Topology.IsInstance(faceB, "Face"):
1571
1679
  return None
1572
1680
 
1573
1681
  eb = faceA.ExternalBoundary()
@@ -1582,7 +1690,7 @@ class Face(Topology):
1582
1690
  return Face.ByWires(p_eb, p_ib_list, tolerance=tolerance)
1583
1691
 
1584
1692
  @staticmethod
1585
- def RectangleByPlaneEquation(origin: topologic.Vertex = None, width: float = 1.0, length: float = 1.0, placement: str = "center", equation: dict = None, tolerance: float = 0.0001) -> topologic.Face:
1693
+ def RectangleByPlaneEquation(origin= None, width: float = 1.0, length: float = 1.0, placement: str = "center", equation: dict = None, tolerance: float = 0.0001):
1586
1694
  from topologicpy.Vertex import Vertex
1587
1695
  # Extract coefficients of the plane equation
1588
1696
  a = equation['a']
@@ -1598,13 +1706,13 @@ class Face(Topology):
1598
1706
  return Face.Rectangle(origin=origin, width=width, length=length, direction = direction, placement=placement, tolerance=tolerance)
1599
1707
 
1600
1708
  @staticmethod
1601
- def Rectangle(origin: topologic.Vertex = None, width: float = 1.0, length: float = 1.0, direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001) -> topologic.Face:
1709
+ def Rectangle(origin= None, width: float = 1.0, length: float = 1.0, direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001):
1602
1710
  """
1603
1711
  Creates a rectangle.
1604
1712
 
1605
1713
  Parameters
1606
1714
  ----------
1607
- origin : topologic.Vertex, optional
1715
+ origin : topologic_core.Vertex, optional
1608
1716
  The location of the origin of the rectangle. The default is None which results in the rectangle being placed at (0, 0, 0).
1609
1717
  width : float , optional
1610
1718
  The width of the rectangle. The default is 1.0.
@@ -1619,26 +1727,27 @@ class Face(Topology):
1619
1727
 
1620
1728
  Returns
1621
1729
  -------
1622
- topologic.Face
1730
+ topologic_core.Face
1623
1731
  The created face.
1624
1732
 
1625
1733
  """
1626
1734
  from topologicpy.Wire import Wire
1735
+ from topologicpy.Topology import Topology
1627
1736
 
1628
1737
  wire = Wire.Rectangle(origin=origin, width=width, length=length, direction=direction, placement=placement, tolerance=tolerance)
1629
- if not isinstance(wire, topologic.Wire):
1738
+ if not Topology.IsInstance(wire, "Wire"):
1630
1739
  print("Face.Rectangle - Error: Could not create the base wire for the rectangle. Returning None.")
1631
1740
  return None
1632
1741
  return Face.ByWire(wire, tolerance=tolerance)
1633
1742
 
1634
1743
  @staticmethod
1635
- def RemoveCollinearEdges(face: topologic.Face, angTolerance: float = 0.1, tolerance: float = 0.0001) -> topologic.Wire:
1744
+ def RemoveCollinearEdges(face, angTolerance: float = 0.1, tolerance: float = 0.0001):
1636
1745
  """
1637
1746
  Removes any collinear edges in the input face.
1638
1747
 
1639
1748
  Parameters
1640
1749
  ----------
1641
- face : topologic.Face
1750
+ face : topologic_core.Face
1642
1751
  The input face.
1643
1752
  angTolerance : float , optional
1644
1753
  The desired angular tolerance. The default is 0.1.
@@ -1647,19 +1756,51 @@ class Face(Topology):
1647
1756
 
1648
1757
  Returns
1649
1758
  -------
1650
- topologic.Face
1759
+ topologic_core.Face
1651
1760
  The created face without any collinear edges.
1652
1761
 
1653
1762
  """
1654
1763
  from topologicpy.Wire import Wire
1764
+ from topologicpy.Topology import Topology
1655
1765
 
1656
- if not isinstance(face, topologic.Face):
1766
+ if not Topology.IsInstance(face, "Face"):
1657
1767
  print("Face.RemoveCollinearEdges - Error: The input face parameter is not a valid face. Returning None.")
1658
1768
  return None
1659
1769
  eb = Wire.RemoveCollinearEdges(Face.Wire(face), angTolerance=angTolerance, tolerance=tolerance)
1660
1770
  ib = [Wire.RemoveCollinearEdges(w, angTolerance=angTolerance, tolerance=tolerance) for w in Face.InternalBoundaries(face)]
1661
1771
  return Face.ByWires(eb, ib)
1662
1772
 
1773
+ @staticmethod
1774
+ def Simplify(face, tolerance=0.0001):
1775
+ """
1776
+ Simplifies the input face edges based on the Douglas Peucker algorthim. See https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm
1777
+ Part of this code was contributed by gaoxipeng. See https://github.com/wassimj/topologicpy/issues/35
1778
+
1779
+ Parameters
1780
+ ----------
1781
+ face : topologic_core.Face
1782
+ The input face.
1783
+ tolerance : float , optional
1784
+ The desired tolerance. The default is 0.0001. Edges shorter than this length will be removed.
1785
+
1786
+ Returns
1787
+ -------
1788
+ topologic_core.Face
1789
+ The simplified face.
1790
+ """
1791
+ from topologicpy.Wire import Wire
1792
+ from topologicpy.Topology import Topology
1793
+
1794
+ if not Topology.IsInstance(face, "Face"):
1795
+ print("Face.Simplify - Error: The input face parameter is not a valid face. Returning None.")
1796
+ return None
1797
+
1798
+ eb = Face.ExternalBoundary(face)
1799
+ eb = Wire.Simplify(eb, tolerance=tolerance)
1800
+ ibList = Face.InternalBoundaries(face)
1801
+ ibList = [Wire.Simplify(ib) for ib in ibList]
1802
+ return Face.ByWires(eb, ibList)
1803
+
1663
1804
  @staticmethod
1664
1805
  def Skeleton(face, tolerance=0.001):
1665
1806
  """
@@ -1668,31 +1809,33 @@ class Face(Topology):
1668
1809
 
1669
1810
  Parameters
1670
1811
  ----------
1671
- face : topologic.Face
1812
+ face : topologic_core.Face
1672
1813
  The input face.
1673
1814
  tolerance : float , optional
1674
1815
  The desired tolerance. The default is 0.001. (This is set to a larger number than the usual 0.0001 as it was found to work better)
1675
1816
 
1676
1817
  Returns
1677
1818
  -------
1678
- topologic.Wire
1819
+ topologic_core.Wire
1679
1820
  The created straight skeleton.
1680
1821
 
1681
1822
  """
1682
1823
  from topologicpy.Wire import Wire
1683
- if not isinstance(face, topologic.Face):
1824
+ from topologicpy.Topology import Topology
1825
+
1826
+ if not Topology.IsInstance(face, "Face"):
1684
1827
  print("Face.Skeleton - Error: The input face is not a valid topologic face. Retruning None.")
1685
1828
  return None
1686
1829
  return Wire.Skeleton(face, tolerance=tolerance)
1687
1830
 
1688
1831
  @staticmethod
1689
- def Square(origin: topologic.Vertex = None, size: float = 1.0, direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001) -> topologic.Face:
1832
+ def Square(origin= None, size: float = 1.0, direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001):
1690
1833
  """
1691
1834
  Creates a square.
1692
1835
 
1693
1836
  Parameters
1694
1837
  ----------
1695
- origin : topologic.Vertex , optional
1838
+ origin : topologic_core.Vertex , optional
1696
1839
  The location of the origin of the square. The default is None which results in the square being placed at (0, 0, 0).
1697
1840
  size : float , optional
1698
1841
  The size of the square. The default is 1.0.
@@ -1705,20 +1848,62 @@ class Face(Topology):
1705
1848
 
1706
1849
  Returns
1707
1850
  -------
1708
- topologic.Face
1851
+ topologic_core.Face
1709
1852
  The created square.
1710
1853
 
1711
1854
  """
1712
1855
  return Face.Rectangle(origin=origin, width=size, length=size, direction=direction, placement=placement, tolerance=tolerance)
1713
1856
 
1857
+
1714
1858
  @staticmethod
1715
- def Star(origin: topologic.Vertex = None, radiusA: float = 1.0, radiusB: float = 0.4, rays: int = 5, direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001) -> topologic.Face:
1859
+ def Squircle(origin = None, radius: float = 0.5, sides: int = 121, a: float = 2.0, b: float = 2.0, direction: list = [0, 0, 1], placement: str = "center", angTolerance: float = 0.1, tolerance: float = 0.0001):
1860
+ """
1861
+ Creates a Squircle which is a hybrid between a circle and a square. See https://en.wikipedia.org/wiki/Squircle
1862
+
1863
+ Parameters
1864
+ ----------
1865
+ origin : topologic_core.Vertex , optional
1866
+ The location of the origin of the squircle. The default is None which results in the squircle being placed at (0, 0, 0).
1867
+ radius : float , optional
1868
+ The radius of the squircle. The default is 0.5.
1869
+ sides : int , optional
1870
+ The number of sides of the squircle. The default is 121.
1871
+ a : float , optional
1872
+ The "a" factor affects the x position of the points to interpolate between a circle and a square.
1873
+ A value of 1 will create a circle. Higher values will create a more square-like shape. The default is 2.0.
1874
+ b : float , optional
1875
+ The "b" factor affects the y position of the points to interpolate between a circle and a square.
1876
+ A value of 1 will create a circle. Higher values will create a more square-like shape. The default is 2.0.
1877
+ radius : float , optional
1878
+ The desired radius of the squircle. The default is 0.5.
1879
+ sides : int , optional
1880
+ The desired number of sides for the squircle. The default is 100.
1881
+ direction : list , optional
1882
+ The vector representing the up direction of the circle. The default is [0, 0, 1].
1883
+ placement : str , optional
1884
+ The description of the placement of the origin of the circle. This can be "center", "lowerleft", "upperleft", "lowerright", or "upperright". It is case insensitive. The default is "center".
1885
+ angTolerance : float , optional
1886
+ The desired angular tolerance. The default is 0.1.
1887
+ tolerance : float , optional
1888
+ The desired tolerance. The default is 0.0001.
1889
+
1890
+ Returns
1891
+ -------
1892
+ topologic_core.Face
1893
+ The created squircle.
1894
+ """
1895
+ from topologicpy.Wire import Wire
1896
+ wire = Wire.Squircle(origin = origin, radius= radius, sides = sides, a = a, b = b, direction = direction, placement = placement, angTolerance = angTolerance, tolerance = tolerance)
1897
+ return Face.ByWire(wire)
1898
+
1899
+ @staticmethod
1900
+ def Star(origin= None, radiusA: float = 1.0, radiusB: float = 0.4, rays: int = 5, direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001):
1716
1901
  """
1717
1902
  Creates a star.
1718
1903
 
1719
1904
  Parameters
1720
1905
  ----------
1721
- origin : topologic.Vertex, optional
1906
+ origin : topologic_core.Vertex, optional
1722
1907
  The location of the origin of the star. The default is None which results in the star being placed at (0, 0, 0).
1723
1908
  radiusA : float , optional
1724
1909
  The outer radius of the star. The default is 1.0.
@@ -1735,25 +1920,27 @@ class Face(Topology):
1735
1920
 
1736
1921
  Returns
1737
1922
  -------
1738
- topologic.Face
1923
+ topologic_core.Face
1739
1924
  The created face.
1740
1925
 
1741
1926
  """
1742
1927
  from topologicpy.Wire import Wire
1928
+ from topologicpy.Topology import Topology
1929
+
1743
1930
  wire = Wire.Star(origin=origin, radiusA=radiusA, radiusB=radiusB, rays=rays, direction=direction, placement=placement, tolerance=tolerance)
1744
- if not isinstance(wire, topologic.Wire):
1931
+ if not Topology.IsInstance(wire, "Wire"):
1745
1932
  print("Face.Rectangle - Error: Could not create the base wire for the star. Returning None.")
1746
1933
  return None
1747
1934
  return Face.ByWire(wire, tolerance=tolerance)
1748
1935
 
1749
1936
  @staticmethod
1750
- def Trapezoid(origin: topologic.Vertex = None, widthA: float = 1.0, widthB: float = 0.75, offsetA: float = 0.0, offsetB: float = 0.0, length: float = 1.0, direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001) -> topologic.Face:
1937
+ def Trapezoid(origin= None, widthA: float = 1.0, widthB: float = 0.75, offsetA: float = 0.0, offsetB: float = 0.0, length: float = 1.0, direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001):
1751
1938
  """
1752
1939
  Creates a trapezoid.
1753
1940
 
1754
1941
  Parameters
1755
1942
  ----------
1756
- origin : topologic.Vertex, optional
1943
+ origin : topologic_core.Vertex, optional
1757
1944
  The location of the origin of the trapezoid. The default is None which results in the trapezoid being placed at (0, 0, 0).
1758
1945
  widthA : float , optional
1759
1946
  The width of the bottom edge of the trapezoid. The default is 1.0.
@@ -1774,25 +1961,27 @@ class Face(Topology):
1774
1961
 
1775
1962
  Returns
1776
1963
  -------
1777
- topologic.Face
1964
+ topologic_core.Face
1778
1965
  The created trapezoid.
1779
1966
 
1780
1967
  """
1781
1968
  from topologicpy.Wire import Wire
1969
+ from topologicpy.Topology import Topology
1970
+
1782
1971
  wire = Wire.Trapezoid(origin=origin, widthA=widthA, widthB=widthB, offsetA=offsetA, offsetB=offsetB, length=length, direction=direction, placement=placement, tolerance=tolerance)
1783
- if not isinstance(wire, topologic.Wire):
1972
+ if not Topology.IsInstance(wire, "Wire"):
1784
1973
  print("Face.Rectangle - Error: Could not create the base wire for the trapezoid. Returning None.")
1785
1974
  return None
1786
1975
  return Face.ByWire(wire, tolerance=tolerance)
1787
1976
 
1788
1977
  @staticmethod
1789
- def Triangulate(face:topologic.Face, mode: int = 0, meshSize: float = None, tolerance: float = 0.0001) -> list:
1978
+ def Triangulate(face, mode: int = 0, meshSize: float = None, tolerance: float = 0.0001) -> list:
1790
1979
  """
1791
1980
  Triangulates the input face and returns a list of faces.
1792
1981
 
1793
1982
  Parameters
1794
1983
  ----------
1795
- face : topologic.Face
1984
+ face : topologic_core.Face
1796
1985
  The input face.
1797
1986
  tolerance : float , optional
1798
1987
  The desired tolerance. The default is 0.0001.
@@ -1828,7 +2017,7 @@ class Face(Topology):
1828
2017
 
1829
2018
  Parameters
1830
2019
  ----------
1831
- face : topologic.Face
2020
+ face : topologic_core.Face
1832
2021
  The input face.
1833
2022
  meshSize : float , optional
1834
2023
  The desired mesh size.
@@ -1837,7 +2026,7 @@ class Face(Topology):
1837
2026
 
1838
2027
  Returns
1839
2028
  ----------
1840
- topologic.Shell
2029
+ topologic_core.Shell
1841
2030
  The shell of triangular meshes.
1842
2031
 
1843
2032
  """
@@ -1877,7 +2066,7 @@ class Face(Topology):
1877
2066
  from topologicpy.Wire import Wire
1878
2067
  from topologicpy.Face import Face
1879
2068
 
1880
- if not isinstance(face, topologic.Face):
2069
+ if not Topology.IsInstance(face, "Face"):
1881
2070
  print("Shell.ByMeshFace - Error: The input face parameter is not a valid face. Returning None.")
1882
2071
  return None
1883
2072
  if not meshSize:
@@ -1952,7 +2141,7 @@ class Face(Topology):
1952
2141
  faces.append(Face.ByVertices(face_vertices))
1953
2142
  return faces
1954
2143
 
1955
- if not isinstance(face, topologic.Face):
2144
+ if not Topology.IsInstance(face, "Face"):
1956
2145
  print("Face.Triangulate - Error: The input face parameter is not a valid face. Returning None.")
1957
2146
  return None
1958
2147
  vertices = Topology.Vertices(face)
@@ -1966,7 +2155,7 @@ class Face(Topology):
1966
2155
  shell_faces = []
1967
2156
  for i in range(0,5,1):
1968
2157
  try:
1969
- _ = topologic.FaceUtility.Triangulate(flatFace, float(i)*0.1, shell_faces)
2158
+ _ = topologic.FaceUtility.Triangulate(flatFace, float(i)*0.1, shell_faces) # Hook to Core
1970
2159
  break
1971
2160
  except:
1972
2161
  continue
@@ -1981,49 +2170,51 @@ class Face(Topology):
1981
2170
  if Face.Angle(face, f) > 90:
1982
2171
  wire = Face.ExternalBoundary(f)
1983
2172
  wire = Wire.Invert(wire)
1984
- f = topologic.Face.ByExternalBoundary(wire)
2173
+ f = Face.ByWire(wire)
1985
2174
  finalFaces.append(f)
1986
2175
  else:
1987
2176
  finalFaces.append(f)
1988
2177
  return finalFaces
1989
2178
 
1990
2179
  @staticmethod
1991
- def TrimByWire(face: topologic.Face, wire: topologic.Wire, reverse: bool = False) -> topologic.Face:
2180
+ def TrimByWire(face, wire, reverse: bool = False):
1992
2181
  """
1993
2182
  Trims the input face by the input wire.
1994
2183
 
1995
2184
  Parameters
1996
2185
  ----------
1997
- face : topologic.Face
2186
+ face : topologic_core.Face
1998
2187
  The input face.
1999
- wire : topologic.Wire
2188
+ wire : topologic_core.Wire
2000
2189
  The input wire.
2001
2190
  reverse : bool , optional
2002
2191
  If set to True, the effect of the trim will be reversed. The default is False.
2003
2192
 
2004
2193
  Returns
2005
2194
  -------
2006
- topologic.Face
2195
+ topologic_core.Face
2007
2196
  The resulting trimmed face.
2008
2197
 
2009
2198
  """
2010
- if not isinstance(face, topologic.Face):
2199
+ from topologicpy.Topology import Topology
2200
+
2201
+ if not Topology.IsInstance(face, "Face"):
2011
2202
  return None
2012
- if not isinstance(wire, topologic.Wire):
2203
+ if not Topology.IsInstance(wire, "Wire"):
2013
2204
  return face
2014
- trimmed_face = topologic.FaceUtility.TrimByWire(face, wire, False)
2205
+ trimmed_face = topologic.FaceUtility.TrimByWire(face, wire, False) # Hook to Core
2015
2206
  if reverse:
2016
2207
  trimmed_face = face.Difference(trimmed_face)
2017
2208
  return trimmed_face
2018
2209
 
2019
2210
  @staticmethod
2020
- def VertexByParameters(face: topologic.Face, u: float = 0.5, v: float = 0.5) -> topologic.Vertex:
2211
+ def VertexByParameters(face, u: float = 0.5, v: float = 0.5):
2021
2212
  """
2022
2213
  Creates a vertex at the *u* and *v* parameters of the input face.
2023
2214
 
2024
2215
  Parameters
2025
2216
  ----------
2026
- face : topologic.Face
2217
+ face : topologic_core.Face
2027
2218
  The input face.
2028
2219
  u : float , optional
2029
2220
  The *u* parameter of the input face. The default is 0.5.
@@ -2036,20 +2227,22 @@ class Face(Topology):
2036
2227
  The created vertex.
2037
2228
 
2038
2229
  """
2039
- if not isinstance(face, topologic.Face):
2230
+ from topologicpy.Topology import Topology
2231
+
2232
+ if not Topology.IsInstance(face, "Face"):
2040
2233
  return None
2041
- return topologic.FaceUtility.VertexAtParameters(face, u, v)
2234
+ return topologic.FaceUtility.VertexAtParameters(face, u, v) # Hook to Core
2042
2235
 
2043
2236
  @staticmethod
2044
- def VertexParameters(face: topologic.Face, vertex: topologic.Vertex, outputType: str = "uv", mantissa: int = 6) -> list:
2237
+ def VertexParameters(face, vertex, outputType: str = "uv", mantissa: int = 6) -> list:
2045
2238
  """
2046
2239
  Returns the *u* and *v* parameters of the input face at the location of the input vertex.
2047
2240
 
2048
2241
  Parameters
2049
2242
  ----------
2050
- face : topologic.Face
2243
+ face : topologic_core.Face
2051
2244
  The input face.
2052
- vertex : topologic.Vertex
2245
+ vertex : topologic_core.Vertex
2053
2246
  The input vertex.
2054
2247
  outputType : string , optional
2055
2248
  The string defining the desired output. This can be any subset or permutation of "uv". It is case insensitive. The default is "uv".
@@ -2062,11 +2255,13 @@ class Face(Topology):
2062
2255
  The list of *u* and/or *v* as specified by the outputType input.
2063
2256
 
2064
2257
  """
2065
- if not isinstance(face, topologic.Face):
2258
+ from topologicpy.Topology import Topology
2259
+
2260
+ if not Topology.IsInstance(face, "Face"):
2066
2261
  return None
2067
- if not isinstance(vertex, topologic.Vertex):
2262
+ if not Topology.IsInstance(vertex, "Vertex"):
2068
2263
  return None
2069
- params = topologic.FaceUtility.ParametersAtVertex(face, vertex)
2264
+ params = topologic.FaceUtility.ParametersAtVertex(face, vertex) # Hook to Core
2070
2265
  u = round(params[0], mantissa)
2071
2266
  v = round(params[1], mantissa)
2072
2267
  outputType = list(outputType.lower())
@@ -2079,13 +2274,13 @@ class Face(Topology):
2079
2274
  return returnResult
2080
2275
 
2081
2276
  @staticmethod
2082
- def Vertices(face: topologic.Face) -> list:
2277
+ def Vertices(face) -> list:
2083
2278
  """
2084
2279
  Returns the vertices of the input face.
2085
2280
 
2086
2281
  Parameters
2087
2282
  ----------
2088
- face : topologic.Face
2283
+ face : topologic_core.Face
2089
2284
  The input face.
2090
2285
 
2091
2286
  Returns
@@ -2094,38 +2289,40 @@ class Face(Topology):
2094
2289
  The list of vertices.
2095
2290
 
2096
2291
  """
2097
- if not isinstance(face, topologic.Face):
2292
+ from topologicpy.Topology import Topology
2293
+
2294
+ if not Topology.IsInstance(face, "Face"):
2098
2295
  return None
2099
2296
  vertices = []
2100
2297
  _ = face.Vertices(None, vertices)
2101
2298
  return vertices
2102
2299
 
2103
2300
  @staticmethod
2104
- def Wire(face: topologic.Face) -> topologic.Wire:
2301
+ def Wire(face):
2105
2302
  """
2106
2303
  Returns the external boundary (closed wire) of the input face.
2107
2304
 
2108
2305
  Parameters
2109
2306
  ----------
2110
- face : topologic.Face
2307
+ face : topologic_core.Face
2111
2308
  The input face.
2112
2309
 
2113
2310
  Returns
2114
2311
  -------
2115
- topologic.Wire
2312
+ topologic_core.Wire
2116
2313
  The external boundary of the input face.
2117
2314
 
2118
2315
  """
2119
2316
  return face.ExternalBoundary()
2120
2317
 
2121
2318
  @staticmethod
2122
- def Wires(face: topologic.Face) -> list:
2319
+ def Wires(face) -> list:
2123
2320
  """
2124
2321
  Returns the wires of the input face.
2125
2322
 
2126
2323
  Parameters
2127
2324
  ----------
2128
- face : topologic.Face
2325
+ face : topologic_core.Face
2129
2326
  The input face.
2130
2327
 
2131
2328
  Returns
@@ -2134,7 +2331,9 @@ class Face(Topology):
2134
2331
  The list of wires.
2135
2332
 
2136
2333
  """
2137
- if not isinstance(face, topologic.Face):
2334
+ from topologicpy.Topology import Topology
2335
+
2336
+ if not Topology.IsInstance(face, "Face"):
2138
2337
  return None
2139
2338
  wires = []
2140
2339
  _ = face.Wires(None, wires)