import static uk.co.nickthecoder.foocad.arrange.v1.Arrange.* import static uk.co.nickthecoder.foocad.circular.v1.Circular.* import static uk.co.nickthecoder.foocad.layout.v1.Layout2d.* import static uk.co.nickthecoder.foocad.layout.v1.Layout3d.* import uk.co.nickthecoder.foocad.threaded.v2.* import uk.co.nickthecoder.foocad.smartextrusion.v1.* import static uk.co.nickthecoder.foocad.smartextrusion.v1.SmartExtrusion.* import uk.co.nickthecoder.foocad.extras.v1.* import static uk.co.nickthecoder.foocad.extras.v1.Extras.* class PokerChipHolder : Model { @Custom( about="Diameter of the tubes (add extra for clearance)" ) var diameter = 41.0 @Custom var chipHeight = 172.0/50 @Custom var chipsPerColumn = 30 @Custom( about="If you want TWO tubes per column within the caddy." ) var extraChipsPerColumn = 0 @Custom var thickness = 3 @Custom var baseThickness = 6 @Custom var accessAngle = 60 @Custom var displayChipCount = true @Custom var threadLength = 6 @Custom var clearance = 0.3 @Custom var capText = "Dealer" var lockHeight = 12 meth thread() : Thread { return Thread( diameter + 1.5, 2 ) .rodChamfer( 1.0 ) } meth handleThread() = Thread( 16, 3 ) meth lockThread() = Thread( diameter - thickness, 3 ).rodChamfer( 1 ) meth packOffset() = diameter+thickness+2 + 2 meth dovetail() : Shape2d { return Trapezium( diameter*0.4, 6 ).angle(30) .mirrorY().centerX().frontTo( -diameter/2 - thickness-3 ) .roundAllCorners(2) } meth tube( chipCount : int ) : Shape3d { val length = chipHeight * chipCount val base = (Circle( diameter/2 + thickness ) + dovetail() ) .smartExtrude( thickness ) .bottom( Chamfer( thickness/3 ) ) val ring = RoundedCircularArc( diameter/2 + thickness, diameter/2, 90+accessAngle/2, 90-accessAngle/2 ) val tubeShape = dovetail() - Circle( diameter/2 ) + ring val tube = tubeShape.extrude( length + threadLength ).bottomTo( base.top ) val thread = thread().threadedHole( threadLength ) .chamferStart(false) //.chamferEnd(false) .topTo( tube.top ) val count = if (displayChipCount) { Text( "$chipCount" ).center().mirrorX() .extrude(0.2) .bottomTo( -0.01 ) } else { null } return base + tube - thread - count } @Piece @Slice( topSolidLayers=6 ) meth tube() = tube( chipsPerColumn ) @Piece @Slice( topSolidLayers=6 ) meth tubeExtra() = tube( extraChipsPerColumn ) @Piece @Slice( topSolidLayers=6 ) meth cap() : Shape3d { val base = Circle( diameter/2 + thickness ) .smartExtrude( thickness ) .bottom( Chamfer( thickness/3 ) ) val thread = thread().threadedRod( threadLength + thickness - 0.5 ) return base + thread } meth labelledCap( label : String ) : Shape3d { val fontSize = if (label.size() <3) 18 else 9 return labelledCap( label, fontSize ) } meth labelledCap( label : String, fontSize : double ) : Shape3d { val plain = cap() val delear = Text(label, fontSize) .hAlign(HAlignment.CENTER) .lineSpacing(0.9) .toPolygon().center().extrude(0.6) .topTo( plain.top + 0.01 ) return plain - delear } @Piece @Slice( topSolidLayers=6 ) meth dealerCap() = labelledCap( capText ) @Piece @Slice( topSolidLayers=6 ) meth handTypeCaps() : Shape3d { val fontSize = 8 return arrangeY( arrangeX( labelledCap("Pair", fontSize), labelledCap("Two\nPair", fontSize), labelledCap("Trips", fontSize), labelledCap("Straight", fontSize) ).centerX(), arrangeX( labelledCap("Flush", fontSize), labelledCap("Full\nHouse", fontSize), labelledCap("Quads", fontSize), labelledCap("Str8\nFlush", fontSize) ).centerX() ) } @Piece meth caddy() : Shape3d { var coreHeight = chipsPerColumn * chipHeight + thickness + threadLength if ( extraChipsPerColumn > 0 ) { coreHeight += extraChipsPerColumn * chipHeight + thickness*2 + threadLength } val baseShape = Circle( diameter *1.75 + thickness + 3 ).sides(6).rotate(30) .roundAllCorners(diameter/2) val base = baseShape .smartExtrude( baseThickness ) .bottom( Chamfer(0.6) ) .top( Chamfer(1) ) val baseIndents = Circle( diameter/2 + thickness - 0.5 ) .translateY( packOffset() ) .smartExtrude( 1 ) .bottom( Chamfer(0.5) ) .topTo( base.top + 0.01 ) .repeatAroundZ(6) val outside = Circle( diameter/2 + thickness + 9 ) .sides(6).roundAllCorners(3) val remove = (Circle( diameter/2 + thickness ) + dovetail()) .offset(clearance).translateY( packOffset() ) .repeatAround(6) val core = ( outside - remove ).extrude( coreHeight ) .bottomTo( base.top ) val forLock = lockThread().threadedRod( lockHeight ) .chamferBottom(false) .bottomTo( core.top ) val holeForHandle = handleThread().threadedHole(20).topTo( forLock.top+0.01 ) val bottomThread = lockThread().threadedHole( lockHeight + 1 ) .chamferEnd(false) val bottomCone = Cylinder( diameter, lockThread().diameter/2, 1 ) .bottomTo( bottomThread.top ) // forces extra perimeters to give move strength - help prevent the core snapping along layer lines. val reinforce = Square(0.6).center() .translateX( diameter/2 + 1 ).translateX(7).also() .extrude(core.top*0.7).bottomTo(0.4) .repeatAroundZ(6) .color("Red") return base - baseIndents + core + forLock - holeForHandle - reinforce - bottomThread - bottomCone } @Piece( about="A mini-caddy for just 2 tubes." ) meth braceCaddy() : Shape3d { var coreHeight = chipsPerColumn * chipHeight + thickness + threadLength val packOffset = diameter/2 + 9 val indentsShape = Circle( diameter/2 + thickness - 0.5 ) .translateY( packOffset ) .mirrorY().also() val postShape = Square( diameter - 10, 16 ).center() .roundAllCorners(3) val baseShape = indentsShape.offset(3) + postShape val base = baseShape .smartExtrude( baseThickness ) .bottom( Chamfer(0.6) ) .top( Chamfer(1) ) val indents = indentsShape .smartExtrude( 1 ) .bottom( Chamfer(0.5) ) .topTo( base.top + 0.01 ) .repeatAroundZ(2) val post = postShape .smartExtrude( coreHeight + base.top ) .edges( Chamfer(0.6) ) val handle = Square( post.size.x, 26 ).center() .roundCorners(listOf(2,3),12.5) .smartExtrude(3) .edges( Fillet(1) ) .rotateX(90).center() .bottomTo( post.top - 3 ) val handleGrip = Circle(8) .smartExtrude(1) .top( ProfileEdge.cove(1).reverse() ) .rotateX(90).center() .frontTo( handle.front - 0.01 ) .topTo( handle.top -4 ) .mirrorY().also() val grooves = (Circle( diameter/2 + thickness ) + dovetail()) .offset(clearance).translateY( packOffset ) .extrude( post.top ) .bottomTo( indents.bottom ) .mirrorY().also() val remove = (Circle( diameter/2 + thickness ) + dovetail()) .offset(clearance) .translateY( packOffset ) .repeatAround(6) val tube = tube().bottomTo( base.top ).translateY(packOffset).previewOnly() return (base - indents) + (post - grooves) + tube + (handle - handleGrip) } @Piece( about="Tests the fit of the interlocking parts (core and tube)" ) meth testCaddy() : Shape3d { val plain = caddy().translateZ( -baseThickness + 1.8 ) val clip = Cube( diameter + thickness*2, diameter*2, plain.top ).centerX() .frontTo( diameter/2-1 ) return plain.intersection( clip ) } @Piece meth handle() : Shape3d { val handleWidth = diameter-10 // Must be small enough to fit inside piece "lock" val slice = 2.5 val thread = handleThread().threadedRod(20) .rotateX(90).bottomTo(-slice) val slicedThread = thread - Cube( 50 ).centerXY().topTo(0) val outside = Square( handleWidth ).centerX() .roundCorners( listOf(2,3), handleWidth/2-0.1 ) .roundCorners( listOf(0,1), 6 ) .frontTo(-3) val handle = (outside - outside.offset(-5) ) .smartExtrude( slicedThread.top + 1 ) .edges( ProfileEdge.roundedChamfer(3) ) return handle + slicedThread } @Piece meth smallLock() : Shape3d { val solid = Circle( packOffset()/2 - 0.5 ) .smartExtrude( lockHeight ) .bottom(Chamfer(1)) val thread = lockThread().threadedHole( lockHeight + clearance ) .topTo( solid.top + 0.01 ) return solid - thread } @Piece meth lock() : Shape3d { val extra = 8 val solid = Circle( packOffset()/2 - 0.5 + extra ) .smartExtrude( lockHeight ) .bottom( ProfileEdge.chamfer(1.5) ) .top( ProfileEdge.step(extra, thickness - clearance) ) val thread = lockThread().threadedHole( lockHeight + clearance ) .topTo( solid.top + 0.01 ) return solid - thread } override meth build() : Shape3d { val singleTube = tube().color("MediumPurple") val tube = if (extraChipsPerColumn == 0) { singleTube } else { singleTube + tubeExtra().bottomTo( singleTube.top + thickness ).color("MediumPurple").darker() } val cap = cap().rotateX(180).topTo( tube.top + thickness ) .color("Orange") val single = (tube + cap).translateY( packOffset() ) val six = single.repeatAroundZ(3) val caddy = caddy().translateZ(-baseThickness).color("Green") //val lock = smallLock().rotateX(180).bottomTo( tube.top ).color( "DarkSeaGreen" ) val lock = lock().rotateX(180).bottomTo( tube.top ).color( "DarkSeaGreen" ) val handle = handle().rotateX(90).centerY().bottomTo( caddy.top - 17 ).color("LightGreen") return six + caddy + handle + lock } }