/Misc/WireTwistTidy.foocad

Insert a cable half way along its length, then twist to gather it together without knotting. Pull the two ends to unwind.
import static uk.co.nickthecoder.foocad.arrange.v1.Arrange.*
import static uk.co.nickthecoder.foocad.chamferedextrude.v1.ChamferedExtrude.*
class WireTwistTidy : AbstractModel() {
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() )
}
@Piece( printable = false )
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()
}
}

