import 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 } }