CadQuery plugin involving extrusion and cuts

421 views
Skip to first unread message

John Beard

unread,
Aug 22, 2018, 6:36:54 AM8/22/18
to CadQuery
Hi,

I have a simple question about making plugins that involve both extrusions
and cuts.

Imagine you want a plugin for a "bossed" hole: drill a hole in a part, and
add a circular "boss" around the hole.

I currently have the following:

from __future__ import division

import cadquery as cq

def bossedHole(self, diameter, bossDia,

depth=None, nutAngle=0, clean=True):

if depth is None:

depth = self.largestDimension()

def _makeHole(center):

# center is in local coordinates

boreDir = cq.Vector(0, 0, -1)

# first make the hole # local coords!

hole = cq.Solid.makeCylinder(diameter/2.0, depth, center, boreDir)

return hole

def _makeBoss(center):

# center is in local coordinates

boreDir = cq.Vector(0, 0, -1)

# make the boss # local coords!

boss = cq.Solid.makeCylinder(bossDia/2.0, depth, center, boreDir)

return boss

r = self.eachpoint(_makeBoss, True).combineSolids()

r = self.cutEach(_makeHole, True, clean)

return r

# link the plugin into cadQuery

cq.Workplane.bossedHole = bossedHole

result = cq.Workplane("XY").box(10, 10, 2)

result = result.faces("<Z").workplane() \

.rect(5, 5).vertices() \

.bossedHole(2, 4, 4)

show_object(result, options={"rgba":(204, 204, 204, 0.0)})

The basic gist is:

- For each point, from the bottom side:
- Extrude the boss right though the part from the bottom to the
desired depth
- Drill the hole from the bottom to the same depth

This is actually what I wanted in this case, but it occurred to me that I
don't know how to do the following operation from the top face:

- For each point, from the top side:
- Extrude the boss upwards by "z" mm
- Drill a hole down though the boss *and* the part to a certain depth

Is that possible within a single plugin?


Cheers,


John

2018-08-22_113614_659x417_screenshot.png

Simon Kirkby

unread,
Aug 25, 2018, 12:01:44 AM8/25/18
to CadQuery

Hey John, 


Here is your object using cqparts 

" A cqparts version of the beard boss" 

import cadquery as cq
import cqparts
from cqparts.params import *

class _boss(cqparts.Part):
    boss_height = PositiveFloat(10)
    boss_diameter = PositiveFloat(10)
    def make(self):
        wp = cq.Workplane("XY").circle(self.boss_diameter/2).extrude(self.boss_height)
        return wp

class BeardBoss(cqparts.Part):
    # Base Plate
    length = PositiveFloat(50)
    width = PositiveFloat(50)
    height = PositiveFloat(6)
    # Boss size
    boss_height = PositiveFloat(10)
    boss_diameter = PositiveFloat(15)
    # Drill Size
    hole_diameter = PositiveFloat(8)
    # Boss spacing
    x_spacing = PositiveFloat(30)
    y_spacing = PositiveFloat(30)

    # hand back vertices for the boss positions
    def mount_points(self,offset=0):
        wp = cq.Workplane("XY",origin=(0,0,offset))
        h = wp.rect(self.x_spacing
                    ,self.y_spacing
                    ,forConstruction=True).vertices()
        return h.objects

    def make(self):
        # base plate
        pl  = cq.Workplane("XY").box(self.length,self.width,self.height)
        pl = pl.translate((0,0,self.height/2))
        # add the bosses
        mp = self.mount_points()
        for i in mp:
            b = _boss(
                    boss_height=self.boss_height,
                    boss_diameter=self.boss_diameter
                    )
            b = b.local_obj.translate((i.X,i.Y,self.height))
            pl = pl.union(b)
        # cur the holes
        for i in mp:
            h  = cq.Workplane("XY").circle(self.hole_diameter/2).extrude(self.height+self.boss_height)
            h = h.translate((i.X,i.Y,0))
            pl = pl.cut(h)
        pl = pl.faces(">Z[1]").edges("not(<X or >X or <Y or >Y)").fillet(1)
        pl = pl.edges("|Z").fillet(3)
        return pl 

if __name__ == "__main__":
    from cqparts.display import display
    bb = BeardBoss()
    display(bb)

It's in my cqparts bucket


you can make an altered version by passing variables into the contstructor

bb = BeardBoss(height=30,boss_height=100)

 Simon

BeardBoss.png

Adam Urbanczyk

unread,
Aug 26, 2018, 5:24:07 AM8/26/18
to CadQuery
Hi John,

would this be an acceptable answer:

import cadquery as cq

def make_holes_with_collars(self, fi,w,h,d):
   
    depth
= d-h
   
   
def _hole(pnt):

        boreDir
= cq.Vector(0, 0, -1)

        hole
= cq.Solid.makeCylinder(fi, depth, pnt, boreDir)  # local coordinates!
       
       
return hole
   
   
def _collar(pnt):
        extDir
= cq.Vector(0, 0, h)
        w1
= cq.Wire.makeCircle(fi,pnt,extDir)
        w2
= cq.Wire.makeCircle(fi+w,pnt,extDir)
       
       
return cq.Solid.extrudeLinear(w2,[w1],extDir)
       
   
base = self.cutEach(_hole,True)
    collars
= self.eachpoint(_collar,True)
   
   
return base.union(collars)

cq
.Workplane.make_holes_with_collars = make_holes_with_collars

result
= cq.Workplane("XY" ).box(5,5,3).edges("|Z").fillet(0.125).\
    faces
('>Z').workplane().rect(3,3,forConstruction=True).vertices().\
    make_holes_with_collars
(.5,.2,.7,2)
   
show_object
(result)



Best,
Adam

John Beard

unread,
Sep 4, 2018, 6:37:29 AM9/4/18
to CadQuery
Hi Adam,

That sort of works, but it doesn't work where the substrate isn't solid (say you add faces('>Z').workplane().hole(4) just before your line 30, then you get "floating" collars, when I really wanted them to extend right through (I see now I didn't say that clearly!). Of course, you can extrude the collars in +Z and -Z easily enough, so you can do it easily enough with a small tweak to your example.

I guess what I'm really asking is when you're in a plugin environment, can you "jump" the workplane to a new position? So say the question was instead:
  • 10mm cube with a pre-existing hole in it parallel to Y, 8mm diameter
  • On the top face, a set of vertices
  • In a single plugin, for each vertex:
  • A circular extrusion of 5mm diameter, extending downwards to the bottom, and upwards 3mm (this will refill part of the existing Y hole)
  • A circular dimple in the top of that 13mm column of 1mm depth (i.e. you can't reach it by cutting upwards from the cube face)
The problem I have is getting to the top of that column to do the dimple within the plugin. The circularity of the dimple and shape of the boss aren't really important, they're just examples.

Thanks,

John

Jeremy Wright

unread,
Sep 12, 2018, 8:18:37 AM9/12/18
to CadQuery
@Dave - Any thoughts on this? I've never really worked with CadQuery plugins.

thebluedirt

unread,
Sep 12, 2018, 7:18:05 PM9/12/18
to CadQuery
Hi, John:

This is an interesting question. Let me start by saying I never contemplated this notion when designing how plugins work, so whatever's possible/not possible isn't on purpose ;)

Now, to the question-- the CQ plugin strategy is really just monkey patching the CQ class itself.

CQ and Workplane objects very much are designed with the notion that there is one workplane.  Workplane is a subclass of CQ, and in all common examples, your plugin method is being attached to an instance of a Workplane class. The plane is stored/available in the 'plane' property.

If you want to create intermediate Workplane() objects within your plugin, there's nothing stopping you from doing so.   You could use the newObject() method to construct a new CQ object based on yourself, or on a created object.

The tricky part (which you clearly already get in your example above), is that you're really just a method in Workplane, so when your method is done running, nobody else will be able to get access to any of the intermediate objects in the stack.

That means that the behavior you get will be dependent on what you _do_ with this intermediate object. 

For example, if you use it to create a solid, and then union it/subtract it with the current object, this will work fine-- because the current object stores a reference to the updated solid.
But as another example, if you created an intermediate workplane, and then created some wires/edges, with the idea that you'd later exrude them, you'd find that those wires are not available to the next step in the chain.


On Tuesday, September 4, 2018 at 6:37:29 AM UTC-4, John Beard wrote:
Reply all
Reply to author
Forward
0 new messages