Exit Full View
ZipUp

/Furniture/Stool.foocad

Stool

The wood is from old bed slats (from Ikea), with each slat cut in thirds. This is why the sizes is almost square, but not quite. Care must be taken to not switch the pieces, which are different by only 4mm.

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.along.v3.Along.*
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.*

include BoxConstruction.foocad

class Stool : Model {

    var size = Vector3( 360, 360, 480 )
    var linkThickness = 4
    var gap = 1
    var rodDiameter = 3.1
    val lidThickness = 12
    
    var lumberSize = Vector2( 21, 19 )

    var quick = false

    meth wood() = Wood( "main", lumberSize.x, lumberSize.y ).color("DarkGrey")

    meth screwHole() = if (quick) {
            Cube(0)
        } else {
            Countersink().headDiameter(7).holeDiameter(3.5).recess(0.75).depth(2.8)
        }

    meth construction() : BoxConstruction = BoxConstruction().apply {
        cornerExtra = 17
        angleExtra = 10
        thickness = 3.0
        insideRadius = 2
        crossSection = lumberSize
        screwHole = screwHole()
    }

    meth fourOf( shape: Shape3d ) : Shape3d {
        return shape.leftTo(1).frontTo(1).mirrorX().also().mirrorY().also()
    }

    @Piece( about="For base's top corners. Required: 2x2" )
    meth corner() : Shape3d {
        val con = construction()
        // As the corner will be hidden by the seat, we can add an extra screw...
        return con.cornerB() -
            screwHole().mirrorZ()
                .translateX( con.thickness + wood().thickness/2 )
                .translateY( con.thickness + wood().width/2 )
    }

    @Piece
    meth corners() = fourOf( corner() )

    @Piece(about="For bars near the floor. Required: 2x2" )
    meth forBar() : Shape3d {
        val a = construction().straightConnectorA()
        val b = construction().straightConnectorC()
            .mirrorY()
            .mirrorZ().bottomTo(0)
            .rotateZ(90)
            .leftTo( a.left ).frontTo( a.front )

        return (a and b).rotateX(90).bottomTo(0)
    }

    @Piece
    meth forBars() = fourOf( forBar() )

    @Piece
    meth forRailA() = forBar()

    @Piece
    meth forRailB() = construction().straightConnectorC().rotateX(90).bottomTo(0)

    @Piece
    meth forRails() : Shape3d {
        val a = forRailA().leftTo(1).frontTo(1)
        val b = forRailB().rightTo(-1).frontTo(1)
        return (a + b).mirrorY().also()
    }

    @Piece
    meth angleA() = construction().angleA(45) 

    @Piece
    meth angleC() = construction().angleC(45) 

    @Piece
    meth backAngles() : Shape3d {
        return fourOf( angleA() )
    }

    @Piece
    meth frontAngles() : Shape3d {
        val con = construction()

        val a = angleA().leftTo(1).mirrorX().also().frontTo(1)
        val c = angleC().leftTo(1).mirrorX().also().backTo(-1)

        return a + c
    }

    @Piece( about="For Lid's corners. Required: 2x2" )
    meth elbow() = construction().jointC()

    @Piece
    meth elbows() : Shape3d {
        return fourOf( elbow() )
    }

    @Piece
    meth foot() = construction().endStop(20) -
        screwHole().mirrorZ()

    @Piece
    meth feet() = fourOf( foot() )

    @Piece( about="Rubber (TPU) glued to piece `foot` (or directly to the leg if you prefer)" )
    meth footPad() = Square( wood().width + 2, wood().thickness + 2 )
            .center()
            .roundAllCorners(3)
            .smartExtrude(2)
                .top( Chamfer(1.5) )
            .color("Purple")

    meth ply( x : double, y : double, cornerSize: double ) : Shape3d {
        val sq = Square(x,y).offset(-3).center()
        val corners = Square( cornerSize -3 )
            .roundCorner(2, cornerSize*0.75)
            .leftTo(sq.left).frontTo(sq.front)
            .mirrorX().also().mirrorY().also()

        return (sq-corners).extrude(6)
    }

    @Piece( about="A template to draw the corners of the seat" )
    meth topCornerJig() = cornerJig( wood().width*2 )
    @Piece( about="A template to draw the corners of the shelf" )
    meth shelfCornerJig() = cornerJig( wood().width*1.75 )
    meth cornerJig( cornerSize : double) : Shape3d {
        val hole = Square( cornerSize -3 )
            .roundCorner(2, cornerSize*0.75 )
        val shape = Square( cornerSize*1.5 ).roundCorner(2,cornerSize*2) - hole

        val walls = Cube( 2, cornerSize/2, 4 )
            .rightTo(0)
            .frontTo( cornerSize )
            .rotateZ(-90).backTo(0).also(2)
        
        return shape.extrude(0.6) + walls
    }

    @Piece( printable=false )
    meth seat() : Shape3d {
        val con = construction()
        val wood = wood()

        val extra = con.thickness*4 + 6
        val size = Vector2( this.size.x + wood.thickness*2 + extra, this.size.y + wood.thickness*2 + extra )

        val sides = wood.cut( size.x )
            .label("lidSide")
            .edgeDownAlongX().centerX().frontTo( -size.y/2 )
            .mirrorY().also()
        val ends = wood.cut( size.y - wood.thickness*2 )
            .label("lidEnd")
            .edgeDownAlongY().centerY().leftTo( -size.x/2 )
            .mirrorX().also()

        val corners = elbow()
            .rotateX(-90).bottomTo( -con.thickness )
            .leftTo( -size.x/2 - con.thickness )
            .frontTo( -size.y/2 - con.thickness )
            .mirrorX().also().mirrorY().also()
            .color("Orange")

        val ply = ply( size.x, size.y, wood.width*2 )
            .bottomTo( sides.top )

        return sides + ends + corners + ply
    }


    meth rail() : Shape3d {
        val con = construction()
        val wood = wood()

        val sideRails = wood.cut( size.y - wood.thickness*2 )
            .label("sideRail")
            .edgeDownAlongY()
            .centerY()
            .rightTo( size.x/2 )
            .mirrorX().also()
        val backRail = wood.cut( size.x - wood.width*2 )
            .label("endRail")
            .edgeDownAlongX()
            .centerX()
            .backTo( size.y/2 )
            .color("LightGrey")

        val forRailsA = forRailA().rotateX(-90)
            .rotateZ(180)
            .rightTo( sideRails.right + con.thickness )
            .backTo( backRail.back + con.thickness )
            .bottomTo( - con.thickness )
            .mirrorX().also()
            .color("Orange")

        val forRailsB = forRailB().rotateX(-90)
            .rotateZ(90)
            .rightTo( sideRails.right + con.thickness )
            .frontTo( -size.y/2 - con.thickness )
            .bottomTo( - con.thickness )
            .mirrorX().also()
            .color("Orange")

        return sideRails + backRail //+ forRailsA + forRailsB
    }

    meth shelf() : Shape3d {

        val con = construction()
        val wood = wood()

        val sideBars = wood.cut( size.y - wood.width*2 )
            .label("sideBar")
            .edgeDownAlongX().centerX().frontTo( -size.y/2 )
            .mirrorY().also()
            .color("LightGrey")

        val endBars = wood.cut( size.x - wood.thickness*2 )
            .label("endBar")
            .edgeDownAlongY().centerY().leftTo( -size.x/2 )
            .mirrorX().also()

        val shelf = ply( size.x -2, size.y-2, wood.width*1.75 )
            .bottomTo( sideBars.top )

        val forBars = forBar().rotateX(-90)
            .translateX( -size.x/2 + wood.width )
            .frontTo( -size.y/2 - con.thickness )
            .translateZ( -con.thickness )
            .mirrorX().also().mirrorY().also()
            .color("Orange")

        return sideBars + endBars + forBars + shelf
    }

    @Piece( printable=false )
    override fun build() : Shape3d {
        quick = true
        val con = construction()
        val wood = wood()

        val barHeight = size.z * 0.2
        val railHeight = size.z * 0.6

        val corners = corner()
            .rotateZ(90)
            .rightTo( size.x/2 + con.thickness )
            .frontTo( -size.y/2 - con.thickness )
            .mirrorX().also().mirrorY().also()
            .mirrorZ().translateZ(size.z + con.thickness)
            .color("Orange")

        val foot = foot()
            .leftTo( -size.x/2 -con.thickness )
            .frontTo( -size.y/2 - con.thickness )
            .translateZ( - con.thickness )
        val feet = foot
            .mirrorX().also().mirrorY().also()
            .color("Orange")

        val pad = footPad().mirrorZ()
            .centerXTo( foot.middle.x )
            .centerYTo( foot.middle.y )
            .topTo(feet.bottom)
        val pads = pad
            .mirrorX().also().mirrorY().also()


        val strutSide = wood.cut( size.x )
            .label("sideStrut")
            .edgeDownAlongX().centerX().frontTo( -size.y/2 )
            .mirrorY().also()
            .color("DarkGrey").darker()
        val strutEnds = wood.cut( size.y - wood.thickness*2 )
            .label("endStrut")
            .edgeDownAlongY().centerY().leftTo( -size.x/2 )
            .mirrorX().also()
        val legs = wood.cut( size.z - wood.width )
            .label("leg")
            .leftTo( -size.x/2 ).frontTo( -size.y/2 )
            .mirrorX().also().mirrorY().also()
            .color("DimGray")

        val struts = legs +
            (strutSide + strutEnds).translateZ( size.z - wood.width)
        
        val seat = seat()
            .translateZ( size.z - wood.width )

        val shelf = shelf().translateZ( barHeight )

        val backAngleJoints1 = con.angleA(45)
            .rotateY(90)
            .backTo( corners.back )
            .topTo( corners.top )
            .translateX(10).mirrorX().also()
            .color("Orange")
        val backAngleJoints2 = con.angleA(45)
            .backTo( corners.back )
            .leftTo(corners.left).mirrorX().also()
            .bottomTo( size.z - size.x/2 )
            .color("Orange")

        val frontAngleJoints1 = con.angleC(45)
            .rotateZ(90)
            .frontTo( corners.front )
            .bottomTo( railHeight - 150 )
            .leftTo( corners.left ).mirrorX().also()
            .color("Orange")

        val frontAngleJoints2 = con.angleA(45)
            .rotateZ(-90).rotateX(90)
            .backTo( -size.y/2 + 150 )
            .leftTo( corners.left ).mirrorX().also()
            .topTo( railHeight + con.thickness )
            .color("Orange")

        val angles = backAngleJoints1 + backAngleJoints2 + frontAngleJoints1 + frontAngleJoints2

        val rails = rail().topTo( railHeight )

        return struts + corners + rails  + angles + seat + feet + pads + shelf
    }

}