import static uk.co.nickthecoder.foocad.screws.v1.Screws.* import static uk.co.nickthecoder.foocad.chamferedExtrude.v1.ChamferedExtrude.* import static uk.co.nickthecoder.foocad.chamferedExtrude.v1.ChamferedExtrude.* class ClubsHolder : Model { // The diameter of the metal rod used to secure the prongs to the back. var rodD = 6.0 // The distance between the centers of the prongs var gap = 90 // The angle that the outer prongs descend. var angle = 10 // The curve which connects the four prongs is circular arc. // This is the radius of that circle. If you change gap or angle, // you will need to adjust this as well to get it going (roughly) // through the centers of each prong. var curveR = 450 var slack = 0.5 fun club() : Shape3d { val profile = SVGParser().parseFile( "jugglingClub.svg" ).shapes["club"] return profile.toOrigin().translateX(0.0001).revolve() .rotateX(90) .translate( 0, 120, 40 ) .previewOnly() } fun prong( doc : SVGDocument ) : Shape3d { val baseSize = doc.shapes["size"].size val foo = doc.shapes["profile"].toOrigin() val profile = foo / Square( baseSize.x, foo.size.y - baseSize.y ).translateY(baseSize.y) val hole = Cylinder( profile.size.y - 10, (rodD+slack)/2 ) return profile.toOrigin().translateX(0.0001).revolve() - hole } fun node( doc : SVGDocument ) : Shape3d { val baseSize = doc.shapes["size"].size val nodeProfile = doc.shapes["profile"].toOrigin() / Square( baseSize ) val node = nodeProfile.toOrigin().translateX(0.0001).revolve() return node } fun back( doc : SVGDocument ) : Shape3d { val node : Shape3d = node(doc) val halfNodes = node.translateX( gap/2 ) + node.translateX( gap ).rotateZ(-angle).translateX( gap/2 ) val nodes = halfNodes.mirrorX().also() val hole = Cylinder( 50, (rodD-slack)/2 ).translateZ(0) val holes = ( hole.translateX( gap/2 ) + hole.translateX( gap ).rotateZ(-angle).translateX( gap/2 ) ).mirrorX().also() val dy = holes.size.y - rodD val path = PolygonBuilder().apply { moveTo( -holes.size.x/2 + rodD/2, -dy ) circularArcTo( holes.size.x/2 - rodD/2, -dy, curveR, false, false ) }.buildPath() val moulding : Shape2d = doc.shapes["moulding"].centerX().toOriginY() val line = moulding.extrude(path.to3d()).mirrorZ() val screwHoles = keyholeHanger( 4, 8 ).mirrorZ() .translate( holes.size.x / 2 -14, -10, 0 ) .mirrorX().also() return nodes + line - holes - screwHoles } val doc = SVGParser().parseFile( "clubsHolder.svg" ) @Piece fun back() = back( doc ) @Piece fun backPiece() : Shape3d { val back : Shape3d = back(doc) - Cube( 40, 10, 4 ).centerX().translateY(4) return (back / back.boundingCube().toOriginX()).toOriginY().translateY(1).mirrorY().also() + Cube( 40-slack, 10-slack, 4-slack ) } @Piece fun backA() : Shape3d { val back : Shape3d = back(doc) - Cube( 40, 10, 4 ).centerX().translateY(4) return (back / back.boundingCube().toOriginX()).rotateZ(45) } @Piece fun backB() : Shape3d { val back : Shape3d = back(doc) - Cube( 40, 10, 4 ).centerX().translateY(4) return (back / back.boundingCube().toOriginX().mirrorX()).rotateZ(45) } @Piece fun connector() = Cube( 40-slack, 10-slack, 4-slack ) @Piece fun prong() = prong( doc ) override fun build() : Shape3d { val prong : Shape3d = prong(doc) val club : Shape3d = club() val back : Shape3d = back(doc) return back.translateZ(-back.size.z) + club + club.rotateZ(-10).translate(90,-10,0) + prong.translateX(gap/2) .mirrorX().also() + prong.translateX(gap).rotateZ(-angle).translateX(gap/2) } }