pondělí 28. května 2012

OpenScad helper funcions for better tomorrow

While working on various reprapped projects I hacked together some useful functions/modules that I want to show and present their strengths. One of basic building blocks of RP parts are holes, and quite often you want them as accurate as possible - and thanks to nophead we now have formula to make them quite well at http://hydraraptor.blogspot.com/2011/02/polyholes.html

Code in blogpost is:

 
module polyhole(d,h) {
    n = max(round(2 * d),3);
    rotate([0,0,180])
        cylinder(h = h, r = (d / 2) / cos (180 / n), $fn = n);
}


which works, but is not programmer-friendly. Considering its cylinder replacement, it should really work that way - but cylinder has radius as argument, not diameter. Another thing is, its not tab-completition friendly - you have to remember another name, instead of just pressing tab and see. So my fix is

 
// make it interchangeable between this and cylinder
module cylinder_poly(r, h, center=false){
    polyhole(d=r*2, h=h, center=center);
}
 
Now when you have hole and realise that polyhole would fit better the change is as simple as it can be.

Another thing that you learn fast is that sharp coners are bad - they tend not to stick well on heatbed destroying overnight prints and tend to look ugly on high speed prints with quick acceleration, making waves on side that is extruded from corner. Good way (except slowing down prints, but who would want to do that? ;)) is corner rounding, or "fillet". As there is no direct way of rounding corners in OpenScad, some work is to be done. Basic building block here is

 
module fillet(radius, height=100, $fn=16) {                                     
    //this creates negative of fillet,
    //i.e. thing to be substracted from object
    translate([-radius, -radius, -height/2-0.01])
        difference() {
            cube([radius*2, radius*2, height+0.02]);
            cylinder(r=radius, h=height+0.02, $fn=16);
        }
}


Then miracle happens:

module cube_negative_fillet(
        size,
        radius=-1,
        vertical=[3,3,3,3], 
        top=[0,0,0,0],
        bottom=[0,0,0,0],
        $fn=0
    ){

    j=[1,0,1,0];

    for (i=[0:3]) {
        if (radius > -1) {
            rotate([0, 0, 90*i])
                translate([size[1-j[i]]/2, size[j[i]]/2, 0])
                    fillet(radius, size[2], $fn=$fn);
        } else {
            rotate([0, 0, 90*i])
                translate([size[1-j[i]]/2, size[j[i]]/2, 0])
                    fillet(vertical[i], size[2], $fn=$fn);
        }
        rotate([90*i, -90, 0])
            translate([size[2]/2, size[j[i]]/2, 0 ])
                fillet(top[i], size[1-j[i]], $fn=$fn);
        rotate([90*(4-i), 90, 0])
            translate([size[2]/2, size[j[i]]/2, 0])
                fillet(bottom[i], size[1-j[i]], $fn=$fn);
    }
}


and we have object to substract from your creations to round their corners, positioned in the same way as centered cube. By default, it will round vertical corners to 3mm radius. You can round all four vertical edges with parameter radius or, giving 4 member array to vertical, you can control which edge you  want to fillet and how much. You can fillet top side with parameter top too, and of course bottom is there too, even tho it probably will not be nicely printable. (You may use support or pass it $fn=4, then it will have 45deg. chamfer). In all cases, it starts in +x, +y quadrant and goes CCW (first top/bottom fillet crosses +x axis).

Of course, I am lazy so I prepared exact cube replacement:

 
module cube_fillet_inside(
         size,
        radius=-1,
        vertical=[3,3,3,3],
        top=[0,0,0,0],
        bottom=[0,0,0,0],
        $fn=0
    ){
    if (radius == 0) {
        cube(size, center=true);
    } else {
        difference() {
            cube(size, center=true);
            cube_negative_fillet(size, radius, vertical, top, bottom, $fn);
        }
    }
}


module cube_fillet(
        size,
        radius=-1,
        vertical=[3,3,3,3],
        top=[0,0,0,0],
        bottom=[0,0,0,0],
        center=false,
        $fn=0 
    ){
    if (center) {
        cube_fillet_inside(size, radius, vertical, top, bottom, $fn);
    } else {
        translate([size[0]/2, size[1]/2, size[2]/2])
            cube_fillet_inside(size, radius, vertical, top, bottom, $fn);
    }
}


Coming soon: Screws and NEMA stepper interface.
Thanks to nophead, vlnofka and josefprusa