import uk.co.nickthecoder.foocad.extras.v1.* import static uk.co.nickthecoder.foocad.extras.v1.Extras.* // BEWARE. Many changes made since I last checked that size.x is respected. // doubleSpiral's length has never been tested. class Band : Model { @Custom var size = Vector3(1000, 3, 1.2) @Custom( about="The diameter of the semicircle at the ends of the band" ) var endDiameter = 10 @Custom( about="Distance between each loop of the spiral (Not used for piece `straight`)" ) var spiralGap = 3 @Custom( about="How close to the center of the spiral do we go? (1=Nearest to center)" ) var startLoops = 3 @Piece meth straight() : Shape3d { val start = CircularArc( endDiameter/2, 0, 180 ).thickness( size.z ) val length = (size.x - Math.PI * endDiameter)/2 val middle = Square(size.z , length) .backTo(0) .centerXTo( -endDiameter/2 ) .mirrorX().also() val end = start.mirrorY().translateY(-length) val shape = start + middle + end return shape.extrude( size.y ) } @Piece meth spiral() = spiralShape(size.x, true).extrude( size.y ) @Piece meth doubleSpiral() = spiralShape(size.x/2, false).rotate(180).also().extrude(size.y) meth spiralShape( length : double, withEnd : bool ) : Shape2d { val spiralDelta = spiralGap + endDiameter var loops = startLoops var remaining = length var flip = false var shape : Shape2d = CircularArc( endDiameter/2, 0, 180 ).thickness( size.z ) .translateX( -spiralDelta/2 * (loops-1) ) remaining -= Math.PI * endDiameter * 2 while (remaining > 0) { val r1 = spiralDelta/2 * loops - endDiameter/2 val r2 = spiralDelta/2 * loops + endDiameter/2 val willUse = Math.PI * (r1 + r2) / 2 var toAngle = 180 if (willUse > remaining) { toAngle = 180 * remaining / willUse } val small = CircularArc( r1, -1, toAngle+1 ).thickness( size.z ) val large = CircularArc( r2, -1, toAngle+1 ).thickness( size.z ) if (flip) { shape = shape + (small + large) } else { shape = shape + (small + large).rotate(180).translateX( spiralDelta/2 ) } // Is this the last iteration? if (willUse > remaining) { if (withEnd) { val end = CircularArc( endDiameter/2, -1, 181 ).thickness( size.z ) .translateX( spiralDelta/2 * loops ) .rotate( toAngle ) if (flip) { shape = shape + end } else { shape = shape + end.rotate(180).translateX( spiralDelta/2 ) } } else { // When the end is omitted, position the resulting shape such that the // end is at the origin. This makes piece `doubleSpiral` simple. if (!flip) { shape = shape.translateX( -spiralDelta/2 ).rotate(-180) } shape = shape.rotate(-toAngle).translateX( -spiralDelta/2 * loops ) } } loops += 1 remaining -= willUse flip = !flip } return shape } override fun build() : Shape3d { return spiral() } }