import static uk.co.nickthecoder.foocad.layout.v1.Layout2d.* import static uk.co.nickthecoder.foocad.layout.v1.Layout3d.* class LampShade2 : Model { var tubeDiameter = 60 var ledLength = 17//0 var ledWidth = 15.5 var ledThickness = 1.7 var ledCount = 5 var thickness = 1.5 var floorThickness = 3 var extraHeight = 5 var strainThickness = 3 var strainHeight = 10 var strainHeight2 = 20 var strainGripThickness = 2 var cableDiameter = 8 var slack = 0.3 fun strainClip() : Shape3d { val fc = flattenedCircle(ledWidth, tubeDiameter/2-slack-thickness) val profile = fc - fc.offset( -strainThickness ) val ring = profile.extrude( strainHeight ) val foo = tubeDiameter + strainThickness val tie = Cylinder( 20, cableDiameter/2 + strainGripThickness ) val hole = Cylinder( 60, cableDiameter/2 ).center() val removeHalf = Cube( cableDiameter + strainGripThickness* 3, cableDiameter + strainGripThickness* 3, strainHeight2 + 2) .centerX() .translateZ(-1) .translateY( -strainGripThickness ) val tab = PolygonBuilder().apply { moveTo( -foo/2 + 4, 0 ) lineTo( -foo/2 + 4, strainHeight ) bezierTo( Vector2(-foo/2+15, strainHeight), Vector2(-20, strainHeight2), Vector2(-10, strainHeight2) ) radius(4) lineTo( 15, strainHeight2 ) radius(0) lineTo( 15, 0 ) }.build().extrude( strainGripThickness ) .rotateX(90) .translateY(-strainGripThickness) val arm = tie + tab - hole - removeHalf return arm.rotateZ(180).also() + ring } fun flattenedCircle( flat : double, radius : double ) : Shape2d { val angle = Math.PI / ledCount * 2 val point1 = Vector2(radius,flat/2) val point2 = Vector2(radius,-flat/2) return PolygonBuilder().apply { moveTo(point1) for (i in 0 until ledCount) { lineTo(point2.rotate(angle*i)) val next = point1.rotate(angle * (i + 1)) circularArcTo(next.x,next.y, radius,false, false) } }.build() } fun clipProfile() : Shape2d { return Square( ledThickness + thickness * 2, ledWidth + thickness*2 ).centerY() - Square( 100, ledWidth-thickness*2 ).centerY().translateX(thickness) - Square( ledThickness, ledWidth ).centerY().translateX(thickness) } fun end(withGaps : bool ) : Shape3d { val tp = flattenedCircle( ledWidth, tubeDiameter/2 ) val profile = (tp - tp.offset(-thickness)) + clipProfile().translateX(tubeDiameter/2-thickness) .repeatAround( ledCount ) val tube = profile.extrude( ledLength+floorThickness + extraHeight ) val floor = ( flattenedCircle( ledWidth+thickness*2, tubeDiameter/2 + thickness + ledThickness ) - flattenedCircle( ledWidth, tubeDiameter/2 ) ).extrude(floorThickness) return if ( withGaps ) { // Create gaps in the clips, so that wires can be attached to // the led. val gapHeight = 5 val gaps = ( // An angled piece, so that there is no overhang Cube( 10, ledWidth + thickness*2 + 1, 20 ) .centerY() .rotateY(20) .translateX( tubeDiameter/2 ) .translateZ( floorThickness + gapHeight ) + // Completely removed at the base Cube( 10, ledWidth + thickness*2 + 1, gapHeight ) .centerY() .translateX( tubeDiameter/2 ) .translateZ( floorThickness ) + // A hole for the wires Cube( 10, 6, 3 ) .centerXY() .translateZ( floorThickness ) .translateX( tubeDiameter/2 ) .rotateZ( 180/ ledCount ) ).repeatAroundZ( ledCount ) tube - gaps + floor } else { tube + floor } } override fun build() : Shape3d { return end(true) + strainClip().translateZ(30) } }