Exit Full View
ZipUp

/Tools/DrillBitTidy.foocad

DrillBitTidy

Stacking trays specially designed to fit into my drill's case.

The trays stacks ontop of each other with an interference fit at 2 positions along each side. Decrease ribClearance for a tighter fit.

FooCAD Source Code
import uk.co.nickthecoder.foocad.smartextrusion.v1.*
import static uk.co.nickthecoder.foocad.smartextrusion.v1.SmartExtrusion.*
import static uk.co.nickthecoder.foocad.layout.v1.Layout2d.*
import static uk.co.nickthecoder.foocad.layout.v1.Layout3d.*
import static uk.co.nickthecoder.foocad.cup.v1.Cup.*

class ShearY( val factor : float ) : Transform3d {
    override meth transform(from: Vector3): Vector3 {
        return Vector3( from.x, from.y + from.x * factor, from.z )
    }
}

class DrillBitTidy : Model {

    @Custom( about="Extarior size of each tray" )
    var size = Vector2( 70, 130 )

    @Custom( about="Wall thickness" )
    var thickness = 1.8

    @Custom( about="Thickness of the base" )
    var baseThickness = 1.2

    @Custom( about="Radius of the corners" )
    var radius = 5

    @Custom( about="How much smaller is the lower part" )
    var clearance = 0.3

    @Custom( about="Each side has ribs, which form an interference fit with the next tray" )
    var ribClearance = -0.3

    @Custom( about="How much the tray fits (Z) into the tray below (not including the chamfer)" )
    var recess = 5

    @Custom( about="A small chamfer at the base (prevents elephant foot, and nicer feel" )
    val baseChamfer = 0.8

    meth tray( dividerHeight : float ) : Shape3d {

        val depth = dividerHeight + recess + thickness + 1

        val shape = Square( size.x, size.y )
            .center()
            .roundAllCorners( radius )
        val base = shape.offset( -thickness - clearance )

        val rib = Square( thickness )
            .center()
            .smartExtrude( recess ).bottom( Chamfer(thickness/5) )
            .bottomTo( baseChamfer )
            .color("red")
    
        val main = ExtrusionBuilder().apply {
            crossSection( base.offset(-baseChamfer) )
            forward(baseChamfer)
            crossSection( base )
            forward( recess-baseChamfer )
            crossSection()
            forward( thickness )
            crossSection( shape )
            forward( depth - recess-thickness )
            crossSection() // Top

            forward( -thickness )
            crossSection( -thickness )
            val down = depth - thickness*2.5 - recess
            forward( -down )
            crossSection()
            val delta1 = depth - down - thickness - baseThickness
            forward( -delta1 )
            crossSection( -delta1 )
            crossSection()
        }.build()

        val ribs =
            rib.rightTo(size.x/2-thickness-ribClearance).repeatY( 2, size.y/2 ).centerY()
                .mirrorX().also() +
            rib.backTo(size.y/2-thickness-ribClearance).repeatX( 2, size.x/2 ).centerX()
                .mirrorY().also()

        return main + ribs
    }

    meth divider( height : float ) : Shape3d {

        val below = Cube( size.x - thickness*2 - clearance*2, thickness, recess + thickness )
            .bottomTo( baseThickness )
            .centerX()
        // x used to use thickness*2, which made it disconnected from the walls :-(
        val middle = Cube( size.x - thickness - clearance*2, thickness, height - below.size.z )            .centerX()
            .bottomTo( below.top )

        return below + middle
    }

    meth slottedDivider( height : float, diameters : List<float> ) : Shape3d {
        val divider = divider( height )
        var shape : Shape2d = Square(0)
        var x = 0.0
        val deltaX = (divider.size.x - thickness * 3) / ( diameters.size() +0.25 )
        for ( diameter in diameters ) {
            val hole = Circle( diameter/2 ).translate( x, height/2 + divider.bottom - 1 )
            val slot = hole.hull( hole.translateY( height ) )
            shape += slot
            x += deltaX
        }

        val toRemove = shape.centerX()
            .extrude( thickness *3 )
            .centerZ()
            .rotateX(90)

        return divider - toRemove
    }

    meth embossedLabels( labels : List<String> ) : Shape3d {
        var labelShape : Shape2d = Square(0)

        var x = 0
        var y = 12
        val deltaX = (size.x - thickness * 3 - recess*2) / ( labels.size() +0.25 )

        for (label in labels) {
            labelShape += Text( label ).centerX().translate( x, y )
            x += deltaX
            y = 12 - y
        }

        return labelShape.centerX()
            .extrude( 1 )
            .bottomTo( baseThickness - 0.6 )
    }

    @Piece
    meth simple() : Shape3d {
        val dividerHeight = 9
        val tray = tray( dividerHeight )

        val divider = divider( dividerHeight )
            .translateY( 37 )

        val slottedDivider = slottedDivider( dividerHeight,
            listOf<float>( 2.8, 4, 6.0, 6.5, 8 )
        ).frontTo( tray.front + 20 )

        val angledDivider = slottedDivider.transform( ShearY( 0.5 ) )
            .frontTo( slottedDivider.front + 30 )

        val labels = embossedLabels( listOf<String>( "3", "4", "6", "6.5", "8" ) )
            .translateY(-34)

        val example = Cylinder( 92, 8/2 )
            .rotateX(90)
            .centerZTo( baseThickness + dividerHeight/2 )
            .frontTo( tray.front + thickness + 6 )
            .centerXTo(22)
            .previewOnly()

        return ( tray - labels + divider + angledDivider + slottedDivider )
            .color("Violet" ) + example
    }

    @Piece
    meth selfCentering() : Shape3d {
        val dividerHeight = 13
        val height = dividerHeight + recess + thickness + 1
        val tray = tray( dividerHeight )

        val example = Cylinder( 100, 12/2 ).rotateX(90)
            .bottomTo( baseThickness )
            .translateY( 50 )
            .previewOnly()

        val farDivider = slottedDivider( dividerHeight,
                listOf<float>( 5.9, 6.0, 6.9, 6.9 )
            ) .translateY( 23 )

        val nearDivider = slottedDivider( dividerHeight,
                listOf<float>( 5, 5, 5, 5 )
            )
            .centerYTo( tray.front + thickness + 20 )

        val labels = embossedLabels( listOf<String>( "1.8", "2.6", "3.3", "4.0" ) )
            .backTo( farDivider.front )

        return (
            tray - labels + farDivider + nearDivider 
        ).color( "Blue" ) + example
    }

    @Piece
    meth countersinkDrill() : Shape3d {
        val dividerHeight = 21
        val tray = tray( dividerHeight )

        val divider = slottedDivider( dividerHeight, listOf<float>( 2.8, 3.9, 6, 4.9, 5.8 ) )
            .backTo( tray.back - thickness - 20 )

        val slottedDividers = slottedDivider( dividerHeight,
            listOf<float>( 2.8, 3.9, 10, 4.9, 5.8 )
        ) + slottedDivider( dividerHeight,
            listOf<float>( 2.8, 3.9, 0, 4.9, 5.8 )
        ).frontTo( tray.front + thickness + 20 )
    
        val labels = embossedLabels( listOf<String>( "3", "4", "", "5", "6" ) )
            .translateY(10)
            .translateY(-40).also()

        val example = Cylinder( 93, 15/2 )
            .rotateX(90)
            .bottomTo( baseThickness )
            .frontTo( tray.front + thickness + 10 )
            .translateX(22)
            .previewOnly()

        val example2 = (
                Cylinder( 73, 5/2 ) +
                Cylinder( 49, 20/2, 5/2 ).translateZ(24)
        )
            .rotateX(90)
            .backTo( tray.back - thickness )
            .centerZTo( baseThickness + dividerHeight/2 )
            .previewOnly()

        return (
            tray + divider + slottedDividers - labels
        ).color( "Green" ) + example + example2
    }

    @Piece
    meth tray15() : Shape3d = tray(15).color("Orange")

    @Piece
    meth lid() : Shape3d {
        val tray = tray( 10 )
        val envelope = Cube( size.x + 2, size.y + 2,  thickness + recess )
            .centerXY()

        val hole = Cylinder( baseThickness*3, 20/2 )
            .centerZ()
            .backTo( tray.back - thickness - 10 )

        return (tray.intersection(envelope) - hole).color("Yellow")
    }

    @Piece
    meth long() : Shape3d {
        size = Vector2( 160, 70 )
        val tray = tray( 15 )

        val divider = divider( 15 )

        return tray + divider
    }

    @Piece
    meth longLid() : Shape3d {
        size = Vector2( 160, 70 )
        return lid()
    }

    override fun build() : Shape3d {
        
        val tray15 = tray15()

        val lid = lid().bottomTo( tray15.top - thickness - recess )

        val selfCentering = selfCentering()
            .leftTo( tray15.right + 2 )

        val countersinkDrills = countersinkDrill()
            .frontTo(tray15.back+2)

        val simple = simple()
            .frontTo(tray15.back+2)
            .leftTo( tray15.right + 2 )

        //return selfCentering + lid.bottomTo( selfCentering.top - thickness - recess )
        //return simple + lid
        return tray15 + lid +
            selfCentering +
            countersinkDrills + simple
    }
}