import static uk.co.nickthecoder.foocad.chamferedextrude.v1.ChamferedExtrude.* import static uk.co.nickthecoder.foocad.layout.v1.Layout2d.* import static uk.co.nickthecoder.foocad.layout.v1.Layout3d.* import static uk.co.nickthecoder.foocad.extras.v1.Extras.* include Hinge.feather class PuzzleOfEvil : Model { @Custom( about="The unit side of the triangles" ) var size = 16.0 // You cannot make thickness+boxThickness too small, otherwise the hinge // will be too small, and likely to fail/break. @Custom( about = "Thickness of the pieces") var thickness = 4 @Custom( about = "Thickness of the base and lid" ) var boxThickness = 2 @Custom( about="Chamfer of the box and playing pieces" ) var chamfer = 0.4 // On my printer using low quality settings, 0.2 is just enough // for them to fit together quite snuggly. (I can turn the solved puzzled // upside down and the pieces don't fall out). You may prefer a larger slack. @Custom( about = "The pieces are shrunk slightly so that they fit together" ) var slack = 0.2 @Custom( about = "Thickness of the magnets which hold the case closed. 0 for no magnets" ) var magnetT = 1.2 @Custom( about = "Diameter of the magnets which hold the case closed" ) var magnetD = 11.0 fun move( shape : Shape2d, x : double, y : double ) = shape.translate( x*size, y * size * Degrees.sin(60) ) fun move( shape : Shape3d, x : double, y : double ) = shape.translate( x*size, y * size * Degrees.sin(60), 0 ) fun triangle() = Triangle( size ).toOrigin() fun line( length : int ) = triangle().hull(triangle().translateX((length-1)*size)) fun profile( n : int ) : Shape2d { val one = triangle().offset(1) val two = line(2).offset(1) val three = line(3).offset(1) val four = line(4).offset(1) val five = line(5).offset(1) val six = line(6).offset(1) val seven = line(7).offset(1) val shape = if ( n == 1 ) { move(three,0,0) + move(three.rotate(-60),2, 0) + move(two.rotate(60),3,-2) + move(two.rotate(180),4,-2) } else if (n == 2) { two.rotate(180) + move(three.rotate(-120), -2,0) + move(two.rotate(-60), -3.5, -3 ) + move(two.rotate(180),-1,-4) } else if (n == 3) { three + move(three.rotate(120),4,-2) + move(one.rotate(60),3.5,-1) + move(one.rotate(60),4,-2) } else if (n == 4) { two + move(three.rotate(-60),0.5,1) + move(three.rotate(60),1,-4) + move(two.rotate(-60),0.5,-3) } else if (n == -1) { // Easy puzzle outline val twoByFive = five.mirrorY().also() val twoByFour = four.mirrorY().also() val hex2 = Hull2d(move(three,-1.5,1).mirrorY().repeatAround(3)) move(twoByFive, 0, -1.5) + move(twoByFour.rotate(-60),0.5,-2.5) + move(twoByFour.rotate(-60),2, -1.5) + move(hex2,5,-3.5) } else if (n == -2) { // Hard Puzzle outline val twoByFive = five.mirrorY().also() val twoByFour = four.mirrorY().also() move(four,1,5.5) + move(five,0.5,4.5) + move(six,0,3.5) + move(seven.mirrorY().also(),-0.5,2.5) + move(two.mirrorY(),4,1.5) + move(four.mirrorY(),0,1.5) } else if (n == -3) { // Box move(three.rotate(60),-0.5,-3).hull( move(four.rotate(-60),5,0) ).hull( move(four,1.5,-7) ) } else { one } return shape.offset(-1) } fun piece( n : int ) = profile(n) .offset(-slack) .chamferedExtrude( thickness, chamfer ).color("Red") @Piece fun testHinge() : Shape3d { val hinge : Shape3d = hinge( 30 ).centerX() val side = Cube( 30, 6, boxThickness + thickness ) .centerX().frontTo(hinge.back).topTo(0) return hinge + side.mirrorY().also() } fun hinge( length : double ) : Shape3d { val hingeGap = chamfer val hinge = Hinge( boxThickness + thickness + slack, (boxThickness + thickness + slack)/2, length- hingeGap*2, 3 ) hinge.profile = hinge.angledProfile( boxThickness + thickness - chamfer ) return hinge.build() .rotateZ(90) .leftTo(hingeGap) } @Piece fun box() : Shape3d { val hinge : Shape3d = hinge( size * 6) val profile : Shape2d = profile( -3 ) val bottom = ( profile .chamferedExtrude( boxThickness, chamfer, 0 ).color("Green") + (profile - profile( -1 )).extrude( thickness ).translateZ(boxThickness) ).backTo(hinge.back+chamfer).topTo(0) val top = ( profile.mirrorY() .chamferedExtrude( boxThickness, chamfer, 0 ).color("Green") + ( profile.mirrorY() - profile(-2)).chamferedExtrude( thickness,0 ).translateZ(boxThickness) ).frontTo(hinge.back).topTo(0) val magnets = move(Cylinder(magnetT,magnetD/2),5.25,6.5).mirrorY().also() .topTo(0) val hard = move( Text("HARD", BOLD).extrude(thickness/2).topTo(top.top) .rotateZ(-60), 5.3, 5.7 ) return bottom + top - hard + hinge - magnets.color("Red") } @Piece fun piece1() = piece(1) @Piece fun piece2() = piece(2) @Piece fun piece3() = piece(3) @Piece fun piece4() = piece(4) fun hard() = profile( -2 ).extrude(0.6) fun easy() = profile( -1 ).extrude(0.6) override fun build() : Shape3d { return box().translateZ(-boxThickness) + ( move(piece(1).rotateZ(-60),-3,-2) + move(piece(2),3.5, -1) + move(piece(3).rotateZ(-120),4.5, -1) + move(piece(4).rotateZ(-60),4.5,-3) ).translate(0.1,3.1,0) } }