Exit Full View
Up

/Bathroom/ToothbrushHolder.foocad

ToothbrushHolder

A wall-mounted toothbrush holder for 4 toothbrushes and toothpaste. Each of the 5 holders are detachable for easy cleaning.

Similar shop-bought items do not have the long tubes, which is a problem. Either the holes are too small for the brush handle, or the toobrush head touches the holder (yuk!).

Each tube has a small hole at the bottom for drainage.

Print Notes

Due to the height of the tubes, a large brim is recommended.

Use the customiser to print shorter tubes for child toothbrushes.

Improvements

Make the toothpaste insert slightly wider.

FooCAD Source Code
class ToothbrushHolder : Model {
    
    @Custom
    var insertHeight = 125

    var insertWidth = 17
    var insertThickness = 1
    
    var pasteHeight = 60
    
    var pasteR = 20
    var pasteX = 20

    var lipHeight = 2
    var lipWidth = 5

    var bracketThickness = 8
    var bracketTaper = 3
    val offset = 5

    val slack = 0.5

    var piece = ""

    fun bushing( size : double ) : Shape3d {
        val solid = Cylinder( bracketThickness, insertWidth/2 + insertThickness) +
            Cylinder( lipHeight, insertWidth/2 + insertThickness + lipWidth)
                .color("Green")

        val hole = Cylinder( 30, size / 2 ).center()

        return solid - hole
    }

    fun displayBrushInsert() : Shape3d {
        val solid = Sphere( insertWidth/2 + insertThickness ) +
            Cylinder( insertHeight, insertWidth/2 + insertThickness) +
            Cylinder( lipHeight, insertWidth/2 + insertThickness + lipWidth)
                .translateZ( insertHeight )
                .color("Green")

        val inside = Sphere( insertWidth/2) +
            Cylinder( insertHeight+10, insertWidth/2 )

        // The hole makes the print worse, so I prefer to drill it out manually
        // val drainage = Cylinder( 200, 2 ).center()

        // translate the inside up a little, so that the bottom is thicker
        // and therefore it is more likely to be well printed
        return solid - inside.translateZ(1)
    }

    fun pasteInsert() : Shape3d {
        
        val solid = Sphere( pasteR+insertThickness ).hull( Sphere(pasteR+insertThickness).translateX(pasteX) ).center() +
            Circle( pasteR+insertThickness ).hull( Circle( pasteR+insertThickness ).translateX(pasteX) ).center()
                .extrude( pasteHeight ) +
            Circle( pasteR+insertThickness + lipWidth ).hull( Circle( pasteR + insertThickness + lipWidth ).translateX(pasteX) ).center()
                .extrude( lipHeight )
                .translateZ( pasteHeight ).color("Green")
        val inside =
            (Sphere( pasteR ).hull( Sphere(pasteR).translateX(pasteX) ).centerX()) +
            (Cylinder( pasteHeight+10, pasteR ).hull( Cylinder( pasteHeight+10, pasteR ).translateX(pasteX) ).centerX())

        // The hole makes the print worse, so I prefer to drill it out manually
        // val drainage = Cylinder( 200, 2 ).translateX(pasteX/2).mirrorX().also().center()

        // translate the inside up a little, so that the bottom is thicker
        // and therefore it is more likely to be well printed
        return solid - inside.translateZ(1) //- drainage
    }

    fun displayBracket() : Shape3d {

        val brushP = Circle(insertWidth/2 + insertThickness + offset)

        val pasteHole = Circle( pasteR + insertThickness + slack ).hull(
                Circle( pasteR + insertThickness + slack ).translateX(pasteX)
            ).center()

        val profile = pasteHole.offset( offset ) +
             layoutBrushes( brushP.hull( brushP.translateX(40) ) )

        val hole = Circle( insertWidth/2 + insertThickness + slack )
            .translateX(40)

        val holes = layoutBrushes(hole) + pasteHole

        val solid = ExtrusionBuilder.drum(
            profile.offset(-bracketTaper),
            profile,
            bracketThickness
        )

        val mount = Square( 38, 30 )
            .roundAllCorners( 4 )
            .centerX()
            .extrude( 10 )
            .rotateX(90)
            .translateY( pasteR + offset + insertThickness )
            .translateZ( -30 + bracketThickness )


        val screwHole = (
            Cylinder( 50, 2 ) + (Cylinder( 10, 2, 10 ).mirrorZ().translateZ(4))
        ).rotateX(-90).translateY( pasteR + offset + insertThickness -8)
            .translateZ(-6)

        return mount + solid -
            holes.extrude( 100 ).centerZ() -
            screwHole.translateX(10).mirrorX().also()
    }

    fun layoutBrushes( shape : Shape3d ) : Shape3d {
        return shape
            .translateX(11).rotateZ(-20)
            .mirrorX().also() +
        shape
            .translateX(5).rotateZ(-65)
            .mirrorX().also()
    }

    fun layoutBrushes( shape : Shape2d ) : Shape2d {
        return shape
            .translateX(11).rotate(-20)
            .mirrorX().also() +
        shape
            .translateX(5).rotate(-65)
            .mirrorX().also()
    }


    override fun pieceNames() = listOf<String>(  "paste", "brush", "bushing", "all" )

    @Piece
    fun bracket() = displayBracket().mirrorZ().toOriginZ()

    @Piece
    fun brushInsert() = displayBrushInsert().mirrorZ().toOriginZ()

    @Piece
    fun bushing() = bushing( 13 )

    @Piece
    fun bushings() = bushing( 13 ) +
            bushing( 14 ).translateX( 30 ) +
            bushing( 15 ).translateY( 30 ) +
            bushing( 12 ).translate( 30, 30, 0 )

    @Piece
    fun paste() = pasteInsert().mirrorZ().toOriginZ()

    override fun build() : Shape3d {
        return displayBracket().color("Blue") +
        pasteInsert()
            .translateZ(-pasteHeight + bracketThickness + lipHeight ) +          
        layoutBrushes( displayBrushInsert().translateX(40) )
            .translateZ(-insertHeight + bracketThickness + lipHeight )
    }
}