Exit Full View
Up

/Util/NutAndBoltForWood.foocad

NutAndBoltForWood
FooCAD Source Code
import static uk.co.nickthecoder.foocad.extras.v1.Extras.*
import uk.co.nickthecoder.foocad.threaded.v1.Threaded

/**
Sometimes, I want to attach things to a plywood sheet, which can be undone by hand (no tools)
Drill a large hole through the plywood, and glue in the `nut`.
Cap the other side of the wood with the optional `trim`.
*/
class NutAndBoltForWood : Model {

    @Custom( about="Thickness of the plywood" )
    var woodThickness = 8

    @Custom
    var holeDiameter = 14.7 // Actually 15mm, but adding clearance.
    
    @Custom( about="The size of the square part on the opposite side of the wood" )
    var nutSize = Vector3( 30, 30, 5 )

    @Custom
    var threadSize = 2

    @Custom
    var threadDiameter = 10

    @Custom
    var threadPitch = 6

    @Custom
    var washerThickness = 2.0

    @Custom( about="Extra length to add to the bolt (the thickness of the thing being attached")
    var boltExtra = 10

    @Custom( about="Thickness and depth of the trim" )
    var trimSize = Vector2( 1, 2 )

    fun boltLength() = woodThickness + nutSize.z + boltExtra + washerThickness

    fun threaded() = Threaded.create( threadSize, threadDiameter, threadPitch )

    // If you need a different shape, create a subclass, and override this method.
    fun nutProfile() : Shape2d = Square( nutSize.x, nutSize.y ).center().roundAllCorners(4)
        
    // If you need a different shape, create a subclass, and override this method.
    fun trimProfile() : Shape2d = Circle( holeDiameter/2 + 2 )

    @Piece
    fun nut() : Shape3d {
        // On the opposite side of the wood.
        val block = nutProfile().extrude( nutSize.z )
        // Inside the hole drilled through the plywood.
        val neck = Cylinder( woodThickness, holeDiameter / 2 )
            .bottomTo( block.top )
        // Recess for the `trim` piece.
        val forTrim = Cylinder( trimSize.y+0.3, holeDiameter / 2 - trimSize.x  )
            .topTo(neck.top+0.1)
            .color("Red")

        val thread = threaded().hole( nutSize.z + woodThickness + 4 ).translateZ(-2)

        return block + neck - thread - forTrim
    }
    
    @Piece
    fun trim() : Shape3d {
        val r = holeDiameter / 2 - trimSize.x - 0.3
        val neck = (Circle( r ) - Circle( r - trimSize.x))
            .extrude( trimSize.x + trimSize.y )
        
        val flat = (trimProfile() - Circle( r ))
            .extrude( trimSize.x )

        return neck + flat
    }

    @Custom
    var boltHeadHeight = 4

    @Piece
    fun bolt() : Shape3d {
        val bolt = threaded().bolt( boltLength() + boltHeadHeight, boltHeadHeight )
        // For a slot for a screwdriver, so we can get the bolt out, if it snaps.
        val slot = Cube( 20, 20, 1 ).centerZ()
            .center()
            .rotateX(90)
            .bottomTo( bolt.top - 1 )
        return bolt - slot
    }

    /**
        Print using TPU
    */
    @Piece
    fun washer() : Shape3d {
        val bolt = bolt()
        return (Circle( bolt.size.x * 0.45 ) - Circle( threadDiameter/2 ) )
            .extrude( 1.6 )
    }

    override fun build() : Shape3d {
        // Set to 0 for the pieces in the final posision.
        // Set to 10 ish so that each piece is separated.
        val explode = 10

        val nut = nut().color("Green")
        val trim = trim().mirrorZ().topTo( nut.top + trimSize.x + explode ).color("LightGreen")
        val attachment = (Square( 30 ).center() - Circle( threadDiameter/2 + 1))
            .extrude( boltExtra )
            .bottomTo( trim.top + explode )
            .previewOnly()
        val washer = washer(). bottomTo( attachment.top + explode ).color( "DarkBlue" )
        val bolt = bolt().mirrorZ().topTo( washer.top + boltHeadHeight + explode )

        return nut + trim + bolt + washer + attachment
    }
}