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( -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 ) { }