/Garden/WateringCanSpout.foocad
My spout has degraded in the sun, but the body is still looking good. 3D printer to save a little plastic ;-)
This version comes in two parts, which should be glued together. See version 2, for a single part version (which may be trickier to print on plate slinger printers due to height and small bed contact).
NOTE. I've changed this script since printing it. Check that the rose will fit correctly!
import uk.co.nickthecoder.foocad.threaded.v1.Threaded class WateringCanSpout : Model { @Custom( about="Diameter of the end, where the rose attaches" ) var endD = 23.5 @Custom( about="Length of the slightly tapered region where the rose attaches" ) var endL = 20.0 @Custom( about="Internal diameter of the tube at the can end" ) var midD = 24 @Custom( about="Length of the shaft, not including the threaded part or the rose attachment" ) var length = 180 @Custom( about="Thread depth" ) var threadDepth = 1.0 @Custom( about="Thread pitch" ) var threadPitch = 5.0 @Custom( about="Thread diameter" ) var threadD = 39.5 @Custom( about="Thickness of all the parts" ) var thickness = 1.2 @Custom( about="Length of the threaded portion" ) var bodyL = 23 val shoulder = 10 val taper = 1.0 @Piece fun tube() : Shape3d { val bodyExternalR = threadD / 2 + thickness + threadDepth val stop = (Circle( bodyExternalR ) - Circle( midD / 2 + thickness ) ).extrude( thickness ) val tube = ExtrusionBuilder().apply { crossSection( Circle( bodyExternalR ) ) forward( shoulder ) crossSection( Circle( midD /2 + thickness*2 ) ) forward( length - shoulder ) crossSection( Circle( endD/2 )) forward( endL ) crossSection( -taper ) crossSection( - thickness ) forward( -endL ) crossSection( taper ) forward( -length + shoulder) crossSection( Circle( midD /2 + thickness ) ) forward( -shoulder ) crossSection() }.buildClosed().bottomTo( stop.top ) return tube + stop } @Piece fun body() : Shape3d { val bodyExternalR = threadD / 2 + thickness + threadDepth val threaded = Threaded.create( threadDepth, threadD, threadPitch ).apply { nutProfile = Circle( bodyExternalR ) nutChamfer = 0 } val rubberT = 4 // the size of the rubber washer val stop = (Circle( bodyExternalR ) - Circle( midD / 2 + thickness ) ).extrude( thickness ) val thread = ( threaded.nut( bodyL-rubberT) ).bottomTo( stop.top ) return thread + stop } override fun build() : Shape3d { val tube : Shape3d = tube() val body : Shape3d = body().mirrorZ() return tube + body } }