/Furniture/LightShadePineCone.foocad

Inspired by : https://www.youtube.com/watch?v=zluFU6wmua4
Unfinished. See script for details.
I liked the look of this lamp shape, but I wanted it much smaller. It's not the kind of model that can be easily scaled down, so I redesigned it from scratch. However, after I spent an hour or so creating this, I realised I didn't like it! It only looks good when the at eye level (or below). I looks naff when seen from below. YMMV.
This version is highly parametric, although the position, sizes and angles of the "petals" cannot be changed from the "Customiser" tab, but should be easy to do by changing the data near the top of the script.
import static uk.co.nickthecoder.foocad.layout.v1.Layout2d.* import static uk.co.nickthecoder.foocad.layout.v1.Layout3d.* import uk.co.nickthecoder.foocad.extras.v1.* import static uk.co.nickthecoder.foocad.extras.v1.Extras.* /** TODO. Cut slots in the petals and the ribs. Cut slots for the ribs to join with the top/bottom pieces. Add a connection for the light. Tinker with the petalData. */ class LightShadePineCone : Model { // Each line below represents 12 "petals". The three numbers are : // 1. Position of the petal as an angle in degrees (like lines of latitude) // 2. The tilt of the petal (0 = horizontal, 90 = straight down) // 3. The size of the petal (in bizare units that aren't important - tweak values by eye!) // The size isn't in millimeters to make it easy to change the overall size of the // light shade without changing the data. val petalData = listOf<Petal>( Petal( -58, 10, 30 ), Petal( -50, 16, 36 ), Petal( -40, 20, 46 ), Petal( -30, 30, 56 ), Petal( -15, 40, 60 ), Petal( 0, 45, 60 ), Petal( 15, 55, 60 ), Petal( 30, 65, 50 ), Petal( 40, 80, 40 ) ) @Custom( about="The diameter of the sphere which forms the core of the lamp" ) var diameter = 200 @Custom var topAngle = -65 @Custom var bottomAngle = 60 @Custom var ribSize = Vector2( 20, 2 ) @Custom var petalThickness = 1.2 @Custom var ribCount = 24 meth topPoint() = Vector2( diameter/2, 0 ).rotateDegrees( topAngle ) meth bottomPoint() = Vector2( diameter/2, 0 ).rotateDegrees( bottomAngle ) @Piece meth top() : Shape3d { val topPoint = topPoint() val shape = Circle( topPoint.x ) - Circle(topPoint.x - ribSize.x ) return shape.extrude( ribSize.y ) } @Piece meth bottom() : Shape3d { val bottomPoint = bottomPoint() val shape = Circle( bottomPoint.x + ribSize.x/2 ) - Circle(bottomPoint.x - ribSize.x/2 ) return shape.extrude( ribSize.y ) } @Piece meth rib() : Shape3d { val topPoint = topPoint() val bottomPoint = bottomPoint() val shape = PolygonBuilder().apply { moveTo( topPoint + Vector2( 0, ribSize.x ) ) lineTo( topPoint ) circularArcTo( bottomPoint, diameter/2, false, false ) }.buildPath().thickness( ribSize.x ) return shape.extrude( ribSize.y ) } meth petalShape( nominalSize : double ) : Shape2d { val size = diameter/2 * nominalSize/100 val foo = size*0.1 val bar = size*0.4 val baz = size*0.4 val x = size * 1.3 return PolygonBuilder().apply { moveTo( size/2, -size/2 ) circularArcTo( size/2, size/2, size/2, true, false ) bezierByTo( Vector2( baz, 0 ), Vector2( bar, -foo ), Vector2( x, 0 ) ) bezierByTo( Vector2( -bar, -foo ), Vector2( -baz, 0 ), Vector2( size/2, -size/2 ) ) }.build() } @Piece meth petal() = petal( petalData[0].size ) meth petal( size : double ) = petalShape( size ).extrude( petalThickness ) @Piece( printable=false ) override fun build() : Shape3d { val topPoint = topPoint() val bottomPoint = bottomPoint() val ribs = rib() .centerZ() .rotateX(90) .repeatAroundZ( 24 ) val top = top() .topTo( topPoint.y + ribSize.x*0.75 ) .translateZ( -ribSize.x/2 ).also() val bottom = bottom() .bottomTo( bottomPoint.y + ribSize.x/2 ) var petals : Shape3d = Union3d() var extraRotation = 0 for ( petal in petalData ) { petals += petal( petal.size ) // Tilt the petal .rotateY( petal.tilt - petal.around ) // Move to the equator .translateX( diameter/2 + ribSize.x/2 ) // Move to the correct latitude .rotateY( petal.around ) .translateX( - ribSize.x/2 ) // Make copies all the way round // Comment out this line to see only 1 petal of each size. .repeatAroundZ( ribCount/2 ) // alternate between odd and even ribs .rotateZ( extraRotation ) .color( "GhostWhite") // alternate between odd and even ribs extraRotation = 360/ribCount - extraRotation } return ribs + top + bottom + petals } } class Petal( val around : double, val tilt : double, val size : double ) { }