33
33
List ,
34
34
cast ,
35
35
Dict ,
36
+ Iterator ,
36
37
)
37
38
from typing_extensions import Literal
38
- from inspect import Parameter , Signature , isbuiltin
39
+ from inspect import Parameter , Signature
39
40
40
41
41
42
from .occ_impl .geom import Vector , Plane , Location
51
52
)
52
53
53
54
from .occ_impl .exporters .svg import getSVG , exportSVG
55
+ from .occ_impl .exporters import export
54
56
55
- from .utils import deprecate , deprecate_kwarg_name
57
+ from .utils import deprecate , deprecate_kwarg_name , get_arity
56
58
57
59
from .selectors import (
58
60
Selector ,
@@ -270,7 +272,7 @@ def split(self: T, keepTop: bool = False, keepBottom: bool = False) -> T:
270
272
...
271
273
272
274
@overload
273
- def split (self : T , splitter : Union [T , Shape ]) -> T :
275
+ def split (self : T , splitter : Union ["Workplane" , Shape ]) -> T :
274
276
...
275
277
276
278
def split (self : T , * args , ** kwargs ) -> T :
@@ -383,9 +385,7 @@ def combineSolids(
383
385
raise ValueError ("Cannot Combine: at least one solid required!" )
384
386
385
387
# get context solid and we don't want to find our own objects
386
- ctxSolid = self ._findType (
387
- (Solid , Compound ), searchStack = False , searchParents = True
388
- )
388
+ ctxSolid = self ._findType ((Solid ,), searchStack = False , searchParents = True )
389
389
if ctxSolid is None :
390
390
ctxSolid = toCombine .pop (0 )
391
391
@@ -748,8 +748,20 @@ def end(self, n: int = 1) -> "Workplane":
748
748
def _findType (self , types , searchStack = True , searchParents = True ):
749
749
750
750
if searchStack :
751
- rv = [s for s in self .objects if isinstance (s , types )]
752
- if rv and types == (Solid , Compound ):
751
+ rv = []
752
+ for obj in self .objects :
753
+ if isinstance (obj , types ):
754
+ rv .append (obj )
755
+ # unpack compounds in a special way when looking for Solids
756
+ elif isinstance (obj , Compound ) and types == (Solid ,):
757
+ for T in types :
758
+ # _entities(...) needed due to weird behavior with shelled object unpacking
759
+ rv .extend (T (el ) for el in obj ._entities (T .__name__ ))
760
+ # otherwise unpack compounds normally
761
+ elif isinstance (obj , Compound ):
762
+ rv .extend (el for el in obj if isinstance (el , type ))
763
+
764
+ if rv and types == (Solid ,):
753
765
return Compound .makeCompound (rv )
754
766
elif rv :
755
767
return rv [0 ]
@@ -781,7 +793,7 @@ def findSolid(
781
793
results with an object already on the stack.
782
794
"""
783
795
784
- found = self ._findType ((Solid , Compound ), searchStack , searchParents )
796
+ found = self ._findType ((Solid ,), searchStack , searchParents )
785
797
786
798
if found is None :
787
799
message = "on the stack or " if searchStack else ""
@@ -802,7 +814,7 @@ def findFace(self, searchStack: bool = True, searchParents: bool = True) -> Face
802
814
:returns: A face or None if no face is found.
803
815
"""
804
816
805
- found = self ._findType (Face , searchStack , searchParents )
817
+ found = self ._findType (( Face ,) , searchStack , searchParents )
806
818
807
819
if found is None :
808
820
message = "on the stack or " if searchStack else ""
@@ -1460,8 +1472,8 @@ def rarray(
1460
1472
If you want to position the array at another point, create another workplane
1461
1473
that is shifted to the position you would like to use as a reference
1462
1474
1463
- :param xSpacing: spacing between points in the x direction ( must be > 0)
1464
- :param ySpacing: spacing between points in the y direction ( must be > 0)
1475
+ :param xSpacing: spacing between points in the x direction ( must be >= 0)
1476
+ :param ySpacing: spacing between points in the y direction ( must be >= 0)
1465
1477
:param xCount: number of points ( > 0 )
1466
1478
:param yCount: number of points ( > 0 )
1467
1479
:param center: If True, the array will be centered around the workplane center.
@@ -1470,8 +1482,8 @@ def rarray(
1470
1482
centering along each axis.
1471
1483
"""
1472
1484
1473
- if xSpacing <= 0 or ySpacing <= 0 or xCount < 1 or yCount < 1 :
1474
- raise ValueError ("Spacing and count must be > 0 " )
1485
+ if ( xSpacing <= 0 and ySpacing <= 0 ) or xCount < 1 or yCount < 1 :
1486
+ raise ValueError ("Spacing and count must be > 0 in at least one direction " )
1475
1487
1476
1488
if isinstance (center , bool ):
1477
1489
center = (center , center )
@@ -3310,9 +3322,7 @@ def _fuseWithBase(self: T, obj: Shape) -> T:
3310
3322
:return: a new object that represents the result of combining the base object with obj,
3311
3323
or obj if one could not be found
3312
3324
"""
3313
- baseSolid = self ._findType (
3314
- (Solid , Compound ), searchStack = True , searchParents = True
3315
- )
3325
+ baseSolid = self ._findType ((Solid ,), searchStack = True , searchParents = True )
3316
3326
r = obj
3317
3327
if baseSolid is not None :
3318
3328
r = baseSolid .fuse (obj )
@@ -3328,7 +3338,7 @@ def _cutFromBase(self: T, obj: Shape) -> T:
3328
3338
:return: a new object that represents the result of combining the base object with obj,
3329
3339
or obj if one could not be found
3330
3340
"""
3331
- baseSolid = self ._findType ((Solid , Compound ), True , True )
3341
+ baseSolid = self ._findType ((Solid ,), True , True )
3332
3342
3333
3343
r = obj
3334
3344
if baseSolid is not None :
@@ -3399,9 +3409,7 @@ def union(
3399
3409
3400
3410
# now combine with existing solid, if there is one
3401
3411
# look for parents to cut from
3402
- solidRef = self ._findType (
3403
- (Solid , Compound ), searchStack = True , searchParents = True
3404
- )
3412
+ solidRef = self ._findType ((Solid ,), searchStack = True , searchParents = True )
3405
3413
if solidRef is not None :
3406
3414
r = solidRef .fuse (* newS , glue = glue , tol = tol )
3407
3415
elif len (newS ) > 1 :
@@ -3414,7 +3422,8 @@ def union(
3414
3422
3415
3423
return self .newObject ([r ])
3416
3424
3417
- def __or__ (self : T , toUnion : Union ["Workplane" , Solid , Compound ]) -> T :
3425
+ @deprecate ()
3426
+ def __or__ (self : T , other : Union ["Workplane" , Solid , Compound ]) -> T :
3418
3427
"""
3419
3428
Syntactic sugar for union.
3420
3429
@@ -3426,15 +3435,15 @@ def __or__(self: T, toUnion: Union["Workplane", Solid, Compound]) -> T:
3426
3435
Sphere = Workplane("XY").sphere(1)
3427
3436
result = Box | Sphere
3428
3437
"""
3429
- return self .union (toUnion )
3438
+ return self .union (other )
3430
3439
3431
- def __add__ (self : T , toUnion : Union ["Workplane" , Solid , Compound ]) -> T :
3440
+ def __add__ (self : T , other : Union ["Workplane" , Solid , Compound ]) -> T :
3432
3441
"""
3433
3442
Syntactic sugar for union.
3434
3443
3435
3444
Notice that :code:`r = a + b` is equivalent to :code:`r = a.union(b)` and :code:`r = a | b`.
3436
3445
"""
3437
- return self .union (toUnion )
3446
+ return self .union (other )
3438
3447
3439
3448
def cut (
3440
3449
self : T ,
@@ -3472,7 +3481,7 @@ def cut(
3472
3481
3473
3482
return self .newObject ([newS ])
3474
3483
3475
- def __sub__ (self : T , toUnion : Union ["Workplane" , Solid , Compound ]) -> T :
3484
+ def __sub__ (self : T , other : Union ["Workplane" , Solid , Compound ]) -> T :
3476
3485
"""
3477
3486
Syntactic sugar for cut.
3478
3487
@@ -3484,7 +3493,7 @@ def __sub__(self: T, toUnion: Union["Workplane", Solid, Compound]) -> T:
3484
3493
Sphere = Workplane("XY").sphere(1)
3485
3494
result = Box - Sphere
3486
3495
"""
3487
- return self .cut (toUnion )
3496
+ return self .cut (other )
3488
3497
3489
3498
def intersect (
3490
3499
self : T ,
@@ -3522,7 +3531,8 @@ def intersect(
3522
3531
3523
3532
return self .newObject ([newS ])
3524
3533
3525
- def __and__ (self : T , toUnion : Union ["Workplane" , Solid , Compound ]) -> T :
3534
+ @deprecate ()
3535
+ def __and__ (self : T , other : Union ["Workplane" , Solid , Compound ]) -> T :
3526
3536
"""
3527
3537
Syntactic sugar for intersect.
3528
3538
@@ -3534,7 +3544,38 @@ def __and__(self: T, toUnion: Union["Workplane", Solid, Compound]) -> T:
3534
3544
Sphere = Workplane("XY").sphere(1)
3535
3545
result = Box & Sphere
3536
3546
"""
3537
- return self .intersect (toUnion )
3547
+
3548
+ return self .intersect (other )
3549
+
3550
+ def __mul__ (self : T , other : Union ["Workplane" , Solid , Compound ]) -> T :
3551
+ """
3552
+ Syntactic sugar for intersect.
3553
+
3554
+ Notice that :code:`r = a * b` is equivalent to :code:`r = a.intersect(b)`.
3555
+
3556
+ Example::
3557
+
3558
+ Box = Workplane("XY").box(1, 1, 1, centered=(False, False, False))
3559
+ Sphere = Workplane("XY").sphere(1)
3560
+ result = Box * Sphere
3561
+ """
3562
+
3563
+ return self .intersect (other )
3564
+
3565
+ def __truediv__ (self : T , other : Union ["Workplane" , Solid , Compound ]) -> T :
3566
+ """
3567
+ Syntactic sugar for intersect.
3568
+
3569
+ Notice that :code:`r = a / b` is equivalent to :code:`r = a.split(b)`.
3570
+
3571
+ Example::
3572
+
3573
+ Box = Workplane("XY").box(1, 1, 1, centered=(False, False, False))
3574
+ Sphere = Workplane("XY").sphere(1)
3575
+ result = Box / Sphere
3576
+ """
3577
+
3578
+ return self .split (other )
3538
3579
3539
3580
def cutBlind (
3540
3581
self : T ,
@@ -3680,6 +3721,10 @@ def _getFaces(self) -> List[Face]:
3680
3721
for el in self .objects :
3681
3722
if isinstance (el , Sketch ):
3682
3723
rv .extend (el )
3724
+ elif isinstance (el , Face ):
3725
+ rv .append (el )
3726
+ elif isinstance (el , Compound ):
3727
+ rv .extend (subel for subel in el if isinstance (subel , Face ))
3683
3728
3684
3729
if not rv :
3685
3730
rv .extend (wiresToFaces (self .ctx .popPendingWires ()))
@@ -4441,6 +4486,19 @@ def __getitem__(self: T, item: Union[int, Sequence[int], slice]) -> T:
4441
4486
4442
4487
return rv
4443
4488
4489
+ def __iter__ (self : T ) -> Iterator [Shape ]:
4490
+ """
4491
+ Special method for iterating over Shapes in objects
4492
+ """
4493
+
4494
+ for el in self .objects :
4495
+ if isinstance (el , Compound ):
4496
+ yield from el
4497
+ elif isinstance (el , Shape ):
4498
+ yield el
4499
+ elif isinstance (el , Sketch ):
4500
+ yield from el
4501
+
4444
4502
def filter (self : T , f : Callable [[CQObject ], bool ]) -> T :
4445
4503
"""
4446
4504
Filter items using a boolean predicate.
@@ -4488,11 +4546,7 @@ def invoke(
4488
4546
:return: Workplane object.
4489
4547
"""
4490
4548
4491
- if isbuiltin (f ):
4492
- arity = 0 # assume 0 arity for builtins; they cannot be introspected
4493
- else :
4494
- arity = f .__code__ .co_argcount # NB: this is not understood by mypy
4495
-
4549
+ arity = get_arity (f )
4496
4550
rv = self
4497
4551
4498
4552
if arity == 0 :
@@ -4506,6 +4560,29 @@ def invoke(
4506
4560
4507
4561
return rv
4508
4562
4563
+ def export (
4564
+ self : T ,
4565
+ fname : str ,
4566
+ tolerance : float = 0.1 ,
4567
+ angularTolerance : float = 0.1 ,
4568
+ opt : Optional [Dict [str , Any ]] = None ,
4569
+ ) -> T :
4570
+ """
4571
+ Export Workplane to file.
4572
+
4573
+ :param path: Filename.
4574
+ :param tolerance: the deflection tolerance, in model units. Default 0.1.
4575
+ :param angularTolerance: the angular tolerance, in radians. Default 0.1.
4576
+ :param opt: additional options passed to the specific exporter. Default None.
4577
+ :return: Self.
4578
+ """
4579
+
4580
+ export (
4581
+ self , fname , tolerance = tolerance , angularTolerance = angularTolerance , opt = opt
4582
+ )
4583
+
4584
+ return self
4585
+
4509
4586
4510
4587
# alias for backward compatibility
4511
4588
CQ = Workplane
0 commit comments