/Games/ThreePieceJigsaw.foocad

An extremely difficult 3 piece puzzle!
Copied from this YouTube video :
[MatthiasWandel](https://www.youtube.com/watch?v=sASwadHr11M)
Arrange the blocks to form a blocky triangular pyramid (AKA tetrahedron).
The model can be printed in a three different ways :
- Pre-assembled using
pieceQuad
,pieceWinged
, andpieceOddball
- In kit form, using
kit
. - In an old-school kit form, using
gift
. This is identical tokit
, but all the pieces are joined together, and must be ripped (or cut) apart. This is great for gifts.
The pre-assembled version requires support material. I try to avoid support material.
If the dowels in the kit don't fit well, adjust dowelClearance
and reprint piece dowels
See piece default
for the finished shape.
Don't worry, it doesn't provide any spoilers, unless you study it
really carefully.
If you print more than one copy (especially if they are presents for siblins), consider creating a mirror image, and make sure they are different colours!
SPOILERS
- This is a mirror image of Mattias's puzzle.
- No cubes are fully aligned with neighbouring cubes.
- There is a void in the middle!
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") } }