@@ -2011,7 +2011,7 @@ def parametricSurface(
2011
2011
:param maxDeg: maximum spline degree (default: 3)
2012
2012
:param smoothing: optional parameters for the variational smoothing algorithm (default: (1,1,1))
2013
2013
:return: a Workplane object with the current point unchanged
2014
-
2014
+
2015
2015
This method might be unstable and may require tuning of the tol parameter.
2016
2016
2017
2017
"""
@@ -2978,7 +2978,7 @@ def twistExtrude(
2978
2978
2979
2979
def extrude (
2980
2980
self : T ,
2981
- distance : float ,
2981
+ until : Union [ float , Literal [ "next" , "last" ], Face ] ,
2982
2982
combine : bool = True ,
2983
2983
clean : bool = True ,
2984
2984
both : bool = False ,
@@ -2987,8 +2987,12 @@ def extrude(
2987
2987
"""
2988
2988
Use all un-extruded wires in the parent chain to create a prismatic solid.
2989
2989
2990
- :param distance: the distance to extrude, normal to the workplane plane
2991
- :type distance: float, negative means opposite the normal direction
2990
+ :param until: the distance to extrude, normal to the workplane plane
2991
+ :param until: The distance to extrude, normal to the workplane plane. When a float is
2992
+ passed, the extrusion extends this far and a negative value is in the opposite direction
2993
+ to the normal of the plane. The string "next" extrudes until the next face orthogonal to
2994
+ the wire normal. "last" extrudes to the last face. If a object of type Face is passed then
2995
+ the extrusion will extend until this face.
2992
2996
:param boolean combine: True to combine the resulting solid with parent solids if found.
2993
2997
:param boolean clean: call :py:meth:`clean` afterwards to have a clean shape
2994
2998
:param boolean both: extrude in both directions symmetrically
@@ -3000,18 +3004,35 @@ def extrude(
3000
3004
The returned object is always a CQ object, and depends on whether combine is True, and
3001
3005
whether a context solid is already defined:
3002
3006
3003
- * if combine is False, the new value is pushed onto the stack.
3007
+ * if combine is False, the new value is pushed onto the stack. Note that when extruding
3008
+ until a specified face, combine can not be False
3004
3009
* if combine is true, the value is combined with the context solid if it exists,
3005
3010
and the resulting solid becomes the new context solid.
3006
-
3007
- FutureEnhancement:
3008
- Support for non-prismatic extrusion ( IE, sweeping along a profile, not just
3009
- perpendicular to the plane extrude to surface. this is quite tricky since the surface
3010
- selected may not be planar
3011
3011
"""
3012
- r = self ._extrude (
3013
- distance , both = both , taper = taper
3014
- ) # returns a Solid (or a compound if there were multiple)
3012
+ # Handle `until` multiple values
3013
+ if isinstance (until , str ) and until in ("next" , "last" ) and combine :
3014
+ if until == "next" :
3015
+ faceIndex = 0
3016
+ elif until == "last" :
3017
+ faceIndex = - 1
3018
+
3019
+ r = self ._extrude (distance = None , both = both , taper = taper , upToFace = faceIndex )
3020
+
3021
+ elif isinstance (until , Face ) and combine :
3022
+ r = self ._extrude (None , both = both , taper = taper , upToFace = until )
3023
+
3024
+ elif isinstance (until , (int , float )):
3025
+ r = self ._extrude (until , both = both , taper = taper , upToFace = None )
3026
+
3027
+ elif isinstance (until , (str , Face )) and combine is False :
3028
+ raise ValueError (
3029
+ "`combine` can't be set to False when extruding until a face"
3030
+ )
3031
+
3032
+ else :
3033
+ raise ValueError (
3034
+ f"Do not know how to handle until argument of type { type (until )} "
3035
+ )
3015
3036
3016
3037
if combine :
3017
3038
newS = self ._combineWithBase (r )
@@ -3355,37 +3376,50 @@ def __and__(self: T, toUnion: Union["Workplane", Solid, Compound]) -> T:
3355
3376
3356
3377
def cutBlind (
3357
3378
self : T ,
3358
- distanceToCut : float ,
3379
+ until : Union [ float , Literal [ "next" , "last" ], Face ] ,
3359
3380
clean : bool = True ,
3360
3381
taper : Optional [float ] = None ,
3361
3382
) -> T :
3362
3383
"""
3363
3384
Use all un-extruded wires in the parent chain to create a prismatic cut from existing solid.
3385
+ You must define either :distance: , :untilNextFace: or :untilLastFace:
3364
3386
3365
3387
Similar to extrude, except that a solid in the parent chain is required to remove material
3366
3388
from. cutBlind always removes material from a part.
3367
3389
3368
- :param distanceToCut: distance to extrude before cutting
3369
- :type distanceToCut: float, >0 means in the positive direction of the workplane normal,
3370
- <0 means in the negative direction
3390
+ :param until: The distance to cut to, normal to the workplane plane. When a negative float
3391
+ is passed the cut extends this far in the opposite direction to the normal of the plane
3392
+ (i.e in the solid). The string "next" cuts until the next face orthogonal to the wire
3393
+ normal. "last" cuts to the last face. If a object of type Face is passed then the cut
3394
+ will extend until this face.
3371
3395
:param boolean clean: call :py:meth:`clean` afterwards to have a clean shape
3372
3396
:param float taper: angle for optional tapered extrusion
3373
3397
:raises ValueError: if there is no solid to subtract from in the chain
3374
3398
:return: a CQ object with the resulting object selected
3375
3399
3376
3400
see :py:meth:`cutThruAll` to cut material from the entire part
3377
-
3378
- Future Enhancements:
3379
- Cut Up to Surface
3380
3401
"""
3381
- # first, make the object
3382
- toCut = self ._extrude (distanceToCut , taper = taper )
3402
+ # Handling of `until` passed values
3403
+ s : Union [Compound , Solid , Shape ]
3404
+ if isinstance (until , str ) and until in ("next" , "last" ):
3405
+ if until == "next" :
3406
+ faceIndex = 0
3407
+ elif until == "last" :
3408
+ faceIndex = - 1
3383
3409
3384
- # now find a solid in the chain
3385
- solidRef = self .findSolid ()
3410
+ s = self ._extrude (None , taper = taper , upToFace = faceIndex , additive = False )
3386
3411
3387
- s = solidRef .cut (toCut )
3412
+ elif isinstance (until , Face ):
3413
+ s = self ._extrude (None , taper = taper , upToFace = until , additive = False )
3388
3414
3415
+ elif isinstance (until , (int , float )):
3416
+ toCut = self ._extrude (until , taper = taper , upToFace = None , additive = False )
3417
+ solidRef = self .findSolid ()
3418
+ s = solidRef .cut (toCut )
3419
+ else :
3420
+ raise ValueError (
3421
+ f"Do not know how to handle until argument of type { type (until )} "
3422
+ )
3389
3423
if clean :
3390
3424
s = s .clean ()
3391
3425
@@ -3441,48 +3475,130 @@ def loft(
3441
3475
return self .newObject ([r ])
3442
3476
3443
3477
def _extrude (
3444
- self , distance : float , both : bool = False , taper : Optional [float ] = None
3478
+ self ,
3479
+ distance : Optional [float ] = None ,
3480
+ both : bool = False ,
3481
+ taper : Optional [float ] = None ,
3482
+ upToFace : Optional [Union [int , Face ]] = None ,
3483
+ additive : bool = True ,
3445
3484
) -> Compound :
3446
3485
"""
3447
3486
Make a prismatic solid from the existing set of pending wires.
3448
3487
3449
3488
:param distance: distance to extrude
3450
- :param boolean both: extrude in both directions symmetrically
3489
+ :param boolean both: extrude in both directions symetrically
3490
+ :param upToFace: if specified extrude up to the :upToFace: face, 0 for the next, -1 for the last
3491
+ :param additive: specify if extruding or cutting, required param for uptoface algorithm
3492
+
3451
3493
:return: OCCT solid(s), suitable for boolean operations.
3452
3494
3453
3495
This method is a utility method, primarily for plugin and internal use.
3454
3496
It is the basis for cutBlind, extrude, cutThruAll, and all similar methods.
3455
3497
"""
3456
3498
3499
+ def getFacesList (eDir , direction , both = False ):
3500
+ """
3501
+ Utility function to make the code further below more clean and tidy
3502
+ Performs some test and raise appropriate error when no Faces are found for extrusion
3503
+ """
3504
+ facesList = self .findSolid ().facesIntersectedByLine (
3505
+ ws [0 ].Center (), eDir , direction = direction
3506
+ )
3507
+ if len (facesList ) == 0 and both :
3508
+ raise ValueError (
3509
+ "Couldn't find a face to extrude/cut to for at least one of the two required directions of extrusion/cut."
3510
+ )
3511
+
3512
+ if len (facesList ) == 0 :
3513
+ # if we don't find faces in the workplane normal direction we try the other
3514
+ # direction (as the user might have created a workplane with wrong orientation)
3515
+ facesList = self .findSolid ().facesIntersectedByLine (
3516
+ ws [0 ].Center (), eDir .multiply (- 1.0 ), direction = direction
3517
+ )
3518
+ if len (facesList ) == 0 :
3519
+ raise ValueError (
3520
+ "Couldn't find a face to extrude/cut to. Check your workplane orientation."
3521
+ )
3522
+ return facesList
3523
+
3457
3524
# group wires together into faces based on which ones are inside the others
3458
3525
# result is a list of lists
3459
3526
3460
3527
wireSets = sortWiresByBuildOrder (self .ctx .popPendingWires ())
3461
3528
3462
3529
# compute extrusion vector and extrude
3463
- eDir = self .plane .zDir .multiply (distance )
3530
+ if upToFace is not None :
3531
+ eDir = self .plane .zDir
3532
+ elif distance is not None :
3533
+ eDir = self .plane .zDir .multiply (distance )
3534
+
3535
+ if additive :
3536
+ direction = "AlongAxis"
3537
+ else :
3538
+ direction = "Opposite"
3464
3539
3465
3540
# one would think that fusing faces into a compound and then extruding would work,
3466
- # but it doesn't -- the resulting compound appears to look right, ( right number of faces, etc)
3541
+ # but it doesnt -- the resulting compound appears to look right, ( right number of faces, etc)
3467
3542
# but then cutting it from the main solid fails with BRep_NotDone.
3468
3543
# the work around is to extrude each and then join the resulting solids, which seems to work
3469
3544
3470
3545
# underlying cad kernel can only handle simple bosses-- we'll aggregate them if there are
3471
3546
# multiple sets
3547
+ thisObj : Union [Solid , Compound ]
3472
3548
3473
3549
toFuse = []
3550
+ taper = 0.0 if taper is None else taper
3551
+ baseSolid = None
3474
3552
3475
- if taper :
3476
- for ws in wireSets :
3477
- thisObj = Solid .extrudeLinear (ws [0 ], [], eDir , taper )
3553
+ for ws in wireSets :
3554
+ if upToFace is not None :
3555
+ baseSolid = self .findSolid () if baseSolid is None else thisObj
3556
+ if isinstance (upToFace , int ):
3557
+ facesList = getFacesList (eDir , direction , both = both )
3558
+ if (
3559
+ baseSolid .isInside (ws [0 ].Center ())
3560
+ and additive
3561
+ and upToFace == 0
3562
+ ):
3563
+ upToFace = 1 # extrude until next face outside the solid
3564
+
3565
+ limitFace = facesList [upToFace ]
3566
+ else :
3567
+ limitFace = upToFace
3568
+
3569
+ thisObj = Solid .dprism (
3570
+ baseSolid ,
3571
+ Face .makeFromWires (ws [0 ]),
3572
+ ws ,
3573
+ taper = taper ,
3574
+ upToFace = limitFace ,
3575
+ additive = additive ,
3576
+ )
3577
+
3578
+ if both :
3579
+ facesList2 = getFacesList (eDir .multiply (- 1.0 ), direction , both = both )
3580
+ limitFace2 = facesList2 [upToFace ]
3581
+ thisObj2 = Solid .dprism (
3582
+ self .findSolid (),
3583
+ Face .makeFromWires (ws [0 ]),
3584
+ ws ,
3585
+ taper = taper ,
3586
+ upToFace = limitFace2 ,
3587
+ additive = additive ,
3588
+ )
3589
+ thisObj = Compound .makeCompound ([thisObj , thisObj2 ])
3590
+ toFuse = [thisObj ]
3591
+ elif taper != 0.0 :
3592
+ thisObj = Solid .extrudeLinear (ws [0 ], [], eDir , taper = taper )
3478
3593
toFuse .append (thisObj )
3479
- else :
3480
- for ws in wireSets :
3481
- thisObj = Solid .extrudeLinear (ws [0 ], ws [1 :], eDir )
3594
+ else :
3595
+ thisObj = Solid .extrudeLinear (ws [0 ], ws [1 :], eDir , taper = taper )
3482
3596
toFuse .append (thisObj )
3483
3597
3484
3598
if both :
3485
- thisObj = Solid .extrudeLinear (ws [0 ], ws [1 :], eDir .multiply (- 1.0 ))
3599
+ thisObj = Solid .extrudeLinear (
3600
+ ws [0 ], ws [1 :], eDir .multiply (- 1.0 ), taper = taper
3601
+ )
3486
3602
toFuse .append (thisObj )
3487
3603
3488
3604
return Compound .makeCompound (toFuse )
0 commit comments