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/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
- miter : bool , optional
351
- if set to True, the corners will be mitered. The default is False.
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
- from random import randrange, sample
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 not miterThreshold:
383
- miterThreshold = offset*math.sqrt(2)
384
- flatFace = Face.ByWire(wire, tolerance=tolerance)
385
- origin = Topology.Centroid(flatFace)
386
- normal = Face.Normal(flatFace)
387
- flatFace = Topology.Flatten(flatFace, origin=origin, direction=normal)
388
-
389
- edges = Wire.Edges(wire)
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
- newVertices.append(Edge.StartVertex(newEdges[0]))
441
-
442
- for i in range(len(newEdges)-1):
443
- e1 = newEdges[i]
444
- e2 = newEdges[i+1]
445
- intV = Topology.Intersect(e1,e2)
446
- if intV:
447
- newVertices.append(intV)
448
- dupVertices.append(vertices[i+1])
449
- elif step:
450
- newVertices.append(Edge.EndVertex(e1))
451
- newVertices.append(Edge.StartVertex(e2))
452
- dupVertices.append(vertices[i+1])
453
- dupVertices.append(vertices[i+1])
454
- else:
455
- tempEdge1 = Edge.ByVertices([Edge.StartVertex(e1), Edge.EndVertex(e2)], tolerance=tolerance, silent=True)
456
- normal = Edge.Normal(e1)
457
- normal = [normal[0]*finalOffset*10, normal[1]*finalOffset*10, normal[2]*finalOffset*10]
458
- tempV = Vertex.ByCoordinates(vertices[i+1].X()+normal[0], vertices[i+1].Y()+normal[1], vertices[i+1].Z()+normal[2])
459
- tempEdge2 = Edge.ByVertices([vertices[i+1], tempV], tolerance=tolerance, silent=True)
460
- intV = Topology.Intersect(tempEdge1,tempEdge2)
461
- newVertices.append(intV)
462
- dupVertices.append(vertices[i+1])
463
-
464
- vertices = dupVertices
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
- newVertices.append(Edge.EndVertex(newEdges[-1]))
467
- newWire = Wire.ByVertices(newVertices, close=Wire.IsClosed(wire))
468
-
469
- newVertices = Wire.Vertices(newWire)
470
- newEdges = Wire.Edges(newWire)
471
- miterEdges = []
472
- cleanMiterEdges = []
473
- # Handle miter
474
- if miter:
475
- for i in range(len(newVertices)):
476
- if miterThresholdKey:
477
- d = Topology.Dictionary(vertices[i])
478
- value = Dictionary.ValueAtKey(d, key=miterThresholdKey)
479
- if value:
480
- finalMiterThreshold = value
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. Retruning None.")
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 recognised string. Retruning None.")
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.cos(angle)*radius + origin.X()
675
- y = math.sin(angle)*radius + origin.Y()
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 physist). See https://arxiv.org/abs/2303.10798
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 wich the ellipse is defined. The default is 1.
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 wich the ellipse is defined. The default is 1.
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 continous path)
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
- normalisedLengths = []
1732
+ normalizedLengths = []
1839
1733
  for aLength in lengths:
1840
- normalisedLengths.append(aLength/minLength)
1841
- return [x for x in itertools.chain(*itertools.zip_longest(normalisedLengths, angles)) if x is not None]
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.NormalAtParameters(face, 0.5, 0.5, "XYZ", mantissa)
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. Retruning None.")
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. Retruning None.")
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. Retruning None.")
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. Retruning None.")
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. Retruning None.")
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 recognised string. Retruning None.")
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.15'
1
+ __version__ = '0.7.18'