Exit Full View
Up

/GardenFurniture/NoobWoodworker.foocad

NoobWoodworker

Can we build garden furniture using just straight cuts?

I'm also testing the use of fabric for side panels (i.e. either canvas, or the thinner plastic tent fabrics).

FooCAD Source Code
import uk.co.nickthecoder.foocad.woodworking.v1.*
import static uk.co.nickthecoder.foocad.woodworking.v1.Woodworking.*
import uk.co.nickthecoder.foocad.screws.v3.*
import static uk.co.nickthecoder.foocad.layout.v1.Layout2d.*
import static uk.co.nickthecoder.foocad.layout.v1.Layout3d.*
import static uk.co.nickthecoder.foocad.along.v3.Along.*
import uk.co.nickthecoder.foocad.woodworking.v1.*
import static uk.co.nickthecoder.foocad.woodworking.v1.Woodworking.*

class DemoNoobWoodworker : Model {

    @Custom
    var noob = NoobWoodworker()

    @Custom
    var wood = Wood( "wood", 45, 16 )

    @Custom
    var angleRadius = 20

    @Piece
    meth guideShim() = noob.guideShim( wood.size )

    meth preview() = wood.cut( 100 ).previewOnly()

    @Piece
    meth angle() = noob.angle( wood.size.x, angleRadius ) +
        preview().edgeDownAlongX().mirrorY() +
        preview().edgeDownAlongY().rightTo(0).frontTo(-wood.thickness)

    @Piece
    meth angle2() = noob.angle2( wood.width, wood.thickness, angleRadius ) +
        preview().edgeDownAlongX().backTo(0).bottomTo( noob.flatThickness ) +
        preview().edgeDownAlongY().leftTo(-wood.thickness).frontTo(-wood.thickness).bottomTo( noob.flatThickness )

    @Piece
    meth corner() = noob.corner( wood ) +
        preview().sideDownAlongX().bottomTo( noob.flatThickness ).leftTo( noob.betweenThickness ) +
        preview().sideDownAlongY().bottomTo( noob.flatThickness ).rightTo( 0 )

    @Piece
    meth doubleCorner() = noob.doubleCorner( wood )
    
    @Piece
    meth tConnector() = noob.tConnector( wood ) +
        preview().sideDownAlongX().bottomTo( noob.flatThickness )  +
        preview().sideDownAlongY().bottomTo( noob.flatThickness ).rightTo( -noob.betweenThickness ).centerYTo( wood.width/2 )

    @Piece
    meth simpleConnector() = noob.simpleConnector( wood.size )

    override fun build() : Shape3d {
        return guideShim()
    }
}

class NoobWoodworker {
    
    @Custom
    var betweenThickness = 2

    @Custom
    var edgeThickness = 3

    @Custom
    var flatThickness = 1
    
    @Custom
    var betweenCountersink = Countersink()

    @Custom
    var outsideCountersink = Countersink()

    @Custom
    var guideCountersink = Countersink()

    @Custom
    var rounding = 4

    var color = "Orange"

    // Set to true when rendering the large-scale, so that the plastic hardware
    // is greatly simplified. i.e. no holes, no threads etc.
    var mock = false

    
    meth guideShim( size : Vector2 ) : Shape3d {
        val shim = Square( size ).center()
            .roundAllCorners(rounding)
            .extrude( edgeThickness )
        val hole = outsideCountersink.topTo( shim.top )

        return if ( mock ) {
            shim
        } else {
            shim - hole
        }.color( color )
    }
    
    meth angle( width : double ) = angle( width, width*0.7 )

    meth angle( width : double, radius : double ) : Shape3d {

        val shape = Square( width, edgeThickness )
            .rotate(90).leftTo(0).also(2)
            .roundCorner(3, radius)
        val holes = outsideCountersink
            .depth( width )
            .recess( width - outsideCountersink.recessDiameter/4 - 1 )
            .rotateX(-90)
            .backTo( width )
            .centerXTo( width *0.75 )
            .centerZTo( width/2 )
            .rotateZ(90).mirrorX().also(2)

        return if (mock) {
            shape.extrude( width )
        } else {
            val roundMask = Square( width )
                .roundCorner(2, rounding)
                .roundCorner(1, rounding)
                .extrude( width*3 )
                .rotateX(90)
                .centerY()
                .bottomTo(0)

            (shape.extrude( width ) - holes)
                .intersection(roundMask)
                .intersection(roundMask.rotateZ(90))
        }
    }

    meth angle2( width : double, thickness : double ) = angle2( width, thickness, width*0.7 )

    meth angle2( width : double, thickness : double, radius : double ) : Shape3d {
        
        val shape = Square( width, edgeThickness )
            .rotate(90).leftTo(0).also(2)
            .roundCorner(3, radius)
        val holes = outsideCountersink
            .depth( width )
            .recess( width - outsideCountersink.recessDiameter/4 - 1 )
            .rotateX(-90)
            .backTo( width )
            .centerXTo( width *0.75 )
            .centerZTo( width/2 )
            .rotateZ(90).mirrorX().also(2)

        val baseShape = (
                Square( width + thickness ).leftTo(-thickness).frontTo(-thickness) -
                Square( width * 2 ).translate(edgeThickness, edgeThickness )
            ).toPolygon()
        val base = baseShape
            .roundCorner(5, rounding )
            .roundCorner(4, rounding )
            .roundCorner(3, rounding )
            .extrude( flatThickness )

        return if (mock) {
            base + shape.extrude( width + flatThickness )
        } else {
            val roundMask = Square( width, width + flatThickness )
                .roundCorner(2, rounding)
                .extrude( width*3 )
                .rotateX(90)
                .centerY()
                .bottomTo(0)
            base + (shape.extrude( width + flatThickness ) - holes)
                .intersection(roundMask)
                .intersection(roundMask.rotateZ(90))
        }

    }

    meth corner( wood : Wood ) = corner( wood.width, wood.thickness )

    meth corner( width : double, thickness : double ) : Shape3d {
        val back = Square( width * 2 + betweenThickness, width )
            .roundCorners( listOf<int>(2, 3), rounding )
            .extrude( flatThickness )
            .leftTo(-width)
        
        val between = Cube( betweenThickness, width, thickness )
            .bottomTo( back.top )

        val betweenHole = betweenCountersink.rotateY(-90)
            .leftTo( between.left )
            .centerYTo( between.middle.y )
            .centerZTo( between.middle.z )
        
        val end = Square( back.size.x, between.size.z + edgeThickness )
            .roundCorners( listOf<int>(2, 3), rounding )
            .leftTo( back.left )
            .extrude( edgeThickness )
            .rotateX(90)

        val endHoles = guideCountersink
            .rotateX(90)
            .frontTo( end.front )
            .centerZTo( end.middle.z )
            .translateX( -width / 4 )
            .translateX( -width / 2 ).also()
            .translateX( width + betweenThickness ).also()

        return if ( mock ) {
            back + between + end
        } else {
            (back + between - betweenHole + end - endHoles )
        }.color( color )
        
    }

    meth doubleCorner( wood : Wood ) = doubleCorner( wood.width, wood.thickness )

    meth doubleCorner( width : double, thickness : double ) : Shape3d {

        val between1 = Square(  width + edgeThickness, thickness)
            .extrude(betweenThickness)
            .rotateY(-90)
            .leftTo(0)
            .frontTo(-thickness)

        val between2 = Square( width + betweenThickness, width + edgeThickness)
            .extrude( edgeThickness )
            .rotateX(90)
            .frontTo(0)
            .leftTo(-width)
        
        val guide1 = Square( width*2 + betweenThickness, thickness )
            .roundCorners( listOf<int>(2, 3), rounding )
            .leftTo( between2.left )
            .extrude( edgeThickness )
            .mirrorY()

        val guide2 = Square( thickness, width )
            .roundCorner( 3, rounding )
            .leftTo( between2.left )
            .frontTo( betweenThickness )
            .extrude( edgeThickness )

        val guide3 = Square( width + edgeThickness, width )
            .roundCorner(2, rounding)
            .extrude( edgeThickness )
            .rotateY(-90)
            .leftTo(guide2.right)
            .translateY(betweenThickness)

        val guide4 = guide3.rotateZ(-90)
            .leftTo(between2.right)
            .frontTo(0)

        val twoHoles = outsideCountersink
            .mirrorZ()
            .translateX(width/2).also()
            .centerXTo( - width/2 )
            .centerYTo( - thickness/2 )

        val bottomHoles = twoHoles.translateX(width + betweenThickness ).also() +
            twoHoles.rotateZ(90)
            .centerXTo( guide2.middle.x )
            .centerYTo( guide2.middle.y )

        val guide4Hole = outsideCountersink
            .rotateX(-90)
            .centerXTo( guide4.middle.x )
            .centerZTo( guide4.middle.z )
            .backTo( guide4.back )

        val guide3Hole = outsideCountersink
            .rotateY(90)
            .centerYTo( guide3.middle.y )
            .centerZTo( guide3.middle.z )
            .rightTo( guide3.right )
            
        val between1Hole = betweenCountersink
            .rotateY(-90)
            .centerYTo( between1.middle.y )
            .centerZTo( width*0.8 )
            .leftTo( between1.left )

        var between2Hole : Shape3d = betweenCountersink
            .rotateX(-90)
            .centerXTo( between2.middle.x )
            .centerZTo( width*0.8 )
            .backTo( between2.back )
        between2Hole = if ( false ) {
            between2Hole.centerXTo( -width + thickness/2 )
        } else {
            between2Hole.centerXTo( -(width-thickness)/2 )
        }
    
        return if ( mock ) {
            between1 + between2 + 
            guide1 + guide2 + guide3 + guide4
        } else {
            between1 + between2 + 
            guide1 + guide2 + guide3 + guide4 -
            guide3Hole - guide4Hole - bottomHoles - between1Hole - between2Hole
        }.color( color )
        
        
    }

    meth tConnector( wood : Wood ) = tConnector( wood.width, wood.thickness )
    meth tConnector( wood : Wood, overlap : double ) = tConnector( wood.width, wood.thickness, overlap )
    meth tConnector( width : double, thickness : double ) = tConnector( width, thickness, width * 2/3 )

    meth tConnector( width : double, thickness : double, overlap : double ) : Shape3d {

        val back = Square( width + overlap + betweenThickness, width + edgeThickness )
            .roundAllCorners(rounding)
            .extrude( flatThickness )
            .leftTo(-overlap)
        val between = Cube( betweenThickness, width, thickness )
            .bottomTo( back.top )
            .frontTo( edgeThickness )

        val guide = Cube( width + betweenThickness - rounding, edgeThickness, thickness + flatThickness )

        val betweenHole = betweenCountersink
            .rotateY(90)
            .centerYTo( between.middle.y )
            .centerZTo( between.middle.z )
            .rightTo( between.right )

        val guideHole = outsideCountersink
            .rotateX(90)
            .centerXTo( guide.middle.x )
            .centerZTo( guide.middle.z )
            .frontTo( guide.front )


        return if (mock) {
            back + between + guide
        } else {
            back + between + guide - betweenHole - guideHole
        }.color( color ).translate( -betweenThickness, -edgeThickness, 0 )
    }

    meth simpleConnector( size : Vector2 ) = simpleConnector( size.x, size.y )

    meth simpleConnector( width : double, thickness : double ) : Shape3d {
        val back = Square( width * 1.5 + betweenThickness, width )
            .extrude( edgeThickness )
            .leftTo(-width)
        val between = Cube( betweenThickness, width, thickness )
            .bottomTo( back.top )

        return (back + between).color( color )
    }
    
}