import static uk.co.nickthecoder.foocad.chamferedextrude.v1.ChamferedExtrude.* import static uk.co.nickthecoder.foocad.layout.v1.Layout2d.* import static uk.co.nickthecoder.foocad.layout.v1.Layout3d.* class BirdFeeder : Model{ @Custom( about="The internal width of the tube" ) var diameter = 50 @Custom( about="The height of the tube" ) var height = 120 @Custom( about="The radius of the tube's corners" ) var radius = 10 @Custom( about="Which way do the hanging holes face?") var sideways = false var thickness = 0.8 var rim = 2 var rimE = 2 var holeD = 20 var holeHeight = 7 + 37 // 37 + pegZ @Custom( about="The amount the perch sticks out" ) var perchOut = 30 var perchD = 6 var perchZ = 7 var innerT = 1.4 @Custom( about="How easily the parts fit together" ) var slack = 0.3 fun profile() = Square( diameter ).center().roundAllCorners(radius) fun plainTube() : Shape3d { val outside = profile().offset(thickness) return ExtrusionBuilder().apply { crossSection( outside.offset(rim) ) forward(rimE) crossSection() forward(rim*2) crossSection( outside ) forward( height - rim*4 -rimE*2 ) crossSection() forward(rim*2) crossSection( outside.offset(rim) ) forward(rimE) crossSection() crossSection( outside.offset(-thickness) ) forward( -height ) crossSection() }.buildClosed() .bottomTo( thickness ) } fun hangingHole() : Shape3d { return Square( 10, 10 ).roundAllCorners(1,3).extrude(100) .center() .rotateY(90) .rotateZ( if (sideways) 90 else 0 ) } /** A (transpent) tube where the seeds are stored. There is a rim at both ends, to add extra strength, without having to make the tube thick in the middle. */ @Piece fun tube() : Shape3d { val tube = plainTube() val hangingHole : Shape3d = hangingHole().topTo(tube.top-10) return (tube - hangingHole - feedingHole() - perchHole() ) .color("White").bottomTo(0) } @Piece fun gromet() : Shape3d { val inside = Square( 10 - slack*2 ).center() val main = (inside.roundAllCorners(2) - Circle(2)).extrude(5) val flat = (inside.offset(2).roundAllCorners(4) - Circle(2)).extrude(0.8) return (flat + main).color("Green") } @Piece fun base() : Shape3d { val profile : Shape2d= profile() val pegHole = perchHole() val height = holeHeight - holeD/2+3 val mainHeight = perchZ+perchD val mid = Square( diameter-slack*2 - radius*2, diameter-slack*2 ).center() val top = Square( 5, diameter-slack*2 ).center() val base = ExtrusionBuilder().apply { crossSection( profile.offset(rim+thickness) ) forward(thickness) crossSection() crossSection( profile.offset(-slack) ) forward( mainHeight-thickness ) crossSection() }.build() - mid.offset(-innerT).extrude( height ).bottomTo(-1) val wedge = ExtrusionBuilder().apply { forward( height - innerT ) crossSection( top.offset(-innerT) ) forward( -height + mainHeight ) crossSection( mid.offset(-innerT) ) crossSection( mid ) forward( height - mainHeight +innerT) crossSection( top ) }.build() return (base + wedge- pegHole).color("Green") } fun feedingHole() : Shape3d { val radius = holeD/2 + innerT + slack // Make the hole easy to print, by making the top angled, then flat // so that the overhang is small, then a bridge. val profile = Hull2d( Circle( radius ), Square(radius).centerY().rightTo(0) ) return profile.extrude( 100 ).centerZ().rotateY(90).centerZTo( holeHeight ) } fun perchProfile() = Square( perchD ).center().roundAllCorners(2,1) fun perchHole() : Shape3d { return perchProfile().extrude( 100 ).center().rotateY(90).centerZTo(perchZ) } @Piece fun perch() : Shape3d { return perchProfile().offset(-slack).chamferedExtrude( diameter + perchOut * 2, 0.6 ) .rotateY(90).bottomTo(0) .color("Green") } @Piece fun front() : Shape3d { val outerR = holeD/2 + innerT val shape = Circle( outerR + 3 ) .centerXTo( holeHeight ) val front = shape.extrude( innerT ) val a = Cylinder( diameter/2 + thickness + innerT - slack, outerR ) .centerXTo( holeHeight ) val b = Cylinder( a.size.z, holeD/2 ) .centerXTo( holeHeight ) .topTo( a.top - innerT*2 ) val cutAway = Cube( holeD, holeD, holeD*2 ).centerXY() .bottomTo( innerT ).rightTo( holeHeight - holeD*0.3 ) val hole = Cylinder( diameter, 1 ).centerXTo( holeHeight ) .bottomTo( b.top + innerT ) return (front + a - b - cutAway - hole).color("Green") } @Piece fun lid() : Shape3d { val profile : Shape2d= profile().offset( 12 ) val lid = ExtrusionBuilder().apply { crossSection( profile ) forward( 3 ) crossSection() for ( i in 0..10) { forward( 1 ) crossSection( profile.offset( -10 + 10 * Degrees.sin( 90 -i * 7))) } forward( diameter*0.3 ) crossSection( profile.scale(0.05, 0.35) ) }.build() val gSlack = slack + 1 val groove = (profile().offset(rim+thickness+gSlack) - profile().offset(-gSlack)) .extrude( rimE * 2) val rodHole = Cylinder( 100, 2 ).center().rightTo( - diameter/2-5 ) .mirrorX().also() return (lid - groove - rodHole).color("Green") } @Piece fun allBarTube() : Shape3d { val fronts = front().tileX(2,2).toOrigin() val gromets = gromet().tileX(2,2).toOrigin() val lid : Shape3d = lid().frontTo(0).toOrigin() val base : Shape3d = base().toOrigin() val perch : Shape3d = perch() val perchWithEars = perch + Cylinder( 0.2, 10 ).centerXTo(perch.left) .centerXTo(perch.right).also() val y1 = layoutY(2, lid, gromets.rightTo(lid.right) ) val y2 = layoutY(2, base, fronts ) val x = layoutX(2, y1, y2).leftTo(0) return layoutY(-2, x, perchWithEars.leftTo(0) ) } override fun build() : Shape3d { val tube : Shape3d = tube().bottomTo(thickness)//.previewOnly() val base : Shape3d = base() val perch : Shape3d = perch().centerZTo(perchZ+slack).centerX() val front : Shape3d = front().rotateY(-90).rightTo( diameter/2 + thickness + innerT ) //.mirrorX().also() val lid : Shape3d = lid().bottomTo( tube.top + 0 ) val gromet : Shape3d = gromet().rotateY(-90).topTo( tube.top - 8 ).rightTo(diameter/2+thickness*2) //.mirrorX().also() return tube + front + base + lid + gromet + perch } }