With chat of libraries and the like, I thought I would provide a module that I wrote that I’ve found very helpful in my own work.

I call it “spiral_extrude” and it’s a linear/rotate extrude hybrid.

It’s particularly useful for bolt threads, or perhaps for anyone who wants to model balusters.

It works kind of like the twist parameter in linear extrude except that each spiral extrusion is stitched to the earlier layer forming a closed watertight polyhedron.

(At least I HOPE it does).

Before I release it into the wild (i.e. thingiverse) I’d appreciate feedback… particularly performance improvements, efficiencies or oversights… and then again, if someone identifies that some existing OpenSCAD functionality does this already and my module is redundant, that would be good to know too. :)

spiral_extrude() does require a set of profile points, but I hope that’s not too much of a drawback. As far as I could determine, there’s no way accomplish the spiral stitching with OpenSCAD 2d objects.

/*

spiral_extrude(height, profilePoints, radius, pitch)

from a set of xy points that define a profile, e.g.

[[0,0], [0,.4],[1,1], [1, 1.4], [0,2]]

produce an spiral extruded polyhedron with the specified pitch and radius and of length height along the z axis.

NOTE:

- profilePoints[0] must be on origin [0,0]

- profilePoints y co-ordinates must be positive and in ascending order

procedure:

- for each point in the profile generate a 3d point by adding a z value equal to the

vertical offset for each segment in the spiral. Number of segments in each rotation

is equal to $fn.

- add a base [0,0,0] and top [0,0,height] origin point for stitching

faces for a bottom and top cap

- sealing the polyhedron requires two operations

1) creating triangular faces from the base and top origin points to all ajacent points

in the bottom and top row respectively

2) creating a vertical 'sealing' face to connect the base and top origin point to

each point in the profile in the first and last segment

*/

kClockwise = 1;

kCounterClockwise = -1;

// extrude in clockwise or counterclockwise manner

$spiralVector = kClockwise;

$fn = 100;

// return the maximum value in nth position of vectors v in matrix m

// recursive function

function m_max_v_nth(m, n, _index = 0, _max = 0) =

_index >= len(m)

? _max

: _index == 0

? m_max_v_nth(m, n, 1, m[0][n])

: m_max_v_nth(m, n, _index + 1, max(_max, m[_index][n]));

// return 3d pt of x rotated by angle a with z of z

// a = angle of rotation

function xza2xyz(x,z,a) = [cos(a) * x, sin(a) * x, z];

function points_in_origin(profilePoints, zIncrement, xOffset) =

let(angle = 360 / $fn * $spiralVector)

[for(segment = [0:$fn - 1])

xza2xyz(profilePoints[0][0] + xOffset, profilePoints[0][1] + (zIncrement * segment), segment * angle) ];

function points_in_rotation(segments, profilePoints, zIncrement, xOffset) =

let(angle = 360 / $fn * $spiralVector)

[for(segment = [0:segments])

for(point = [1:len(profilePoints)-1])

xza2xyz(profilePoints[point][0] + xOffset, profilePoints[point][1] + (zIncrement * segment), segment * angle)];

module spiral_extrude(height, profilePoints, radius, pitch) {

defaultWidth = m_max_v_nth(profilePoints,0);

// calculate any additional x offset to increase the diameter of the extrusion

xOffset =

is_undef(radius)

? 0

: defaultWidth < radius

? radius - defaultWidth

: 0;

// calculate any addtitional y offset to pad the profile to the stated pitch

defaultPitch = m_max_v_nth(profilePoints,1);

pHeight =

is_undef(pitch)

? defaultPitch

: max(pitch, defaultPitch);

rotations = ceil(height / pHeight) + 2;

zIncrement = pHeight / $fn;

pPointCount = len(profilePoints);

pPointLimit = pPointCount - 1;

pFaceCount = pPointLimit;

pFaceLimit = pFaceCount - 1;

segCount = $fn * rotations;

segLimit = segCount - 1;

pp = points_in_rotation(segCount, profilePoints, zIncrement, xOffset);

bp = points_in_origin(profilePoints, zIncrement, xOffset);

// concat all points adding the top bottom and top origin points

allPoints = concat(pp, bp, [[0,0,0], [0,0,segCount * zIncrement + pHeight]]);

ppIndex = 0;

bpIndex = len(pp);

bottomOriginIndex = bpIndex + len(bp);

topOriginIndex = bottomOriginIndex + 1;

// eoPPindex = bpIndex - 1;

ppCount = len(pp);

lastProfilePointPos = ppCount - 1;

firstTopCapPointPos = lastProfilePointPos - ($fn * pPointLimit);

fnLimit = $fn - 1;

af =

[

for(segment = [0:segLimit])

for(face = [0:pFaceLimit])

let(faces =

face == 0

?

[

segment < $fn

? ppCount + segment

: (segment - fnLimit) * pFaceCount - 1

,

segment * pFaceCount + face,

(segment + 1) * pFaceCount + face,

segment < fnLimit

? ppCount + segment + 1

: (segment - fnLimit) * pFaceCount + pFaceLimit

]

: // face != 0

[

segment * pFaceCount + face - 1,

segment * pFaceCount + face,

(segment + 1) * pFaceCount + face,

(segment + 1) * pFaceCount + face - 1

]

)

for(i = [0:1])

i == 0

? [faces[1], faces[2], faces[3]] // 0,1,2 ... 0,2,3

: [faces[3], faces[0], faces[1]]

];

bottomCap =

[for(f = [0:$fn - 1])

[

bottomOriginIndex,

bpIndex + f,

f == $fn - 1

? ppIndex + pPointLimit - 1

: bpIndex + f + 1

]

];

topCap =

[for(f = [firstTopCapPointPos:pPointLimit:lastProfilePointPos - 1])

[

topOriginIndex,

f + pPointLimit,

f

]

];

topSeal =

let(startPos = lastProfilePointPos - pPointCount + 1)

[for(f = [0:pPointLimit - 1])

[

f == 0

? startPos - $fn * pPointLimit + pPointLimit

: startPos + f

,

startPos + f + 1,

topOriginIndex,

]

];

bottomSeal =

[

for(f = [0:pPointLimit - 1])

[

f,

f == 0

? ppCount

: f - 1,

bottomOriginIndex,

]

];

allFaces = concat(af, bottomCap, topCap, topSeal, bottomSeal);

polyhedron(points = allPoints, faces = allFaces);

}

module spiral_extrude_examples() {

circlePoints = [[0, 0], [6.18034, 0.9789], [11.7557, 3.8197], [16.1803, 8.2443], [19.0211, 13.8197], [20, 20], [19.0211, 26.1803], [16.1803, 31.7557], [11.7557, 36.1803], [6.18034, 39.0211], [0, 40]];

topPoints = [[10,0],[12,1],[11,2],[11,3],[13,4],[10,5]];

threadPoints = [[10.9175, 0], [10.9175, 0.5], [12, 1.125], [12, 1.375], [10.9175, 2]];

// polygon(circlePoints);

translate([-100,0,0])

spiral_extrude(200,circlePoints);

translate([-15,0,0])

spiral_extrude(200,circlePoints, radius = 40);

translate([120,0,0])

spiral_extrude(200,circlePoints, radius = 80);

translate([0,180,0]) {

translate([-100,0,0])

spiral_extrude(200,topPoints);

translate([-25,0,0])

spiral_extrude(200,topPoints, radius = 20, pitch = 10);

translate([80,0,0])

spiral_extrude(200,topPoints, radius = 50, pitch = 20);

}

translate([0,-180,0]) {

translate([-100,0,0])

spiral_extrude(200,threadPoints);

translate([-25,0,0])

spiral_extrude(200,threadPoints, radius = 20);

translate([80,0,0])

spiral_extrude(200,threadPoints, radius = 40);

}

}

spiral_extrude_examples();