import static uk.co.nickthecoder.foocad.layout.v1.Layout2d.* import static uk.co.nickthecoder.foocad.layout.v1.Layout3d.* class LightShadeCylindrical : Model { func curved(x : double, y : double) = PolygonBuilder().apply { moveTo( -1, 0) lineTo( x, 0 ) bezierBy( Vector2( 0, y/2 ), Vector2( 0, y/2 ), Vector2(-x, y) ) lineTo( -1, y ) }.build().mirrorY().also() static val ribbed = Circle( 10 ) static val jagged = Square( 10 ).rotate(45).center() static val curved = curved( 3, 5 ) static val flat = Square(5).center() @Custom( about="The diameter of the hole for the fixture" ) var fixtureDiameter = 29 var ripple : Shape2d = flat @Custom var piece1Size = Vector2( 100, 160 ) @Custom var piece2Size = Vector2( 170, 110 ) @Custom var piece3Size : Vector2 = Vector2( 200, 100 ) @Custom var piece2Offset = 35 @Custom var piece3Offset = -20 val stepSize = 5 @Piece meth piece1() = solid( piece1Size, 1 ) @Piece meth piece2() = solid( piece2Size, 2 ) @Piece meth piece3() = solid( piece3Size, 3 ) override fun build() : Shape3d { val a = piece1().mirrorZ().color("Red") val b = piece2().mirrorZ().translateZ(-piece2Offset).color("Orange") val c = if (piece3Size == null) { Cube(0) } else { piece3().mirrorZ().translateZ(-piece2Offset - piece3Offset) } return a + b + c } meth solid( size : Vector2, pieceNumber : int ) : Shape3d { val main = if ( ripple == null ) { Circle( size.x/2 ).extrude( size.y ) } else { rippleExtrude( size.x/2, size.y, ripple, pieceNumber ) } val holeRadius = if (pieceNumber == 1 ) { fixtureDiameter/2 } else if (pieceNumber == 3) { if (piece3Offset > 0) { piece2Size.x/2 + 1 - stepSize } else { piece1Size.x/2 + 1 - (pieceNumber-1) * stepSize } } else { piece1Size.x/2 +1 - (pieceNumber-1) * stepSize } val hole = Circle( holeRadius ).extrude( 300 ).translateZ(-1) println( "Piece $pieceNumber Hole radius = $holeRadius" ) return main - hole } fun rippleExtrude( radius : double, height : double, ripple : Shape2d, pieceNumber : int ) : Shape3d { val count = height ~/ ripple.size.y val actualRippleHeight = height / count val actualRipple = ripple.scaleY( height / (ripple.size.y * count) ).frontTo(0) val profile = Square( radius - ripple.right, actualRipple.size.y ) + actualRipple.rightTo( radius ) val completeProfile = profile.tileY( count ).toPolygon() val steppedProfile = if (pieceNumber == 1) { val first = completeProfile.transform( StepTransform2d( piece2Offset, stepSize ) ) if (piece3Offset < 0 && piece3Size != null) { first.transform( StepTransform2d( piece2Offset + piece3Offset, stepSize ) ) } else { first } } else if (pieceNumber == 2 && (piece3Size != null && piece3Offset > 0) ) { completeProfile.transform( StepTransform2d( piece3Offset, stepSize ) ) } else { completeProfile } val stack = steppedProfile.revolve() return stack } } class StepTransform2d( val y : double, val delta : double ) : Transform2d { override meth transform(from: Vector2): Vector2 { val fromStep = y + delta - from.y if ( fromStep < 0 || from.x < delta ) return from val d = Math.min( delta, fromStep ) return Vector2( from.x - d, from.y ) } }