Exit Full View
Up

/Games/JugglingClub.foocad

JugglingClub

A high quality Juggling club, with a wooden dowel core (default size is 18mm).

The shape od the club comes from a 2D profile in an svg diagram, which you can edit using the free Inkscape program.

If you want a "basic" clubs, just go out and buy them! The beauty of 3D printing is customisation, so get customising ;-)

Change the length to suit the length of your arms - one size does NOT fit all. Change the shape of the club. Go wild - jazz up the profile into freeky curves! Edit the svg document using inkscape (a free vector drawing program).

And obviously, you can choose your color. If your printer lets you change filament part way through a print, then why not create striped clubs! I suggest not transitioning from one strong colour to another, as the mixed colour may be nasty. Instead add a stripe of white or black between each colour change. If you want consistant clubs, make a note of the times of each change, or print all three clubs at the same time.

I originally split the head into two parts (headA and headB), but I've changed the profile of the club so that "head" can fit on my printer in one part. The "stop" is now longer than it used to be.

It screws together, but all screws are hidden from view. To take the club apart you will need to remove the glued parts "cap" and "endB". If you used CA glue, a sharp knife should work without damaging the parts too much. But I consider these pieces "consumerbes", because they will take lots of knocks.

Pieces

  • one - The whole club in one piece (not recommended)

  • head - The main part of the club. Currently just over 200mm

  • headA - The main part of the club cut in two for those with a low Z height printer

  • headB - The companion to headA

  • end1 - Half of the knob at the handle end of the club. Print this in rubbery TPU

  • end2 - The other half of the knob

  • end3 - Optional final touch to round off the end. Glued in place. The end is in parts, so that each can be printed without overhangs.

  • endWasher - Optional. Only when using the endRing. Printed in PLA.

  • handle - A TPU Tube. Optional - You could wrap the handle with cloth or tape instead.

  • cap - A rounded piece to glue onto the head. Print in rubbery TPU.

  • stop - In the middle of the club. Screws into the wooden dowel that runs the length of the club, but the screw is hidden from view. The other screw is in the top of the head, which must be inserted second, and then the cap glued on to hide the screw.

  • spacer - Optional. Merely decorative.

  • exploded - For documentation purposes only. Shows an exploded view of all the pieces. The head uses only one piece, so headA and headB aren't shown.

  • inspect - For debugging only. A slice through pieces headA and headB to check the join.

Print Notes

Change your slic3 settings so that cap, end, endB, endRing and spacer use TPU material. This may have different temperature values to PLA.

Other slicer settings have been hard coded in "fun slic3rOverrides" at the end of this file. If you slice this manually, look at the recommended slicer settings there.

Construction

Take piece "head" (or fit the "headA" and "headBb" together) and insert the dowel and the "stop".

Mark where the stop fits on the dowel. Remove the head, and screw the stop to the dowel (Drill a pilot hole first)

If you printed the head in two parts headA/headB glue them together with expoy glue. Replace the head, and screw together at the end of the clud (Drill a pilot hole first).

Push the handle into place, and measure and cut the rod to the final length. Note that the rod also goes into "end1".

Place end1, end2 and endWasher, then screw in place (pre drill the dowel). Optionally glue on end3 to round off the end and hide the screw.

Happy juggling. Practice your flairs while printing the next club ;-) For my favourite single clubetrick, search YouTube for : kick up juggling club Practice with both feet. You will have nailed it by the time the 2nd club is printed ;-)

NOTE. If you glue the screws to the end and head pieces, and let it set, then you can unscrew the club without breaking any glue seals. Maybe it will get loose over time though.

FYI, you can use ordinary wood screws, but I used threaded inserts, and machine screws, so that unscrewing and rescrewing doesn't loosen the joint. (If that happens, a wooden match in the hole is usually enough to make it tight again)

FooCAD Source Code
import static uk.co.nickthecoder.foocad.layout.v1.Debug.*
import static uk.co.nickthecoder.foocad.layout.v1.Layout2d.*
import static uk.co.nickthecoder.foocad.layout.v1.Layout3d.*
import static uk.co.nickthecoder.foocad.labelled.v1.Labelled.*

class JugglingClub : Model {
    
    // The wall thickness. Ensure you have enough "perimeters" in your slicer
    // so that it doesn't infill (unless that is what you want!)
    var thickness = 1.9

    // The total length of the club.
    var length = 420

    // The height of lip forming the joins between the sections.
    var join = 10

    // The gap between the two parts where they join.
    // Use epoxy resin at the join, so the slack shouldn't be too minimal.
    // Also not that the joint
    @Custom
    var slack = 0.3

    var piece = "one"

    // The thickness of the base
    @Custom
    var baseThickness = 8.0

    @Custom
    var stopLength = 60

    @Custom
    var spacer = 8

    @Custom
    var handleLength = 200

    // Use this to print the handle in multiple parts - if your printer is too short
    // or the handle is too unstable to print in one length.
    @Custom
    var handlePieces = 1

    // The is the size of the hole. Include some slack by making it slightly
    // larger than you rod's actual diameter.
    @Custom
    var rodDiameter = 18.6 // For an 18mm dowel.

    @Custom
    // Number of sides for the revolution.
    var sides = 90

    val svgFile = "jugglingClub.svg" 
    var doc = SVGParser().parseFile( svgFile )

    @Piece
    fun onePiece() : Shape3d {
        val profile = doc.shapes["club"].toOrigin().translateX(0.001)
        val inside = profile.offset(-thickness)

        val hollow = (profile - inside).revolve().sides(sides) -
            Cylinder( inside.size.y, thickness*2 ).translateZ(thickness)

        val base = (profile / Square( profile.size.x + 2, baseThickness ))
                .revolve().sides(sides) -
            Cylinder.hole( baseThickness, rodDiameter/2 ).translateZ(baseThickness*0.7)

        return hollow + base
    }
    
    @Piece
    fun head() : Shape3d {
        val y = doc.shapes["a"].size.y + doc.shapes["b"].size.y
        val x = Math.max(doc.shapes["a"].size.x, doc.shapes["b"].size.x)

        val main = onePiece() / Cube( x, x, y ).centerXY()

        return main
    }

    @Piece
    fun headA() : Shape3d {
        val size = doc.shapes["a"].size
        val main = onePiece() / Cube( size.x, size.x, size.y ).centerXY()
        val radius = main.size.x/2
        // Causes the join to tilt inwards slightly, which fits the profile of
        // part "b" better.
        val tilt = 1.3

        // Create a "lip" for part "b" to fit around. The end is tapered,
        // and there is "slack" (the lip is smaller than part "b"'s inside) 
        // and there's a chamfer so that there is no overhang.
        val lip = PolygonBuilder().apply {

            moveTo(0, -thickness*2-slack)
            lineTo(0, 0)
            lineTo(-slack,0)
            lineTo(-slack*tilt, join*0.7)
            lineTo(-slack-thickness/2, join)
            lineTo(-slack*tilt-thickness,join)
            lineTo(-slack-thickness,-thickness)
        }.build().translate(radius - thickness,size.y).revolve().sides(sides)


        val screwHole = Cylinder( 100, 1.5 ).center() +
            Cylinder( 5, 5, 0)

        return main + lip - screwHole
    }

    @Piece
    fun headB() : Shape3d {
        val sizeA = doc.shapes["a"].size
        val sizeB = doc.shapes["b"].size
        val main = onePiece().translateZ(-sizeA.y) / (Cube( sizeB.x, sizeB.x, sizeB.y ).centerXY())

        return main
    }

    @Piece
    fun handle() : Shape3d {
        val radius = doc.shapes["handle"].size.x
        return (
            Circle( radius ).sides(60) -
            Circle( rodDiameter / 2 ).sides(40)
        ).extrude( handleLength / handlePieces )
    }

    @Piece
    fun spacer() : Shape3d {
        val radius = doc.shapes["handle"].size.x
        return (
            Circle( radius ).sides(60) -
            Circle( rodDiameter / 2 ).sides(40)
        ).extrude( spacer )

    }

    @Piece
    fun cap() : Shape3d {
        val profile = doc.shapes["cap"].mirrorY().toOrigin().translateX(0.001)

        val cap = profile.revolve().sides(sides)
        val hole = Cylinder( 3, 4 )
        return cap - hole
    }


    var washerHeight = 8
    var washerDiameter = 6

    @Piece
    fun end1() : Shape3d {
        val profile = doc.shapes["club"]
        val mask = doc.shapes["end1"]
        val solid = (profile / mask).mirrorY().toOrigin().translateX(0.001).revolve().sides(sides)
        val hole = Cylinder.hole( solid.size.z, rodDiameter/2 ).translateZ( washerHeight/2 )
        val washerHole = Cylinder( mask.size.y, washerDiameter/2 + 0.25 ).translateZ( -0.01 )
        return solid - hole - washerHole
    }

    @Piece
    fun end2() : Shape3d {
        val profile = doc.shapes["club"]
        val mask = doc.shapes["end2"]
        val solid = (profile / mask).mirrorY().toOrigin().translateX(0.001).revolve().sides(sides)
        val hole = Cylinder.hole( mask.size.y - washerHeight/2, rodDiameter/2 ).translateZ( -0.01 )
        val washerHole = Cylinder( mask.size.y, washerDiameter/2 + 0.25 ).translateZ( 0.01 )

        return (solid - hole - washerHole).mirrorZ().toOriginZ()
    }

    @Piece
    fun end3() : Shape3d {
        val profile = doc.shapes["club"]
        val mask = doc.shapes["end3"]
        val solid = (profile / mask).toOrigin().translateX(0.001).revolve().sides(sides)

        return solid
    }

    // The endWasher goes inside "end1" and "end2" so that when you screw in the screw,
    // the endWasher is pushed firmly against the wooden rod without squashing
    // the TPU of the end piece.
    @Piece
    fun endWasher() : Shape3d {
        val baseT = 3
        return (
            Cylinder( baseT, rodDiameter/2 ).sides(40) + // Wide part
            Cylinder( washerHeight, washerDiameter/2 ).sides(12).translateZ(baseT) - // Tube
            Cylinder( 5, 5, 0 ).sides(20).translateZ(-0.01) -  // ChamferedHead
            Cylinder( 50, 2 ).sides(12) // Hole for screw
        )
    }

    @Piece
    fun stop( ) : Shape3d {
        var profile = doc.shapes["stop"].mirrorY().toOrigin().translateX(0.001)
        profile = profile.scale(1, stopLength / profile.size.y )

        val lip = Cylinder( join, profile.size.x-thickness-slack ).translateZ(profile.size.y)
        val hole = Cylinder( 300, rodDiameter/2 + slack ).center()
        val grubHoles = (
            Cylinder( 10, 1.5 ).center().rotateY(90) +
            Cylinder( 5, 5, 0 ).rotateY(90).translateX(-lip.size.x/2)
        ).translateZ(profile.size.y + join/2).repeatAroundZ(3)

        return profile.revolve().sides(sides) + lip - hole - grubHoles
    }

    @Piece
    fun exploded() : Shape3d {

        // The "standard" configuration, with a single piece handle,
        // and single piece head.
        // Also shown are the optional pieces "spacer" and "end3"
            
        handlePieces = 1
        val normal = layoutZ(
            2,
            end3().mirrorZ().toOriginZ().color("DimGray"),
            endWasher().color("Purple"),
            end2().mirrorZ().color("DimGray"),
            end1().color("DimGray"),
            handle().tileZ(handlePieces,2),
            spacer().color("DimGray"),
            stop().color("Purple"),
            head().mirrorZ().color("Purple"),
            cap().color("DimGray")
        )

        // Head in twp parts. Handle in 3 parts. No "end3" nor "spacer".

        handlePieces = 3
        val twoPiece = layoutZ(
            2,
            endWasher().color("Purple"),
            end2().mirrorZ().color("DimGray"),
            end1().color("DimGray"),
            handle().tileZ(handlePieces,2),
            stop().color("Purple"),
            headB().mirrorZ().color("Purple"),
            headA().mirrorZ().color("Purple"),
            cap().color("DimGray")
        )

        return onePiece().translateX(-200) + normal + twoPiece.translateX( 200 )

    }

    @Piece
    fun inspect() : Shape3d {
        val headA : Shape3d = headA() 
        val all = headA + headB().translateZ(93) + stop().mirrorZ().translateZ(268)
        return all / Cube( 100, 1, length*2 ).center()
    }

    override fun build() = onePiece()
    
}