topologicpy 0.7.15__py3-none-any.whl → 0.7.18__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 +19 -12
- topologicpy/CellComplex.py +3 -3
- topologicpy/DGL.py +1 -1
- topologicpy/Edge.py +46 -20
- topologicpy/Face.py +215 -110
- topologicpy/Graph.py +230 -106
- topologicpy/Neo4j.py +2 -2
- topologicpy/Shell.py +57 -11
- topologicpy/Topology.py +17 -37
- topologicpy/Vector.py +1 -3
- topologicpy/Vertex.py +2 -4
- topologicpy/Wire.py +288 -191
- topologicpy/version.py +1 -1
- {topologicpy-0.7.15.dist-info → topologicpy-0.7.18.dist-info}/METADATA +37 -1
- topologicpy-0.7.18.dist-info/RECORD +33 -0
- topologicpy-0.7.15.dist-info/RECORD +0 -33
- {topologicpy-0.7.15.dist-info → topologicpy-0.7.18.dist-info}/LICENSE +0 -0
- {topologicpy-0.7.15.dist-info → topologicpy-0.7.18.dist-info}/WHEEL +0 -0
- {topologicpy-0.7.15.dist-info → topologicpy-0.7.18.dist-info}/top_level.txt +0 -0
topologicpy/Wire.py
CHANGED
@@ -334,12 +334,9 @@ class Wire(Topology):
|
|
334
334
|
return Wire.ByEdges(edges, tolerance=tolerance)
|
335
335
|
|
336
336
|
@staticmethod
|
337
|
-
def ByOffset(wire, offset: float = 1.0,
|
338
|
-
miter: bool = False, miterThreshold: float = None,
|
339
|
-
offsetKey: str = None, miterThresholdKey: str = None,
|
340
|
-
step: bool = True, angTolerance: float = 0.1, tolerance: float = 0.0001):
|
337
|
+
def ByOffset(wire, offset: float = 1.0, bisectors: bool = False, tolerance: float = 0.0001):
|
341
338
|
"""
|
342
|
-
Creates an offset wire from the input wire.
|
339
|
+
Creates an offset wire from the input wire. A positive offset value results in an offset to the interior of the anti-clockwise wire.
|
343
340
|
|
344
341
|
Parameters
|
345
342
|
----------
|
@@ -347,18 +344,8 @@ class Wire(Topology):
|
|
347
344
|
The input wire.
|
348
345
|
offset : float , optional
|
349
346
|
The desired offset distance. The default is 1.0.
|
350
|
-
|
351
|
-
|
352
|
-
miterThreshold : float , optional
|
353
|
-
The distance beyond which a miter should be added. The default is None which means the miter threshold is set to the offset distance multiplied by the square root of 2.
|
354
|
-
offsetKey : str , optional
|
355
|
-
If specified, the dictionary of the edges will be queried for this key to specify the desired offset. The default is None.
|
356
|
-
miterThresholdKey : str , optional
|
357
|
-
If specified, the dictionary of the vertices will be queried for this key to specify the desired miter threshold distance. The default is None.
|
358
|
-
step : bool , optional
|
359
|
-
If set to True, The transition between collinear edges with different offsets will be a step. Otherwise, it will be a continous edge. The default is True.
|
360
|
-
angTolerance : float , optional
|
361
|
-
The desired angular tolerance. The default is 0.1.
|
347
|
+
bisectors : bool , optional
|
348
|
+
If set to True, The bisectors (seams) edges will be included in the returned wire. The default is False.
|
362
349
|
tolerance : float , optional
|
363
350
|
The desired tolerance. The default is 0.0001.
|
364
351
|
|
@@ -368,169 +355,76 @@ class Wire(Topology):
|
|
368
355
|
The created wire.
|
369
356
|
|
370
357
|
"""
|
358
|
+
def compute_h(alpha, offset):
|
359
|
+
import math
|
360
|
+
alpha = math.radians(alpha) *0.5
|
361
|
+
h = offset/math.cos(alpha)
|
362
|
+
return h
|
363
|
+
|
371
364
|
from topologicpy.Vertex import Vertex
|
372
365
|
from topologicpy.Edge import Edge
|
373
366
|
from topologicpy.Face import Face
|
374
367
|
from topologicpy.Cluster import Cluster
|
375
368
|
from topologicpy.Topology import Topology
|
376
369
|
from topologicpy.Dictionary import Dictionary
|
377
|
-
|
378
|
-
|
370
|
+
from topologicpy.Vector import Vector
|
371
|
+
import math
|
379
372
|
|
380
373
|
if not Topology.IsInstance(wire, "Wire"):
|
374
|
+
print("Wire.ByOffset - Error: The input wire parameter is not a valid wire. Returning None")
|
381
375
|
return None
|
382
|
-
if
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
vertices = Wire.Vertices(wire)
|
391
|
-
flatEdges = []
|
392
|
-
flatVertices = []
|
393
|
-
newEdges = []
|
394
|
-
for i in range(len(vertices)):
|
395
|
-
flatVertex = Topology.Flatten(vertices[i], origin=origin, direction=normal)
|
396
|
-
flatVertices.append(flatVertex)
|
397
|
-
vertices = flatVertices
|
398
|
-
for i in range(len(edges)):
|
399
|
-
flatEdge = Topology.Flatten(edges[i], origin=origin, direction=normal)
|
400
|
-
flatEdges.append(flatEdge)
|
401
|
-
if offsetKey:
|
402
|
-
d = Topology.Dictionary(edges[i])
|
403
|
-
value = Dictionary.ValueAtKey(d, key=offsetKey)
|
404
|
-
c = Topology.Centroid(flatEdge)
|
405
|
-
if value:
|
406
|
-
finalOffset = value
|
407
|
-
else:
|
408
|
-
finalOffset = offset
|
409
|
-
else:
|
410
|
-
finalOffset = offset
|
411
|
-
e1 = Edge.ByOffset2D(flatEdge,finalOffset)
|
412
|
-
newEdges.append(e1)
|
413
|
-
edges = flatEdges
|
414
|
-
newVertices = []
|
415
|
-
dupVertices = []
|
416
|
-
if Wire.IsClosed(wire):
|
417
|
-
e1 = newEdges[-1]
|
418
|
-
e2 = newEdges[0]
|
419
|
-
intV = Topology.Intersect(e1,e2)
|
420
|
-
if intV:
|
421
|
-
newVertices.append(intV)
|
422
|
-
dupVertices.append(vertices[0])
|
423
|
-
elif step:
|
424
|
-
edgeVertices= Edge.Vertices(e1)
|
425
|
-
newVertices.append(Vertex.NearestVertex(vertices[-1], Cluster.ByTopologies(edgeVertices), useKDTree=False))
|
426
|
-
edgeVertices= Edge.Vertices(e2)
|
427
|
-
newVertices.append(Vertex.NearestVertex(vertices[0], Cluster.ByTopologies(edgeVertices), useKDTree=False))
|
428
|
-
dupVertices.append(vertices[0])
|
429
|
-
dupVertices.append(vertices[0])
|
430
|
-
else:
|
431
|
-
tempEdge1 = Edge.ByVertices([Edge.StartVertex(e1), Edge.EndVertex(e2)], tolerance=tolerance, silent=True)
|
432
|
-
normal = Edge.Normal(e1)
|
433
|
-
normal = [normal[0]*finalOffset*10, normal[1]*finalOffset*10, normal[2]*finalOffset*10]
|
434
|
-
tempV = Vertex.ByCoordinates(vertices[0].X()+normal[0], vertices[0].Y()+normal[1], vertices[0].Z()+normal[2])
|
435
|
-
tempEdge2 = Edge.ByVertices([vertices[0], tempV], tolerance=tolerance, silent=True)
|
436
|
-
intV = Topology.Intersect(tempEdge1,tempEdge2)
|
437
|
-
newVertices.append(intV)
|
438
|
-
dupVertices.append(vertices[0])
|
376
|
+
if abs(offset) < tolerance:
|
377
|
+
return wire
|
378
|
+
vertices = Topology.Vertices(wire)
|
379
|
+
if len(vertices) < 3:
|
380
|
+
print("Wire.ByOffset - Error: The input wire parameter contains less than three vertices. Cannot proceed. Returning None")
|
381
|
+
return None
|
382
|
+
if not Wire.IsClosed(wire):
|
383
|
+
w = Wire.ByVertices(vertices, close=True)
|
439
384
|
else:
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
385
|
+
w = wire
|
386
|
+
three_vertices = Wire.Vertices(w)[0:3]
|
387
|
+
temp_w = Wire.ByVertices(three_vertices, close=True)
|
388
|
+
flat_face = Face.ByWire(temp_w, tolerance=tolerance)
|
389
|
+
origin = Vertex.Origin()
|
390
|
+
normal = Face.Normal(flat_face)
|
391
|
+
flat_wire = Topology.Flatten(w, origin=origin, direction=normal)
|
392
|
+
edges = Wire.Edges(flat_wire)
|
393
|
+
vertices = Wire.Vertices(flat_wire)
|
394
|
+
int_angles = Wire.InteriorAngles(flat_wire)
|
395
|
+
bisector_list = []
|
396
|
+
final_vertices = []
|
397
|
+
for i in range(len(vertices)):
|
398
|
+
v_edges = Topology.SuperTopologies(vertices[i], flat_wire, topologyType="edge")
|
399
|
+
if len(v_edges) < 2:
|
400
|
+
continue
|
401
|
+
bisector = Edge.Bisect(v_edges[0], v_edges[1], length=offset, placement=1)
|
402
|
+
if int_angles[i] > 180:
|
403
|
+
bisector = Topology.TranslateByDirectionDistance(bisector, Vector.Reverse(Edge.Direction(bisector)), distance=abs(offset))
|
404
|
+
bisector = Edge.Reverse(bisector)
|
405
|
+
|
406
|
+
h = abs(compute_h(180-int_angles[i], abs(offset)))
|
407
|
+
bisector = Edge.SetLength(bisector, length=h, bothSides=False)
|
408
|
+
final_vertices.append(Edge.EndVertex(bisector))
|
409
|
+
bisector_list.append(bisector)
|
410
|
+
return_wire = Wire.ByVertices(final_vertices, close=Wire.IsClosed(wire))
|
411
|
+
vertices = Topology.Vertices(return_wire)
|
465
412
|
if not Wire.IsClosed(wire):
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
else:
|
482
|
-
finalMiterThreshold = miterThreshold
|
483
|
-
else:
|
484
|
-
finalMiterThreshold = miterThreshold
|
485
|
-
if Vertex.Distance(vertices[i], newVertices[i]) > abs(finalMiterThreshold):
|
486
|
-
st = Topology.SuperTopologies(newVertices[i], newWire, topologyType="edge")
|
487
|
-
if len(st) > 1:
|
488
|
-
e1 = st[0]
|
489
|
-
e2 = st[1]
|
490
|
-
if not Edge.IsCollinear(e1, e2, tolerance=tolerance):
|
491
|
-
e1 = Edge.Reverse(e1, tolerance=tolerance)
|
492
|
-
bisector = Edge.ByVertices([vertices[i], newVertices[i]], tolerance=tolerance)
|
493
|
-
nv = Edge.VertexByDistance(bisector, distance=finalMiterThreshold, origin=Edge.StartVertex(bisector), tolerance=0.0001)
|
494
|
-
vec = Edge.Normal(bisector)
|
495
|
-
nv2 = Topology.Translate(nv, vec[0], vec[1], 0)
|
496
|
-
nv3 = Topology.Translate(nv, -vec[0], -vec[1], 0)
|
497
|
-
miterEdge = Edge.ByVertices([nv2,nv3], tolerance=tolerance)
|
498
|
-
if miterEdge:
|
499
|
-
miterEdge = Edge.SetLength(miterEdge, abs(offset)*10)
|
500
|
-
msv = Topology.Intersect(miterEdge, e1)
|
501
|
-
mev = Topology.Intersect(miterEdge, e2)
|
502
|
-
#if msv == None or mev == None:
|
503
|
-
#Topology.Show(miterEdge, wire, renderer="browser")
|
504
|
-
#Topology.Show(miterEdge, e2, wire, renderer="browser")
|
505
|
-
if (Vertex.IsInternal(msv, e1,tolerance=0.01) and (Vertex.IsInternal(mev, e2, tolerance=0.01))):
|
506
|
-
miterEdge = Edge.ByVertices([msv, mev], tolerance=tolerance)
|
507
|
-
if miterEdge:
|
508
|
-
cleanMiterEdges.append(miterEdge)
|
509
|
-
miterEdge = Edge.SetLength(miterEdge, Edge.Length(miterEdge)*1.02)
|
510
|
-
miterEdges.append(miterEdge)
|
511
|
-
|
512
|
-
c = Topology.SelfMerge(Cluster.ByTopologies(newEdges+miterEdges), tolerance=tolerance)
|
513
|
-
vertices = Wire.Vertices(c)
|
514
|
-
subtractEdges = []
|
515
|
-
for v in vertices:
|
516
|
-
edges = Topology.SuperTopologies(v, c, topologyType="edge")
|
517
|
-
if len(edges) == 2:
|
518
|
-
if not Edge.IsCollinear(edges[0], edges[1], tolerance=tolerance):
|
519
|
-
adjacentVertices = Topology.AdjacentTopologies(v, c)
|
520
|
-
total = 0
|
521
|
-
for adjV in adjacentVertices:
|
522
|
-
tempEdges = Topology.SuperTopologies(adjV, c, topologyType="edge")
|
523
|
-
total += len(tempEdges)
|
524
|
-
if total == 8:
|
525
|
-
subtractEdges = subtractEdges+edges
|
526
|
-
|
527
|
-
if len(subtractEdges) > 0:
|
528
|
-
newWire = Topology.Boolean(newWire, Cluster.ByTopologies(subtractEdges), operation="difference", tolerance=tolerance)
|
529
|
-
if len(cleanMiterEdges) > 0:
|
530
|
-
newWire = Topology.Boolean(newWire, Cluster.ByTopologies(cleanMiterEdges), operation="merge", tolerance=tolerance)
|
531
|
-
|
532
|
-
newWire = Topology.Unflatten(newWire, origin=origin, direction=normal)
|
533
|
-
return newWire
|
413
|
+
sv = Topology.Vertices(flat_wire)[0]
|
414
|
+
ev = Topology.Vertices(flat_wire)[-1]
|
415
|
+
first_edge = Topology.SuperTopologies(sv, flat_wire, topologyType="edge")[0]
|
416
|
+
first_normal = Edge.NormalAsEdge(first_edge, length=abs(offset), u = 0)
|
417
|
+
last_edge = Topology.SuperTopologies(ev, flat_wire, topologyType="edge")[0]
|
418
|
+
last_normal = Edge.NormalAsEdge(last_edge, length=abs(offset), u = 1.0)
|
419
|
+
sv1 = Edge.EndVertex(first_normal)
|
420
|
+
ev1 = Edge.EndVertex(last_normal)
|
421
|
+
vertices = [sv1] + vertices[1:-1] + [ev1]
|
422
|
+
bisector_list = [first_normal] + bisector_list[1:-1] + [last_normal]
|
423
|
+
return_wire = Wire.ByVertices(vertices, close=Wire.IsClosed(wire))
|
424
|
+
if bisectors:
|
425
|
+
return_wire = Topology.SelfMerge(Cluster.ByTopologies(bisector_list, Topology.Edges(return_wire)))
|
426
|
+
return_wire = Topology.Unflatten(return_wire, origin=origin, direction=normal)
|
427
|
+
return return_wire
|
534
428
|
|
535
429
|
@staticmethod
|
536
430
|
def ByVertices(vertices: list, close: bool = True, tolerance: float = 0.0001):
|
@@ -646,10 +540,10 @@ class Wire(Topology):
|
|
646
540
|
if not Topology.IsInstance(origin, "Vertex"):
|
647
541
|
origin = Vertex.ByCoordinates(0, 0, 0)
|
648
542
|
if not Topology.IsInstance(origin, "Vertex"):
|
649
|
-
print("Wire.Circle - Error: The input origin parameter is not a valid Vertex.
|
543
|
+
print("Wire.Circle - Error: The input origin parameter is not a valid Vertex. Returning None.")
|
650
544
|
return None
|
651
545
|
if not placement.lower() in ["center", "lowerleft", "upperleft", "lowerright", "upperright"]:
|
652
|
-
print("Wire.Circle - Error: The input placement parameter is not a
|
546
|
+
print("Wire.Circle - Error: The input placement parameter is not a recognized string. Returning None.")
|
653
547
|
return None
|
654
548
|
radius = abs(radius)
|
655
549
|
if radius < tolerance:
|
@@ -671,8 +565,8 @@ class Wire(Topology):
|
|
671
565
|
sides = int(math.floor(sides))
|
672
566
|
for i in range(sides+1):
|
673
567
|
angle = fromAngle + math.radians(angleRange/sides)*i
|
674
|
-
x = math.
|
675
|
-
y = math.
|
568
|
+
x = math.sin(angle)*radius + origin.X()
|
569
|
+
y = math.cos(angle)*radius + origin.Y()
|
676
570
|
z = origin.Z()
|
677
571
|
xList.append(x)
|
678
572
|
yList.append(y)
|
@@ -746,8 +640,8 @@ class Wire(Topology):
|
|
746
640
|
nearest = nearest_vertex(end, ends)
|
747
641
|
if not nearest in used:
|
748
642
|
d = Vertex.Distance(end, nearest)
|
749
|
-
i1 = Vertex.Index(end, vertices)
|
750
|
-
i2 = Vertex.Index(nearest, vertices)
|
643
|
+
i1 = Vertex.Index(end, vertices, tolerance=tolerance)
|
644
|
+
i2 = Vertex.Index(nearest, vertices, tolerance=tolerance)
|
751
645
|
if i1 == None or i2 == None:
|
752
646
|
print("Wire.Close - Error: Something went wrong. Returning None.")
|
753
647
|
return None
|
@@ -1040,7 +934,7 @@ class Wire(Topology):
|
|
1040
934
|
@staticmethod
|
1041
935
|
def Einstein(origin= None, radius: float = 0.5, direction: list = [0, 0, 1], placement: str = "center", mantissa: int = 6):
|
1042
936
|
"""
|
1043
|
-
Creates an aperiodic monotile, also called an 'einstein' tile (meaning one tile in German, not the name of the famous
|
937
|
+
Creates an aperiodic monotile, also called an 'einstein' tile (meaning one tile in German, not the name of the famous physicist). See https://arxiv.org/abs/2303.10798
|
1044
938
|
|
1045
939
|
Parameters
|
1046
940
|
----------
|
@@ -1106,7 +1000,7 @@ class Wire(Topology):
|
|
1106
1000
|
origin : topologic_core.Vertex , optional
|
1107
1001
|
The location of the origin of the ellipse. The default is None which results in the ellipse being placed at (0, 0, 0).
|
1108
1002
|
inputMode : int , optional
|
1109
|
-
The method by
|
1003
|
+
The method by which the ellipse is defined. The default is 1.
|
1110
1004
|
Based on the inputMode value, only the following inputs will be considered. The options are:
|
1111
1005
|
1. Width and Length (considered inputs: width, length)
|
1112
1006
|
2. Focal Length and Eccentricity (considered inputs: focalLength, eccentricity)
|
@@ -1158,7 +1052,7 @@ class Wire(Topology):
|
|
1158
1052
|
origin : topologic_core.Vertex , optional
|
1159
1053
|
The location of the origin of the ellipse. The default is None which results in the ellipse being placed at (0, 0, 0).
|
1160
1054
|
inputMode : int , optional
|
1161
|
-
The method by
|
1055
|
+
The method by which the ellipse is defined. The default is 1.
|
1162
1056
|
Based on the inputMode value, only the following inputs will be considered. The options are:
|
1163
1057
|
1. Width and Length (considered inputs: width, length)
|
1164
1058
|
2. Focal Length and Eccentricity (considered inputs: focalLength, eccentricity)
|
@@ -1581,7 +1475,7 @@ class Wire(Topology):
|
|
1581
1475
|
outputType : str , optional
|
1582
1476
|
The desired type of output. The options are case insensitive. The default is "contour". The options are:
|
1583
1477
|
- "Default" or "Contours" (wires are not connected)
|
1584
|
-
- "Raster or "Zigzag" or "Toolpath" (the wire ends are connected to create a
|
1478
|
+
- "Raster or "Zigzag" or "Toolpath" (the wire ends are connected to create a continuous path)
|
1585
1479
|
- "Grid" (the wire ends are connected to create a grid).
|
1586
1480
|
mapping : str , optional
|
1587
1481
|
The desired type of mapping for wires with different number of vertices. It is case insensitive. The default is "default". The options are:
|
@@ -1835,10 +1729,10 @@ class Wire(Topology):
|
|
1835
1729
|
for anEdge in edges:
|
1836
1730
|
lengths.append(Edge.Length(anEdge))
|
1837
1731
|
minLength = min(lengths)
|
1838
|
-
|
1732
|
+
normalizedLengths = []
|
1839
1733
|
for aLength in lengths:
|
1840
|
-
|
1841
|
-
return [x for x in itertools.chain(*itertools.zip_longest(
|
1734
|
+
normalizedLengths.append(aLength/minLength)
|
1735
|
+
return [x for x in itertools.chain(*itertools.zip_longest(normalizedLengths, angles)) if x is not None]
|
1842
1736
|
|
1843
1737
|
if (wireA.IsClosed() == False):
|
1844
1738
|
return None
|
@@ -1951,6 +1845,209 @@ class Wire(Topology):
|
|
1951
1845
|
vertices.append(Edge.EndVertex(edge))
|
1952
1846
|
return Wire.ByVertices(vertices)
|
1953
1847
|
|
1848
|
+
@staticmethod
|
1849
|
+
def Miter(wire, offset: float = 0, offsetKey: str = None, tolerance: float = 0.0001, silent: bool = False):
|
1850
|
+
"""
|
1851
|
+
Fillets (rounds) the interior and exterior corners of the input wire given the input radius. See https://en.wikipedia.org/wiki/Fillet_(mechanics)
|
1852
|
+
|
1853
|
+
Parameters
|
1854
|
+
----------
|
1855
|
+
wire : topologic_core.Wire
|
1856
|
+
The input wire.
|
1857
|
+
offset : float
|
1858
|
+
The desired offset length of the miter along each edge.
|
1859
|
+
offsetKey : str , optional
|
1860
|
+
If specified, the dictionary of the vertices will be queried for this key to specify the desired offset length. The default is None.
|
1861
|
+
tolerance : float , optional
|
1862
|
+
The desired tolerance. The default is 0.0001.
|
1863
|
+
silent : bool , optional
|
1864
|
+
If set to False, error and warning messages are printed. Otherwise, they are not. The default is False.
|
1865
|
+
|
1866
|
+
Returns
|
1867
|
+
-------
|
1868
|
+
topologic_core.Wire
|
1869
|
+
The filleted wire.
|
1870
|
+
|
1871
|
+
"""
|
1872
|
+
def start_from(edge, v):
|
1873
|
+
sv = Edge.StartVertex(edge)
|
1874
|
+
ev = Edge.EndVertex(edge)
|
1875
|
+
if Vertex.Distance(v, ev) < Vertex.Distance(v, sv):
|
1876
|
+
return Edge.Reverse(edge)
|
1877
|
+
return edge
|
1878
|
+
|
1879
|
+
def compute_kite_edges(alpha, r):
|
1880
|
+
# Convert angle to radians
|
1881
|
+
alpha = math.radians(alpha) *0.5
|
1882
|
+
h = r/math.cos(alpha)
|
1883
|
+
a = math.sqrt(h*h - r*r)
|
1884
|
+
return [a,h]
|
1885
|
+
|
1886
|
+
import math
|
1887
|
+
from topologicpy.Vertex import Vertex
|
1888
|
+
from topologicpy.Edge import Edge
|
1889
|
+
from topologicpy.Wire import Wire
|
1890
|
+
from topologicpy.Face import Face
|
1891
|
+
from topologicpy.Cluster import Cluster
|
1892
|
+
from topologicpy.Topology import Topology
|
1893
|
+
from topologicpy.Vector import Vector
|
1894
|
+
from topologicpy.Dictionary import Dictionary
|
1895
|
+
|
1896
|
+
if not Topology.IsInstance(wire, "Wire"):
|
1897
|
+
if not silent:
|
1898
|
+
print("Wire.Fillet - Error: The input wire parameter is not a valid wire. Returning None.")
|
1899
|
+
return None
|
1900
|
+
if not Wire.IsManifold(wire):
|
1901
|
+
if not silent:
|
1902
|
+
print("Wire.Fillet - Error: The input wire parameter is not manifold. Returning None.")
|
1903
|
+
return None
|
1904
|
+
if not Topology.IsPlanar(wire):
|
1905
|
+
if not silent:
|
1906
|
+
print("Wire.Fillet - Error: The input wire parameter is not planar. Returning None.")
|
1907
|
+
return None
|
1908
|
+
|
1909
|
+
orig_offset = offset
|
1910
|
+
f = Face.BoundingRectangle(wire, tolerance=tolerance)
|
1911
|
+
normal = Face.Normal(f)
|
1912
|
+
flat_wire = Topology.Flatten(wire, origin=Vertex.Origin(), direction=normal)
|
1913
|
+
vertices = Topology.Vertices(flat_wire)
|
1914
|
+
final_vertices = []
|
1915
|
+
miters = []
|
1916
|
+
for v in vertices:
|
1917
|
+
offset = orig_offset
|
1918
|
+
edges = Topology.SuperTopologies(v, flat_wire, topologyType="edge")
|
1919
|
+
if len(edges) == 2:
|
1920
|
+
for edge in edges:
|
1921
|
+
ev = Edge.EndVertex(edge)
|
1922
|
+
if Vertex.Distance(v, ev) < tolerance:
|
1923
|
+
edge0 = edge
|
1924
|
+
else:
|
1925
|
+
edge1 = edge
|
1926
|
+
ang = Edge.Angle(edge0, edge1)
|
1927
|
+
e1 = start_from(edge0, v)
|
1928
|
+
e2 = start_from(edge1, v)
|
1929
|
+
|
1930
|
+
dir1 = Edge.Direction(e1)
|
1931
|
+
dir2 = Edge.Direction(e2)
|
1932
|
+
if Vector.IsParallel(dir1, dir2) or Vector.IsAntiParallel(dir1, dir2):
|
1933
|
+
pass
|
1934
|
+
else:
|
1935
|
+
if isinstance(offsetKey, str):
|
1936
|
+
d = Topology.Dictionary(v)
|
1937
|
+
if Topology.IsInstance(d, "Dictionary"):
|
1938
|
+
v_offset = Dictionary.ValueAtKey(d, offsetKey)
|
1939
|
+
if isinstance(v_offset, float) or isinstance(v_offset, int):
|
1940
|
+
if v_offset >= 0:
|
1941
|
+
offset = v_offset
|
1942
|
+
if offset > 0 and offset <= Edge.Length(e1) and offset <=Edge.Length(e2):
|
1943
|
+
v1 = Topology.TranslateByDirectionDistance(v, dir1, offset)
|
1944
|
+
v2 = Topology.TranslateByDirectionDistance(v, dir2, offset)
|
1945
|
+
final_vertices += [v1,v2]
|
1946
|
+
else:
|
1947
|
+
print("Wire.Fillet - Warning: The input offset parameter is greater than the length of the edge. Skipping.")
|
1948
|
+
final_vertices.append(v)
|
1949
|
+
else:
|
1950
|
+
final_vertices.append(v)
|
1951
|
+
flat_wire = Wire.ByVertices(final_vertices, close=Wire.IsClosed(wire))
|
1952
|
+
# Unflatten the wire
|
1953
|
+
return_wire = Topology.Unflatten(flat_wire, origin=Vertex.Origin(), direction=normal)
|
1954
|
+
return return_wire
|
1955
|
+
|
1956
|
+
@staticmethod
|
1957
|
+
def Normal(wire, outputType="xyz", mantissa=6):
|
1958
|
+
"""
|
1959
|
+
Returns the normal vector to the input wire. A normal vector of a wire is a vector perpendicular to it.
|
1960
|
+
|
1961
|
+
Parameters
|
1962
|
+
----------
|
1963
|
+
wire : topologic_core.Wire
|
1964
|
+
The input wire.
|
1965
|
+
outputType : string , optional
|
1966
|
+
The string defining the desired output. This can be any subset or permutation of "xyz". It is case insensitive. The default is "xyz".
|
1967
|
+
mantissa : int , optional
|
1968
|
+
The desired length of the mantissa. The default is 6.
|
1969
|
+
|
1970
|
+
Returns
|
1971
|
+
-------
|
1972
|
+
list
|
1973
|
+
The normal vector to the input face.
|
1974
|
+
|
1975
|
+
"""
|
1976
|
+
from topologicpy.Topology import Topology
|
1977
|
+
from topologicpy.Vertex import Vertex
|
1978
|
+
import os
|
1979
|
+
import warnings
|
1980
|
+
try:
|
1981
|
+
import numpy as np
|
1982
|
+
except:
|
1983
|
+
print("Wire.Normal - Warning: Installing required numpy library.")
|
1984
|
+
try:
|
1985
|
+
os.system("pip install numpy")
|
1986
|
+
except:
|
1987
|
+
os.system("pip install numpy --user")
|
1988
|
+
try:
|
1989
|
+
import numpy as np
|
1990
|
+
print("Wire.Normal - Warning: numpy library installed correctly.")
|
1991
|
+
except:
|
1992
|
+
warnings.warn("Wire.Normal - Error: Could not import numpy. Please try to install numpy manually. Returning None.")
|
1993
|
+
return None
|
1994
|
+
|
1995
|
+
if not Topology.IsInstance(wire, "Wire"):
|
1996
|
+
print("Wire.Normal - Error: The input wire parameter is not a valid wire. Returning None.")
|
1997
|
+
return None
|
1998
|
+
|
1999
|
+
vertices = Topology.Vertices(wire)
|
2000
|
+
vertices = [Vertex.Coordinates(v, mantissa=mantissa) for v in vertices]
|
2001
|
+
|
2002
|
+
if len(vertices) < 3:
|
2003
|
+
print("Wire.Normal - Error: At least three vertices are required to define a plane. Returning None.")
|
2004
|
+
return None
|
2005
|
+
|
2006
|
+
# Convert vertices to numpy array for easier manipulation
|
2007
|
+
vertices = np.array(vertices)
|
2008
|
+
|
2009
|
+
# Try to find two non-collinear edge vectors
|
2010
|
+
vec1 = None
|
2011
|
+
vec2 = None
|
2012
|
+
for i in range(1, len(vertices)):
|
2013
|
+
for j in range(i + 1, len(vertices)):
|
2014
|
+
temp_vec1 = vertices[i] - vertices[0]
|
2015
|
+
temp_vec2 = vertices[j] - vertices[0]
|
2016
|
+
cross_product = np.cross(temp_vec1, temp_vec2)
|
2017
|
+
if np.linalg.norm(cross_product) > 1e-6: # Check if the cross product is not near zero
|
2018
|
+
vec1 = temp_vec1
|
2019
|
+
vec2 = temp_vec2
|
2020
|
+
break
|
2021
|
+
if vec1 is not None and vec2 is not None:
|
2022
|
+
break
|
2023
|
+
|
2024
|
+
if vec1 is None or vec2 is None:
|
2025
|
+
print("Wire.Normal - Error: The given vertices do not form a valid plane (all vertices might be collinear). Returning None.")
|
2026
|
+
return None
|
2027
|
+
|
2028
|
+
# Calculate the cross product of the two edge vectors
|
2029
|
+
normal = np.cross(vec1, vec2)
|
2030
|
+
|
2031
|
+
# Normalize the normal vector
|
2032
|
+
normal_length = np.linalg.norm(normal)
|
2033
|
+
if normal_length == 0:
|
2034
|
+
print("Wire.Normal - Error: The given vertices do not form a valid plane (cross product resulted in a zero vector). Returning None.")
|
2035
|
+
return None
|
2036
|
+
|
2037
|
+
normal = normal / normal_length
|
2038
|
+
normal = normal.tolist()
|
2039
|
+
normal = [round(x, mantissa) for x in normal]
|
2040
|
+
return_normal = []
|
2041
|
+
outputType = list(outputType.lower())
|
2042
|
+
for axis in outputType:
|
2043
|
+
if axis == "x":
|
2044
|
+
return_normal.append(normal[0])
|
2045
|
+
elif axis == "y":
|
2046
|
+
return_normal.append(normal[1])
|
2047
|
+
elif axis == "z":
|
2048
|
+
return_normal.append(normal[2])
|
2049
|
+
return return_normal
|
2050
|
+
|
1954
2051
|
@staticmethod
|
1955
2052
|
def OrientEdges(wire, vertexA, tolerance=0.0001):
|
1956
2053
|
"""
|
@@ -2104,7 +2201,7 @@ class Wire(Topology):
|
|
2104
2201
|
if not Topology.IsInstance(face, "Face"):
|
2105
2202
|
return None
|
2106
2203
|
if not direction:
|
2107
|
-
direction = -1*Face.
|
2204
|
+
direction = -1*Face.Normal(face, outputType="xyz", mantissa=mantissa)
|
2108
2205
|
large_face = Topology.Scale(face, face.CenterOfMass(), 500, 500, 500)
|
2109
2206
|
edges = []
|
2110
2207
|
_ = wire.Edges(None, edges)
|
@@ -2162,18 +2259,18 @@ class Wire(Topology):
|
|
2162
2259
|
if not Topology.IsInstance(origin, "Vertex"):
|
2163
2260
|
origin = Vertex.ByCoordinates(0, 0, 0)
|
2164
2261
|
if not Topology.IsInstance(origin, "Vertex"):
|
2165
|
-
print("Wire.Rectangle - Error: specified origin is not a topologic vertex.
|
2262
|
+
print("Wire.Rectangle - Error: specified origin is not a topologic vertex. Returning None.")
|
2166
2263
|
return None
|
2167
2264
|
if not placement.lower() in ["center", "lowerleft", "upperleft", "lowerright", "upperright"]:
|
2168
|
-
print("Wire.Rectangle - Error: Could not find placement in the list of placements.
|
2265
|
+
print("Wire.Rectangle - Error: Could not find placement in the list of placements. Returning None.")
|
2169
2266
|
return None
|
2170
2267
|
width = abs(width)
|
2171
2268
|
length = abs(length)
|
2172
2269
|
if width < tolerance or length < tolerance:
|
2173
|
-
print("Wire.Rectangle - Error: One or more of the specified dimensions is below the tolerance value.
|
2270
|
+
print("Wire.Rectangle - Error: One or more of the specified dimensions is below the tolerance value. Returning None.")
|
2174
2271
|
return None
|
2175
2272
|
if (abs(direction[0]) + abs(direction[1]) + abs(direction[2])) < tolerance:
|
2176
|
-
print("Wire.Rectangle - Error: The direction vector magnitude is below the tolerance value.
|
2273
|
+
print("Wire.Rectangle - Error: The direction vector magnitude is below the tolerance value. Returning None.")
|
2177
2274
|
return None
|
2178
2275
|
xOffset = 0
|
2179
2276
|
yOffset = 0
|
@@ -2233,9 +2330,9 @@ class Wire(Topology):
|
|
2233
2330
|
new_edges = []
|
2234
2331
|
for edge in edges:
|
2235
2332
|
sv = Edge.StartVertex(edge)
|
2236
|
-
sv = vertices[Vertex.Index(sv, vertices)]
|
2333
|
+
sv = vertices[Vertex.Index(sv, vertices, tolerance=tolerance)]
|
2237
2334
|
ev = Edge.EndVertex(edge)
|
2238
|
-
ev = vertices[Vertex.Index(ev, vertices)]
|
2335
|
+
ev = vertices[Vertex.Index(ev, vertices, tolerance=tolerance)]
|
2239
2336
|
if Vertex.Distance(sv, ev) > tolerance:
|
2240
2337
|
new_edges.append(Edge.ByVertices([sv,ev]))
|
2241
2338
|
new_wire = Topology.SelfMerge(Cluster.ByTopologies(new_edges), tolerance=tolerance)
|
@@ -2834,10 +2931,10 @@ class Wire(Topology):
|
|
2834
2931
|
if not Topology.IsInstance(origin, "Vertex"):
|
2835
2932
|
origin = Vertex.ByCoordinates(0, 0, 0)
|
2836
2933
|
if not Topology.IsInstance(origin, "Vertex"):
|
2837
|
-
print("Wire.Squircle - Error: The input origin parameter is not a valid Vertex.
|
2934
|
+
print("Wire.Squircle - Error: The input origin parameter is not a valid Vertex. Returning None.")
|
2838
2935
|
return None
|
2839
2936
|
if not placement.lower() in ["center", "lowerleft", "upperleft", "lowerright", "upperright"]:
|
2840
|
-
print("Wire.Squircle - Error: The input placement parameter is not a
|
2937
|
+
print("Wire.Squircle - Error: The input placement parameter is not a recognized string. Returning None.")
|
2841
2938
|
return None
|
2842
2939
|
radius = abs(radius)
|
2843
2940
|
if radius < tolerance:
|
topologicpy/version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = '0.7.
|
1
|
+
__version__ = '0.7.18'
|