Exit Full View
Up

/Misc/WireTwistTidy.foocad

WireTwistTidy

Insert a cable half way along its length, then twist to gather it together without knotting. Pull the two ends to unwind.

FooCAD Source Code
import static uk.co.nickthecoder.foocad.arrange.v1.Arrange.*
import static uk.co.nickthecoder.foocad.chamferedextrude.v1.ChamferedExtrude.*

class WireTwistTidy : Model {

    var size = Vector2( 50, 10 )
    var thickness = 2
    var innerThickness = 1
    var lipThickness = 1.5
    var clearance = 0.3
    var slack = 0.9
    var recess = 6
    val wireD = 3
    var overhang = 3

    fun yin(radius : double) : Shape2d {

        val part = PolygonBuilder().apply {
            moveTo( -radius, 0 )
            lineTo( -radius, wireD / 2 )
            circularArcTo( Vector2( -wireD/2, radius ), radius, false, false )
            lineTo( -wireD/2, -radius * 0.3 )
            bezierTo( Vector2( 0, -radius ), Vector2( -radius, -radius*0.9 ), Vector2( -radius, 0 ) )
        }.build()

        val topSemi = Circle( radius ) intersection Square( radius *2, radius ).centerX()
        val foo = part + topSemi - part.rotate(180).offset( wireD )

        return foo
    }

    @Piece
    fun inner() : Shape3d {

        val radius = size.x / 2 - recess

        val yin = yin(radius)
        val largeHollow = yin - yin.offset( -innerThickness )
        val smallHollow = yin.offset(-innerThickness - clearance ) - yin.offset( -2*innerThickness - clearance )

        val height = size.y - lipThickness * 2
        val circle = Circle( size.x / 2 )
        val flats = Square( overhang + slack, circle.size.y*3 ).center()
            .rightTo( circle.right )
            .rotate(-45)
            .rotate(180).also()
        val holes = yin.offset( -2*innerThickness - clearance ) +
            yin.offset( -innerThickness ).rotate(180)

        val lip = (circle - flats - holes).extrude(lipThickness)

        val left = (
                (yin-yin.offset(-innerThickness - clearance)).extrude( wireD ) +
                smallHollow.extrude( height + lipThickness )
            )
            .color("Green")    

        val right = largeHollow.rotate(180)
            .extrude( height - wireD - clearance  )
            .color("Orange")

        return lip + (left + right).bottomTo(lip.top)
    }

    @Piece
    fun inner2() = inner().mirrorY()

    @Piece
    fun inner2Alternate() : Shape3d {
        val inner = inner()
        
        val radius = size.x / 2 - recess
        val yin = yin(radius).offset( wireD )
        val valley = yin intersection yin.rotate(180).toPolygon()
        val straight = Square( 100, wireD ).backTo( -valley.front )
            .rotate(-8)
            .leftTo( size.x *0.2 )
            .rotate(180).also()

        val remove = ( valley + straight ).extrude( lipThickness + 0.2 ).bottomTo( -0.1)
        
        return (inner - remove).mirrorY()
    }

    @Piece
    fun outer() : Shape3d {
        val innerR = size.x / 2 + slack
        val largeCircle = Circle( innerR + thickness )
        val base = largeCircle.chamferedExtrude(lipThickness, lipThickness/2, 0)
        val sides = (Circle( innerR + thickness ) - Circle( innerR ))
            .extrude( size.y + slack )
            .bottomTo( base.top )
    
        val slots = Square( wireD * 1.2, base.size.x*2 )
            .leftTo( -innerR )
            .backTo(0)
            .extrude( size.y/2+wireD/2 )
            .topTo( sides.top +0.1 )
            .rotateZ(180).also()

        val overhang = (largeCircle - Square( 100, size.x - overhang*2 ).center())
            .chamferedExtrude( lipThickness, 0, lipThickness/2 )
            .bottomTo( sides.top )

        return base + sides - slots + overhang
    }

    @Piece
    fun all() : Shape3d {
        return arrangeX( 2, inner(), inner2Alternate(), outer() )
    }

    override fun build() : Shape3d {
        val inner = inner().color("Yellow")
        val inner2 = inner2().rotateY(180).topTo( size.y ).color("Green")

        val outer = outer().bottomTo( - lipThickness )
            
        return inner + inner2 + outer.previewOnly()
    }
}