import uk.co.nickthecoder.foocad.cup.v1.* import static uk.co.nickthecoder.foocad.cup.v1.Cup.* import uk.co.nickthecoder.foocad.threaded.v2.* import uk.co.nickthecoder.foocad.extras.v1.* import static uk.co.nickthecoder.foocad.extras.v1.Extras.* import uk.co.nickthecoder.foocad.smartextrusion.v1.* import static uk.co.nickthecoder.foocad.smartextrusion.v1.SmartExtrusion.* import static uk.co.nickthecoder.foocad.layout.v1.Layout2d.* import static uk.co.nickthecoder.foocad.layout.v1.Layout3d.* class SmallBirdTable : Model { @Custom( about="Upcycled dowels from ice lollies. Form the columns to keep large birds/squirrels out" ) var dowelSize = Vector2( 100, 6 ) @Custom( about="Distance between the columns" ) var columnSpacing = 40 @Custom var roughDiameter = 200 @Custom( about="Width and height of the ring with the columns (yellow)" ) var innerRingSize = Vector2( 10, 8 ) @Custom( about="Width and height of the outside ring (green)") var outerRingSize = Vector2( 8, 14 ) @Custom var baseThickness = 2.0 @Custom var clearance = 0.1 @Custom var pinLength = 160 - dowelSize.x @Custom( about="Radius, depth and thickness of the pots" ) var potSize = Vector3( 60, 30, 1.8 ) @Custom( about="How many pots make up a complete circle" ) var potCount = 3 meth dowelCount() = roughDiameter*Math.PI ~/ columnSpacing /** * The radius of the mid point of the innerRings. */ meth radius() = dowelCount() * columnSpacing / Math.PI / 2 @Piece meth testFit() : Shape3d { val solid = Square( innerRingSize.x ).center().roundAllCorners(2).extrude(innerRingSize.y) val hole = Circle( dowelSize.y / 2 + clearance ).hole() .smartExtrude( innerRingSize.y - 1 ) .top( Chamfer(0.5).reverse() ) .bottomTo( 1.01 ) return solid - hole } @Piece( about="Use wooden dowels instead???" ) meth column() = dowel( dowelSize.x ) @Piece( about="Use wooden dowels instead???" ) meth pin() = dowel( pinLength ) meth dowel( length : double ) : Shape3d { val shape = Circle( dowelSize.y / 2 ) val full = shape.extrude( length ).rotateX(-90).centerXY() return full.bottomTo( -dowelSize.y / 10 ) .remove(Cube( length + 2 ).centerXY().topTo(0)) } @Piece( printable=false ) meth assembly() : Shape3d { val dowelCount = dowelCount() val radius = radius() val ringShape = (Circle( radius + innerRingSize.x/2 ) - Circle( radius - innerRingSize.x/2 )) val columns = Circle( dowelSize.y / 2 ).hole() .extrude( dowelSize.x ) .translateX( radius ) .bottomTo(1) .repeatAroundZ( dowelCount ) val pinHole = Circle( dowelSize.y/2 + clearance ).hole() .extrude( pinLength ) .rotateY(90) .leftTo( radius - innerRingSize.x / 2 + 1 ) .centerZTo( innerRingSize.y / 2 ) val pinHoles = pinHole.repeatAroundZ( 6 ).rotateZ(4) val bottomRing = ( ringShape .smartExtrude( innerRingSize.y ) .top( Chamfer(1) ) - columns - pinHoles ).label( "innerRing") val topRing = bottomRing.mirrorZ() .topTo( columns.top + 2 ) val baseRadius = radius + innerRingSize.x / 2 + clearance*2 val baseRing = (Circle( baseRadius + outerRingSize.x ) - Circle( baseRadius ) ) .smartExtrude( outerRingSize.y ) .top( Fillet(outerRingSize.x/2) ) .bottom( Chamfer(1.5) ) .bottomTo( - baseThickness ) - pinHoles val baseFlat = Circle( radius + 10 ) .extrude( baseThickness ) .topTo( 0.01 ) val base = (baseFlat + baseRing - pinHoles) .color("Green") .label( "base" ) .translateZ( -0.1 ) val plainLid = base.mirrorZ().topTo( topRing.top + baseThickness ) val thread = Thread(10) val threadedHole = thread.threadedHole( 7 ) val reinforceRadius = radius - innerRingSize.x/2 - clearance*2 val connection = Circle( 12 ) .smartExtrude(threadedHole.size.z) .bottom(Chamfer(3) ) .remove( threadedHole ) .topTo( plainLid.top ) .rightTo( reinforceRadius ) val connections = connection .repeatAroundZ(3).withCavities() val reinforcements = ( Ring( reinforceRadius, reinforceRadius - innerRingSize.x ) + Square( radius* 2 - innerRingSize.x - 2, innerRingSize.x ) .center() .roundAllCorners(2) .repeatAround(3) ) .smartExtrude( 4 ) .bottom( Chamfer(1) ) .topTo( plainLid.top - baseThickness ) val lid = (plainLid and reinforcements and connections) .color("Green") .label("lid") val eyeThread = thread.threadedRod( threadedHole.size.z + 1 ) .rotateY(90).bottomTo( -thread.diameter/5 ) val eyeHole = Ring( 6, 2 ) .smartExtrude(eyeThread.top + eyeThread.bottom) .edges( Chamfer(0.5) ) .rightTo(1) val eye = (eyeThread.intersection( Cube( 50 ).centerXY() ) + eyeHole) .color("Orange") .label("eye") val eyes = eye.rotateY(90) .translateX( connections.right - connection.size.x/2 -2 ) .bottomTo( lid.top ) .repeatAroundZ(3) //return lid + topRing return base + bottomRing + topRing + columns.previewOnly() + lid + pinHoles.previewOnly() + eyes } @Piece meth innerRing() = assembly().find("innerRing") @Piece meth eye() = assembly().find("eye") @Piece meth base() = assembly().find("base") @Piece meth lid() = assembly().find("lid").rotateX(180).bottomTo(0) @Piece( about="Optional - Omit a column and replace it with a door" ) meth door() : Shape3d { val doorThickness = 2.2 val radius = radius() val insideRadius = radius + dowelSize.y/2 + 1 val dowelCount = dowelCount() val arc = CircularArc( insideRadius + doorThickness, insideRadius, 0, 360 / dowelCount * 2 + 2 ) val door = arc.extrude( dowelSize.x - (outerRingSize.y-baseThickness) * 2 ) val hingeR = dowelSize.y / 2 + clearance*2 val hingeShape = Circle( hingeR + doorThickness + 1 ) - Circle( hingeR ) val hinge = hingeShape.translateX( radius ) .extrude( door.size.z ) val clip = CircularArc( hingeR + 2, hingeR+0.5, -5, 140 ) .extrude( door.size.z /3 ) .translateX(radius) .rotateZ( 360/dowelCount*2 ) val handle = Trapezium( 20, 10 ).deltaX(10) .smartExtrude(3).edges(Chamfer(1)) .rotateY(90).rotateZ(90).centerY().bottomTo(3).leftTo( insideRadius ) .rotateZ( 360/dowelCount*2 ) val spacer = (Circle( hingeR + 2 ) - Circle( hingeR )) .extrude(outerRingSize.y - innerRingSize.y) .rightTo( hinge.left - 1 ) return door + hinge + clip + spacer + handle } @Piece( print="printPerchJoint", about="Add a horizontal perch between two columns" ) meth perchJoint() : Shape3d { val hingeR = dowelSize.y / 2 + clearance*2 val ringShape = Circle( hingeR + 2 ) remove Circle( hingeR ) val spacer = ringShape .smartExtrude( 30 ) val acrossShape = Square( hingeR*2 + 4, hingeR*2 + 6 ).center() .roundCorner(3, 2, 1) .roundCorner(2, 2, 1) .roundCorner(1, hingeR + 1) .roundCorner(0, hingeR + 1) remove Circle( hingeR - clearance ).safeOverhang().mirrorY() val forPerch = acrossShape .smartExtrude(ringShape.size.x) .edges( Chamfer(0.5) ) .rotateX(90).centerY() .topTo( spacer.top ) .leftTo( hingeR -1.5 ) return spacer and forPerch } @Piece meth printPerchJoint() = perchJoint().mirrorZ().bottomTo(0) @Piece meth pot() : Shape3d { val shape = Sector( potSize.x, 0, 360/potCount ) .roundCorner(-1,5) .roundCorner(1,5) .roundCorner(0,5) val top = ProfileEdge.chamfer(2).reverse() and ProfileEdge.flat(2) and ProfileEdge.chamfer(1) return shape.cup( potSize.y, potSize.z ) .outsideBottom( ProfileEdge.roundedChamfer(3) ) .insideBottom( Fillet(3) ) .insideTop( top ) .outsideTop( Chamfer(0.5) ) } @Piece( printable=false ) override meth build() : Shape3d { val pot = pot() val door = door().translateZ(outerRingSize.y).previewOnly() return assembly() + door + pot } }