import static uk.co.nickthecoder.foocad.layout.v1.Layout2d.* import static uk.co.nickthecoder.foocad.layout.v1.Layout3d.* import static uk.co.nickthecoder.foocad.arrange.v1.Arrange.* import uk.co.nickthecoder.foocad.smartextrusion.v1.* import static uk.co.nickthecoder.foocad.smartextrusion.v1.SmartExtrusion.* class ThreePieceJigsaw : Model { // Settings common to the pre-assembled and the kit. @Custom( about="Size of a single cube" ) var blockSize = 20 @Custom( about="Radius of fillets on all block edges" ) var round = 1 // Setting for pre-assebled version @Custom( about="Only used for the pre-assembled version" ) var clearance = 0.2 // Setings for DIY blocks. @Custom( about="Size of the holes for the kit's blocks" ) var dowelWidth = 4 @Custom( about="Dowels are smaller by this amount than the holes in the kit's blocks" ) var dowelClearance = 0.08 // End of settings. // The 2D shape of the holes used by the kit. // Square to help alignment, but with rounded corners to minimise issues from printing inaccuracies. meth holeShape() = Square( dowelWidth ).center() .roundAllCorners( dowelWidth/8 ) @Piece( about="A single dowel for the kit" ) meth dowel() : Shape3d { return holeShape() .offset(-clearance) .smartExtrude( blockSize/2 - 1 ) .edges( Chamfer(0.5) ) .rotateX(90) } @Piece( about="10 dowels for the kit (only 9 are needed)") meth dowels() : Shape3d { return dowel().tileX(5,1).tileY(2,1) } // A hole position at the top of a `plainBlock` meth hole() = holeShape() .translate( -blockSize/4, -blockSize/4 ) .smartExtrude( blockSize/4 ) .bottom( Chamfer(0.5).reverse() ) .mirrorZ().also() .translateZ( blockSize ) // ***** The kit contains 6 types of blocks. Some are repeated ***** @Piece( about="Corner hole : (4 copies : Quad, Winged * 2, Oddball )" ) meth holedBlock1() = plainBlock() - hole() @Piece( about="Double corner hole : (Winged)") meth holedBlock2() = plainBlock() - hole() - hole().rotateX(90).translate(0, blockSize*3/2, blockSize/2) @Piece( about="Double hole (2 copies : Quad, Oddball)" ) meth holedBlock3() = plainBlock() - hole().mirrorY().also() @Piece( about="Two double-holes (Quad)") meth holedBlock4() = plainBlock() - hole().mirrorY().also() - hole().rotateY(90).translateX(-blockSize/2).translateZ(blockSize/2).also() @Piece( about="Double-hole and opposite single-hole (Oddball) - A mirror image of holedBlock6") meth holedBlock5() = plainBlock() - hole().mirrorX().also() - hole().translateZ(-blockSize) @Piece( about="Double-hole and opposite single-hole (Quad) - A mirror image of holedBlock5" ) meth holedBlock6() = plainBlock() - hole().mirrorX().also() - hole().translateZ(-blockSize).mirrorX() @Piece( about="The kit's blocks. To complete the kit, aslo print `dowels`." ) meth holedBlocks() : Shape3d { return arrange( holedBlock1(), holedBlock1(), holedBlock1(), holedBlock1(), holedBlock2(), holedBlock3(), holedBlock3(), holedBlock4(), holedBlock5(), holedBlock6() ) } @Piece( about="A complete kit of 10 blocks and the dowels." ) meth kit() : Shape3d { return arrange( dowels(), holedBlock1(), holedBlock1(), holedBlock1(), holedBlock1(), holedBlock2(), holedBlock3(), holedBlock3(), holedBlock4(), holedBlock5(), holedBlock6() ) } @Piece( about="A complete kit of 10 blocks and the dowels, joined together like a airfix model." ) meth gift() : Shape3d { val dowels = dowel().centerXY().tileY(2,1).centerY().bottomTo(0) val cagedDowels = cage( dowels, 0 ) val line1 = arrangeX( 2, dowels, dowels, holedBlock2(), holedBlock3(), holedBlock3(), dowels, dowels ) val line2 = holedBlock1().tileX(4,2) val line3 = arrangeX( 2, dowels, holedBlock4(), holedBlock5(), holedBlock6(), dowels ) val pieces = arrangeY( 2, line1.centerX(), line2.centerX(), line3.centerX() ) return cage( pieces, 3 ) } // Used by `gift` to form the outline and the thin tear-away lines that connect all the pieces. meth cage( shape : Shape3d, lineCount : int ) : Shape3d { val lineWidth = 2 val bounds = Vector2( shape.size.x, shape.size.y ) val inside = Square( bounds ) .roundAllCorners(2) .center() .offset(2) val cage = (inside.offset(2) - inside) .smartExtrude(2) .top(Chamfer(0.4)) val lines = Cube( cage.size.x, lineWidth, 0.4 ) .spreadY( lineCount, bounds.y - blockSize + lineWidth ) .centerXY() return shape.centerXY() + cage + lines } // A single cube without holes use by the kit. meth plainBlock() = Square( blockSize ) .center() .roundAllCorners( round ) .smartExtrude( blockSize ) .edges( Fillet( round ) ) // A single block use by the pre-assembled pieces (`pieceQuad`, `pieceOddball` and `pieceWinged`). meth block() = Square( blockSize - clearance ) .roundAllCorners( round ) .translate( clearance/2, clearance/2 ) .smartExtrude( blockSize - clearance*2 ) .edges( Fillet( round ) ) .bottomTo( clearance / 2 ) meth join1x1x0() = Square( blockSize/2 - round*2 ) .leftTo( round ).frontTo( round ) .extrude( clearance ) .centerZ() .rotateX(90) .color("Green") meth join1x2x0() = Square( blockSize/2 - round*2, blockSize - round*2 ) .leftTo( round ).frontTo( round ) .extrude( clearance ) .centerZ() .rotateX(90) .color("Green") meth join0x1x1() = Square( blockSize/2 - round*2 ) .leftTo( round ).frontTo( round ) .extrude( clearance ) .centerZ() .rotateY(-90) .color("Green") meth join0x2x1() = Square( blockSize/2 - round*2, blockSize - round*2 ) .leftTo( round ).frontTo( round ) .extrude( clearance ) .centerZ() .rotateY(-90) .color("Green") meth join1x0x2() = Square( blockSize/2 - round*2, blockSize - round*2 ) .leftTo( round ).frontTo( round ) .extrude( clearance ) .centerZ() .rotateX(90) .color("Green") @Piece( about="One of the 3 pre-assembled pieces. Requires supports." ) @Slice( supportMaterial=1 ) meth pieceQuad() : Shape3d { val raisedFront = block().translate( 0, 0, blockSize/2 ) val raisedBack = block().translate(blockSize/2, blockSize, blockSize/2 ) val floorFront = block().translate(-blockSize, 0, 0) val floorBack = block().translate(0, blockSize*2, 0) val joinA = join1x1x0().translate( blockSize/2 , blockSize*2, blockSize/2 ) val joinB = join1x2x0().translate( blockSize/2, blockSize, blockSize/2 ) val joinC = join0x2x1().translate( 0, 0, blockSize/2 ) return ( floorFront + floorBack + raisedFront + raisedBack + joinA + joinB + joinC ).bottomTo(0) } @Piece( about="One of the 3 pre-assembled pieces. Requires supports." ) @Slice( supportMaterial=1 ) meth pieceWinged() : Shape3d { val floorBack = block() val raised = block().translate( blockSize/2, -blockSize, blockSize/2 ) val floorFront = block().translate( blockSize*3/2, -blockSize*3/2, 0 ) val joinA = join1x1x0().translate( blockSize/2, 0, blockSize/2 ) val joinB = join0x1x1().translate( blockSize*3/2, -blockSize, blockSize/2 ) return ( floorBack + raised + floorFront + joinA + joinB ).bottomTo(0) } @Piece( about="One of the 3 pre-assembled pieces. Reuires supports." ) @Slice( supportMaterial=1 ) meth pieceOddball() : Shape3d { val floorBack = block() val floorFront = block().translate( -blockSize/2, -blockSize , 0 ) val raised = block().translate( -blockSize/2, blockSize, blockSize/2 ) val joinA = join1x1x0().translate(0, blockSize, blockSize/2 ) val joinB = join1x0x2() return ( floorBack + floorFront + raised + joinA + joinB ).bottomTo(0) } @Piece( about="All 3 pre-assembled pieces" ) @Slice( supportMaterial=1 ) meth preAssembled() : Shape3d { val a = pieceQuad() val b = pieceWinged() val c = pieceOddball() return a + b.rotateZ(-90).translate(blockSize*-1-2, blockSize*2.5, 0) + c.translate( blockSize*2+2, blockSize, 0 ) } @Piece( about="Shows what the completed puzzle looks like", printable=false ) override fun build() : Shape3d { val a = pieceQuad().mirrorZ().color("Green") val b = pieceWinged().mirrorZ().color("Orange") val c = pieceOddball().mirrorZ().color("Violet") val all = b.rotateZ(-90) + a.rotateZ(-90).translate( -blockSize*3/2, -blockSize/2, blockSize ) + c.rotateX(90).rotateZ(180).translate(0, blockSize/2, -blockSize) return all.bottomTo(0).color("Yellow") + gift().translateX( blockSize* 3.5 ).color("GhostWhite") } }