FooCAD Source Codeimport static uk.co.nickthecoder.foocad.chamferedextrude.v1.ChamferedExtrude.*
/**
A tilting stand for a phone.
Can be tilted forwards or backwards, but to tilt forwards, the tall fins
will partially obscure the screen.
3 pieces in PLA : ring, stand, fins
3 pieces in TPU : finRubber (x2), finRubberFront
The TPU pieces could be replaced by felt.
In the default view, the red sphere is the center of rotation.
Ideally, for maximum stability, this should also be the center of gravity of the phone.
Print Notes
===========
`fins` needs a high `fillDensity`.
However, superslicer is still making part of the fins hollow.
As a workaround, `perimeters` = 20 (or solid layers = 10,000)
Either will make a solid print.
*/
class PhoneTiltStand : Model {
@Custom( about="How high up is the center of gravity?" )
var sphereRadius = 80
@Custom
var standHeight = 19
@Custom( about="Outer radius of the ring" )
var ringRadius = 35
@Custom( about="How much smaller is the inner radius compared to ringRadius" )
var ringThickness = 8
@Custom( about="Height of the ring" )
var ringHeight = 8
@Custom( about="The thickness of the plate that the fins sit on" )
var plateThickness = 2
@Custom
var finSize = Vector3( 10, 20, 60 )
@Custom
var finTopThickness = 5
@Custom( about= "X Offset for the outer pair of fins" )
var finOffset = 24
@Custom
var phoneThickness = 11.8
@Custom( about="Thickness of the rubber to protect the phone" )
var backRubberThickness = 1.2
// This can be customised later to adjust for different phone thicknesses.
@Custom( about="Thickness of the front rubber" )
var frontRubberThickness = 1.2
@Custom( about = "Degrees (preview only)" )
val tilt = -20
fun ringProfile() : Shape2d {
val square = Square( ringThickness, ringHeight ).roundAllCorners(1)
.rightTo( ringRadius )
val circle = Circle( sphereRadius ).sides(300)
.frontTo( 1 ) // Ensure the stand doesn't touch the table
return square - circle
}
@Piece
fun ring() : Shape3d {
return ringProfile().revolve().sides(80)
}
fun standProfile() : Shape2d {
val halfCircle = Circle(ringRadius) intersection (Square(ringRadius*3).centerX())
return (
halfCircle.scaleY(1.2) +
halfCircle.mirrorY().scaleY(0.8)
).translateY(8)
}
@Piece
fun stand() : Shape3d {
val sphere = Sphere( sphereRadius ).sides(150)
.topTo( standHeight )
val block = standProfile().extrude( standHeight )
return sphere intersection block
}
fun fin(finSize : Vector3, scale : double) : Shape3d {
val fatFin = Square( finSize.x, finSize.z )
.roundCorner(3,finSize.x/2)
.roundCorner(2,finSize.x/2)
.extrude( finSize.y )
.rotateX(90)
.centerX()
// Cut out a 1/4 cylinder, so that the fin gets progressively shallower towards the top.
val cylinderX = Circle( finSize.z ).sides(80)
.scaleY((finSize.y-finTopThickness) / finSize.z)
.extrude(finSize.x+1, Vector2(scale, 1) ) // Adds an extra curve.
.rotateY(90)
.centerX()
.translateZ( finSize.z )
return (fatFin - cylinderX) //+ cylinderX.previewOnly()
}
@Piece
@Slice
fun fins() : Shape3d {
val base = standProfile().extrude(plateThickness)
val middleFin = fin(Vector3(finSize.x, 10, 10), 1)
.frontTo(phoneThickness /2 + frontRubberThickness)
.mirrorY()
val outerFins = fin(finSize, 1.5)
.frontTo(phoneThickness /2 + backRubberThickness + 1)
.translateX(finOffset)
.mirrorX().also()
return base + (middleFin + outerFins).bottomTo(base.top)
}
fun finRubber(height : double, thickness : double) : Shape3d {
val chamfer = thickness*0.3
return ExtrusionBuilder().apply {
crossSection( Square( finSize.x, height ).centerX()
.roundCorner(3,finSize.x/2)
.roundCorner(2,finSize.x/2)
)
.forward( thickness/2 )
.crossSection( Square( finSize.x, height - chamfer*0.8).centerX()
.roundCorner(3,finSize.x/2)
.roundCorner(2,finSize.x/2)
)
.forward( thickness/2 )
.crossSection( Square( finSize.x, height - chamfer*3).centerX()
.roundCorner(3,finSize.x/2)
.roundCorner(2,finSize.x/2)
)
}.build()
}
@Piece
fun finRubber() = finRubber( finSize.z, backRubberThickness )
@Piece
fun finRubberFront() = finRubber( 10, frontRubberThickness)
override fun build() : Shape3d {
val theStand = stand().mirrorZ().color("Green")
val theFins = fins().color("LightGreen")
val phone = Cube( 170, phoneThickness, 80 ).previewOnly().centerXY().translateZ(1)
val phone2 = Cube( 80, phoneThickness, 170 ).previewOnly().centerXY().translateZ(1)
val centerOfGravity = Sphere(10).translateZ( sphereRadius ).color("red")
val gap = 0.1
val movable = (phone + phone2 + centerOfGravity + theStand + theFins)
.translateZ( - sphereRadius + standHeight+1 + gap)
.rotateX( tilt )
.translateZ( sphereRadius )
return ring() + movable
}
}