import static uk.co.nickthecoder.foocad.layout.v1.Layout2d.* import static uk.co.nickthecoder.foocad.layout.v1.Layout3d.* import static uk.co.nickthecoder.foocad.chamferedExtrude.v1.ChamferedExtrude.* class HexBall : Model { @Custom val ballD = 20.5 // The size of the "inner" sphere. // A larger number will capture the balls more completely, // If you get this number "just right", you may be able to pop the balls // in and out of a TPU printed object. ??? @Custom var innerScale = 1.25 // I like to print a 2 to test the fit. // Adjust ballD and innerScale if the fit isn't quite perfect. // Only then will I print the remaining 6 pieces. @Custom var quantity = 8 fun whole() : Shape3d { val hole = Sphere(ballD/2) val end = hole.tileX(2).tileY(2).center() val dist = ballD * Math.sqrt(2) val middle = hole.repeatX( 2, dist ).repeatY( 2, dist ).center().rotateZ(45) val holes = middle + end.translateZ( dist/2 ).mirrorZ().also() val sphere = Sphere( ballD * innerScale ) val result = (sphere - holes) return result } @Piece fun eighth() = eighth(1) fun eighth( quantity : double ) : Shape3d { val sphere = Sphere( ballD * innerScale ) val quarter = Cube( ballD *2 ).rotateZ(45) val hole = Sphere(ballD/2) val dist = ballD * Degrees.cos(60) //Math.sqrt(2) val piece = (sphere / quarter) - hole.translateY( ballD ) - hole.translateY( ballD ).rotateX(45).rotateZ(45).mirrorX().also() // 8 pieces will make a complete sphere. // But now lets make them easy to print, with less overhang, putting the // corner stright down, and then slicing it off for good bed contact. // NOTE. 54.735 isn't the "right" rotation, but my maths is poor :-( // I obtained this by trial and error (with a binary chop to get ever closer). val eighth = piece.rotateX(54.735).translateZ(-ballD*0.3) / Cube( ballD*2 ).centerXY() // See how the eighth piece compares to the whole sphere //return eighth + whole().rotateX(54.735).translateZ(-ballD*0.3).previewOnly() return if ( quantity %2 == 0) { eighth.tileX(quantity/2).tileY(2) } else { eighth.tileX(quantity) } } @Piece fun center() : Shape3d { val delta = ballD*0.3 val debug = eighth(1).previewOnly() .rotateX(180) val size = ballD*0.52 + 0.3 val solid = PolygonBuilder().apply { moveTo(0,-size) lineTo(size,0) lineTo(0,size) }.build().revolve().sides(4) .rotateZ(45) .rotateX(54.735) .toPolyhedron() .toOriginZ() return solid + debug } override fun build() : Shape3d { return center().translateY(-30) + eighth(quantity) } }