Why can't I fillet or chamfer the bottom of this shape?

580 views
Skip to first unread message

Dusty Gamble

unread,
Jan 9, 2022, 11:57:52 PM1/9/22
to CadQuery
result = (
    cq.Workplane("XY")
    .circle(id/2)
    .circle(od/2)
        .extrude(h)
    .pushPoints(cls.motor.hole_pos)
        .circle(bolt_od/2)
    .pushPoints(cls.motor.hole_pos)
        .circle(bolt_id/2)
    .extrude(h)

    # this does not work, but '>Z' works
    .faces('|Z').chamfer(.5) 
)

Also, how would I troubleshoot this?  The generic error message "StdFail_NotDone: Brep_API: command not done" does not seem helpful.

Dusty Gamble

unread,
Jan 10, 2022, 12:04:12 AM1/10/22
to CadQuery
Sorry, the code above wasn't quite extracted properly.  This should work:

id = 22.35
od = id + 8

h = 7
bolt_od = 8
bolt_id = 4
arm_w = 8
arm_l = 60  # from center
hole_pos = ((17.5,0), (-17.5,0))

result = ( cq.Workplane("XY")
    .circle(id/2)
    .circle(od/2)
        .extrude(h)
    .pushPoints(hole_pos)
        .circle(bolt_od/2)
    .pushPoints(hole_pos)
        .circle(bolt_id/2)
    .extrude(h)
    .faces('|Z').chamfer(.5)  # this fails, but '>Z' works
)

Adam Urbanczyk

unread,
Jan 10, 2022, 11:31:56 AM1/10/22
to CadQuery
Looks to me like an issue with https://dev.opencascade.org/doc/occt-7.4.0/refman/html/class_shape_upgrade___unify_same_domain.html . You could constuct the object using sketches (NB: I added extra fillet for fun):

id = 22.35
od = id + 8

h = 7
bolt_od = 8
bolt_id = 4
arm_w = 8
arm_l = 60  # from center
hole_pos = ((17.5,0), (-17.5,0))

s = ( cq.Sketch()
    .circle(od/2)
    .circle(id/2, mode='s')
    .push(hole_pos)
    .circle(bolt_od/2)
    .circle(bolt_id/2, mode='s')
    .clean()
    .reset()
    .vertices('>Y or <Y')
    .fillet(1)
)


result = cq.Workplane().placeSketch(s).extrude(h).faces('|Z').chamfer(0.5)
show_object(result)

Dusty Gamble

unread,
Jan 11, 2022, 10:03:44 AM1/11/22
to CadQuery
That fixed it.  That got me started learning about sketch, too.  Thanks.

Roger Maitland

unread,
Jan 11, 2022, 11:00:46 AM1/11/22
to CadQuery
Adam's solution can be further optimized by using cq_warehouse.fastener to put the countersunk bolt holes into your part as follows:
import cadquery as cq
from cq_warehouse.fastener import CounterSunkScrew

screw = CounterSunkScrew(size="M4-0.7", fastener_type="iso2009", length=10)

id = 22.35
od = id + 8
h = 7
bolt_od = 8
# bolt_id = 4
arm_w = 8
arm_l = 60 # from center
hole_pos = ((17.5, 0), (-17.5, 0))

s = (
cq.Sketch()
.circle(od / 2)
.circle(id / 2, mode="s")
.push(hole_pos)
.circle(bolt_od / 2)
# .circle(bolt_id / 2, mode="s")
.clean()
.reset()
.vertices(">Y or <Y")
.fillet(1)
)

result = (
cq.Workplane()
.placeSketch(s)
.extrude(h)
.faces("|Z")
.chamfer(0.5)
.faces(">Z")
.workplane()
.pushPoints(hole_pos)
.clearanceHole(fastener=screw)
)
show_object(result)
which results in:
image.png
Cheers,
Roger

--
cadquery home: https://github.com/CadQuery/cadquery
post issues at https://github.com/CadQuery/cadquery/issues
run it at home at : https://github.com/CadQuery/CQ-editor
---
You received this message because you are subscribed to the Google Groups "CadQuery" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cadquery+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/cadquery/5cd0bd67-80b5-4622-957e-ffd7233e9e44n%40googlegroups.com.

Dave Cowden

unread,
Jan 11, 2022, 12:16:09 PM1/11/22
to Roger Maitland, CadQuery
thanks Roger!

I feel obligated to point out that the below output is a great example of what CQ is supposed to be all about.  Code that's as close as possible to how a human would describe the object, and the associated intent.

a human might describe this object as "a ring with arms on each side, for countersunk holes on each side for a M4-07 screw, and with the top surface fileted"

Of course the code below has a lot more details, but I think all in all it does a very good job of achieving the result with as little 'fuss' as possible. 

Two observations about ways we could get closer to the ideal:

(1) in this particular case, a 'ring' object, having an id,od, and height would reduce the verbosity, but i dont think that's common enough that its worth adding

(2) .clean() .reset() makes me sad. i know they are needed, and i know i'm probably even to blame for needing them, but they stick out to me as 'the framework is making me do this'. somehow getting rid of those would be nice.



Dusty Gamble

unread,
Jan 11, 2022, 1:04:18 PM1/11/22
to CadQuery
Thank you both!  Regarding cq_warehouse's clearanceHoles, I wondered if it would work for something I commonly do.  Since I use resin printing, FDM, and CNC stuff, a clearance hole might have many different sizes.  It might also expand to the size it'd need to be to hold a heat-inserted nut.  So, in my personal library (of SolidPython code), I've always stored many values on each thing, like this:
    class Bolt:
        def draw(self, ):

    class M3Bolt(Bolt):
        od = 3  # nominal
        od_fit_bolt_3dp_pla = 3.3  # TESTED
        od_fit_bolt_cnc = 3.3  # UNTESTED
        od_fit_bolt_3dp_resin = 3.2  # TESTED
        def __init__(self, l=6):
             self.l = l
   

Regarding clean and reset, I have a rather wild proposal: use indentation.  I indent my code like this anyway, because it helps me keep ideas sorted out.  I've noticed that .clean() and .reset() always come just before the next re-indentation.  I know that this is kind of an extreme approach, but look at this code - each .reset() and .clean() could be removed:

def teardrop2d(d=18, w=None, fillet=None, l=None, gap=.5):
    w = w or d/4
    fillet = fillet or d/3
    l = d/2
    return (
        cq.Sketch()
        .circle(d/2)
        .push([(0, d/2)])
            .rect(w, l*2)
            .reset()
            .clean()
        .vertices('not(>Y)').fillet(fillet/2)
            .clean()
        .push([(0, d+gap)])
            .rect(d, d, mode='s')
            .clean()
            .reset()

    )
result = (
    cq.Workplane()
        .placeSketch(teardrop2d()).extrude(h)
            # .faces('|Z').chamfer(0.5)
)

Alternatively, I was considering, for my own use, making a plugin that would call both with .cr().  Conceptually, this maps with the idea of a "carriage return" - it's somewhat like the equivalent of a newline for a sketch.

Really, I think I would like to create a DSL.  Get rid of all of the dots and parenthesis entirely, and replace them with newlines and indents.  Get rid of the outer array parens on push.  etc.  Look how clean it could be.  I've seen tools for making DSL's and wanted to try them before - no idea how difficult it is in practice.  Would probably be nice/more achievable as a short term goal if you could drop back into python by starting a line with a special character - maybe just starting a line with a dot, which would be part of the python code anyway.

Dave Cowden

unread,
Jan 11, 2022, 1:10:14 PM1/11/22
to Dusty Gamble, CadQuery
is there a reason clean() and reset() can't be done automatically by CQ somewhere? Or a way to eliminate the need to use them?

Adam Urbanczyk

unread,
Jan 12, 2022, 2:33:20 AM1/12/22
to CadQuery
I *really* disagree on your observation regarding .clean() and reset(). They do not exist in the Workplane API and because of that is much more magical/implicit.

Adam Urbanczyk

unread,
Jan 12, 2022, 2:38:07 AM1/12/22
to CadQuery
The semantics are as follows: reset() resets current selection and clean calls ShapeUpgrade_UnifySameDomain. The former you need to know when to want to use - it affects what will be selected with the next selection statement. The latter only makes sense when you wan to get rid of extra edges after a union. You definitely are calling it too many times in your code above.
Reply all
Reply to author
Forward
0 new messages