discuss@lists.openscad.org

OpenSCAD general discussion Mailing-list

View all threads

Generating a helix with linear extrude

JB
Jordan Brown
Tue, Feb 27, 2024 5:58 AM

[ This was a math puzzle.  It's almost certainly not the right way to
generate a helix. I thought others might find it interesting. ]

Everybody knows that you can't generate a helix with linear_extrude. 
Linear extrude works with horizontal cross sections, and a proper helix
needs a circle (or whatever shape) that's tilted to perpendicular to the
angle of the helix.  You end up with something like so:

Some time back, I realized that you could generate a helix with linear
extrude.  The problem is that the horizontal cross-section isn't
simple.  It's sort of banana-shaped:

But what is that shape?

I got interested in this question again, and played with the idea that
it's a circle that's been stretched around the circumference of the helix.

I tried translating it into polar coordinates and scaling theta by
1/sin(helix angle), and although I'm not sure that it's absolutely
right, the result is pretty good:

Here's a cross-section perpendicular to the helix angle:

Perfect circle?  Maybe, maybe not.  I'm not sure that I cut the
cross-section at exactly the right point, or took the screen shot from
the right angle.

Here's the demo program, intended for use with the customizer.  The best
algorithm, A, is in bananaA().  bananaB() is a different way to stretch
the circle that doesn't really work, but I don't understand why not
because I can't think well in polar coordinates.  circle() is a
straightforward circle, like you would get from the "doesn't work"
linear extrude solution.  (And yes, of course if I was really trying to
write a helix function everything would be an argument, not a global.)

// Vertical distance from the centerline of one turn to the centerline of the next turn
pitch = 20;
// Radius of the helix, from XY=0 to the centerline of the extruded circle
helix_r = 15;
// Radius of the extruded circle
r = 5;
// Height of the helix
h = 100;
// Show cross-section (view from -Y orthogonal)
intersect = false;
// Which algorithm to use (a is best, b is interesting but not as good, 2 is simple circle)
algorithm = 0;  // [ 0: a, 1: b, 2: circle ]

module stop();

// This is a little interesting to animate:
// pitch = 20 + 10abs($t2-1);

function torect2(p) = [
p[0] * cos(p[1]),
p[0] * sin(p[1])
];
function topolar2(p) = [
norm(p),
atan2(p.y, p.x)
];

// Helix angle
helix_a = atan2(pitch, helix_r2PI);

// degrees per unit, along the circumference
theta_scale = 360/(2PIhelix_r);

// Banana, algorithm A
bananaA = function() [
for (a=[0:359])
torect2([helix_r, 0] + [r*cos(a), r * sin(a) * theta_scale / sin(helix_a)])
];

// Banana, algorithm B
bananaB = function() [
for (a=[0:359])
let(circ = torect2([r, a]))        // circle
let(tcirc = circ + [helix_r, 0])    // translated
let(pcirc = topolar2(tcirc))        // in polar coords
let(smeared_circ = [pcirc[0], pcirc[1]/sin(helix_a)])
torect2(smeared_circ)
];

// Simple circle, for comparison
circle = function() [
for (a=[0:359])
torect2([r, a]) + [helix_r, 0]
];

functions = [ bananaA, bananaB, circle ];

module helix() {
linear_extrude(height=h, twist=360*h/pitch, convexity=2)
polygon(functionsalgorithm);
}

if (intersect) {
intersection() {
#helix();
translate([helix_r, 0, pitch]) rotate([-helix_a,0,0]) cube([r2, 0.01, r2], center=true);
}
} else {
helix();
}

// A few reference shapes

// A circle.  If helix_a is 90, this should match the bottom of the helix.
color("blue") translate([helix_r,0,-0.1]) linear_extrude(height=0.1) circle(r=r);

// The circumference of the helix.
color("green") translate([0,0,-0.2]) linear_extrude(height=0.1) difference() { circle(r=helix_r+0.1); circle(r=helix_r-0.1); }

[ This was a math puzzle.  It's almost certainly not the *right* way to generate a helix. I thought others might find it interesting. ] Everybody knows that you can't generate a helix with linear_extrude.  Linear extrude works with horizontal cross sections, and a proper helix needs a circle (or whatever shape) that's tilted to perpendicular to the angle of the helix.  You end up with something like so: Some time back, I realized that you *could* generate a helix with linear extrude.  The problem is that the horizontal cross-section isn't simple.  It's sort of banana-shaped: But what *is* that shape? I got interested in this question again, and played with the idea that it's a circle that's been stretched around the circumference of the helix. I tried translating it into polar coordinates and scaling theta by 1/sin(helix angle), and although I'm not sure that it's absolutely right, the result is pretty good: Here's a cross-section perpendicular to the helix angle: Perfect circle?  Maybe, maybe not.  I'm not sure that I cut the cross-section at exactly the right point, or took the screen shot from the right angle. Here's the demo program, intended for use with the customizer.  The best algorithm, A, is in bananaA().  bananaB() is a different way to stretch the circle that doesn't really work, but I don't understand why not because I can't think well in polar coordinates.  circle() is a straightforward circle, like you would get from the "doesn't work" linear extrude solution.  (And yes, of course if I was really trying to write a helix function everything would be an argument, not a global.) // Vertical distance from the centerline of one turn to the centerline of the next turn pitch = 20; // Radius of the helix, from XY=0 to the centerline of the extruded circle helix_r = 15; // Radius of the extruded circle r = 5; // Height of the helix h = 100; // Show cross-section (view from -Y orthogonal) intersect = false; // Which algorithm to use (a is best, b is interesting but not as good, 2 is simple circle) algorithm = 0; // [ 0: a, 1: b, 2: circle ] module stop(); // This is a little interesting to animate: // pitch = 20 + 10*abs($t*2-1); function torect2(p) = [ p[0] * cos(p[1]), p[0] * sin(p[1]) ]; function topolar2(p) = [ norm(p), atan2(p.y, p.x) ]; // Helix angle helix_a = atan2(pitch, helix_r*2*PI); // degrees per unit, along the circumference theta_scale = 360/(2*PI*helix_r); // Banana, algorithm A bananaA = function() [ for (a=[0:359]) torect2([helix_r, 0] + [r*cos(a), r * sin(a) * theta_scale / sin(helix_a)]) ]; // Banana, algorithm B bananaB = function() [ for (a=[0:359]) let(circ = torect2([r, a])) // circle let(tcirc = circ + [helix_r, 0]) // translated let(pcirc = topolar2(tcirc)) // in polar coords let(smeared_circ = [pcirc[0], pcirc[1]/sin(helix_a)]) torect2(smeared_circ) ]; // Simple circle, for comparison circle = function() [ for (a=[0:359]) torect2([r, a]) + [helix_r, 0] ]; functions = [ bananaA, bananaB, circle ]; module helix() { linear_extrude(height=h, twist=360*h/pitch, convexity=2) polygon(functions[algorithm]()); } if (intersect) { intersection() { #helix(); translate([helix_r, 0, pitch]) rotate([-helix_a,0,0]) cube([r*2, 0.01, r*2], center=true); } } else { helix(); } // A few reference shapes // A circle. If helix_a is 90, this should match the bottom of the helix. color("blue") translate([helix_r,0,-0.1]) linear_extrude(height=0.1) circle(r=r); // The circumference of the helix. color("green") translate([0,0,-0.2]) linear_extrude(height=0.1) difference() { circle(r=helix_r+0.1); circle(r=helix_r-0.1); }
HL
Hans L
Tue, Feb 27, 2024 10:48 PM

Here is my solution from ~8.5 yrs ago
https://www.thingiverse.com/thing:1098806

On Mon, Feb 26, 2024 at 11:59 PM Jordan Brown via Discuss <
discuss@lists.openscad.org> wrote:

[ This was a math puzzle.  It's almost certainly not the right way to
generate a helix. I thought others might find it interesting. ]

Everybody knows that you can't generate a helix with linear_extrude.
Linear extrude works with horizontal cross sections, and a proper helix
needs a circle (or whatever shape) that's tilted to perpendicular to the
angle of the helix.  You end up with something like so:

Some time back, I realized that you could generate a helix with linear
extrude.  The problem is that the horizontal cross-section isn't simple.
It's sort of banana-shaped:

But what is that shape?

I got interested in this question again, and played with the idea that
it's a circle that's been stretched around the circumference of the helix.

I tried translating it into polar coordinates and scaling theta by
1/sin(helix angle), and although I'm not sure that it's absolutely right,
the result is pretty good:

Here's a cross-section perpendicular to the helix angle:

Perfect circle?  Maybe, maybe not.  I'm not sure that I cut the
cross-section at exactly the right point, or took the screen shot from the
right angle.

Here's the demo program, intended for use with the customizer.  The best
algorithm, A, is in bananaA().  bananaB() is a different way to stretch the
circle that doesn't really work, but I don't understand why not because I
can't think well in polar coordinates.  circle() is a straightforward
circle, like you would get from the "doesn't work" linear extrude
solution.  (And yes, of course if I was really trying to write a helix
function everything would be an argument, not a global.)

// Vertical distance from the centerline of one turn to the centerline of the next turn
pitch = 20;
// Radius of the helix, from XY=0 to the centerline of the extruded circle
helix_r = 15;
// Radius of the extruded circle
r = 5;
// Height of the helix
h = 100;
// Show cross-section (view from -Y orthogonal)
intersect = false;
// Which algorithm to use (a is best, b is interesting but not as good, 2 is simple circle)
algorithm = 0;  // [ 0: a, 1: b, 2: circle ]

module stop();

// This is a little interesting to animate:
// pitch = 20 + 10abs($t2-1);

function torect2(p) = [
p[0] * cos(p[1]),
p[0] * sin(p[1])
];
function topolar2(p) = [
norm(p),
atan2(p.y, p.x)
];

// Helix angle
helix_a = atan2(pitch, helix_r2PI);

// degrees per unit, along the circumference
theta_scale = 360/(2PIhelix_r);

// Banana, algorithm A
bananaA = function() [
for (a=[0:359])
torect2([helix_r, 0] + [r*cos(a), r * sin(a) * theta_scale / sin(helix_a)])
];

// Banana, algorithm B
bananaB = function() [
for (a=[0:359])
let(circ = torect2([r, a]))        // circle
let(tcirc = circ + [helix_r, 0])    // translated
let(pcirc = topolar2(tcirc))        // in polar coords
let(smeared_circ = [pcirc[0], pcirc[1]/sin(helix_a)])
torect2(smeared_circ)
];

// Simple circle, for comparison
circle = function() [
for (a=[0:359])
torect2([r, a]) + [helix_r, 0]
];

functions = [ bananaA, bananaB, circle ];

module helix() {
linear_extrude(height=h, twist=360*h/pitch, convexity=2)
polygon(functionsalgorithm);
}

if (intersect) {
intersection() {
#helix();
translate([helix_r, 0, pitch]) rotate([-helix_a,0,0]) cube([r2, 0.01, r2], center=true);
}
} else {
helix();
}

// A few reference shapes

// A circle.  If helix_a is 90, this should match the bottom of the helix.
color("blue") translate([helix_r,0,-0.1]) linear_extrude(height=0.1) circle(r=r);

// The circumference of the helix.
color("green") translate([0,0,-0.2]) linear_extrude(height=0.1) difference() { circle(r=helix_r+0.1); circle(r=helix_r-0.1); }


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

Here is my solution from ~8.5 yrs ago https://www.thingiverse.com/thing:1098806 On Mon, Feb 26, 2024 at 11:59 PM Jordan Brown via Discuss < discuss@lists.openscad.org> wrote: > [ This was a math puzzle. It's almost certainly not the *right* way to > generate a helix. I thought others might find it interesting. ] > > Everybody knows that you can't generate a helix with linear_extrude. > Linear extrude works with horizontal cross sections, and a proper helix > needs a circle (or whatever shape) that's tilted to perpendicular to the > angle of the helix. You end up with something like so: > > > > Some time back, I realized that you *could* generate a helix with linear > extrude. The problem is that the horizontal cross-section isn't simple. > It's sort of banana-shaped: > > > But what *is* that shape? > > I got interested in this question again, and played with the idea that > it's a circle that's been stretched around the circumference of the helix. > > I tried translating it into polar coordinates and scaling theta by > 1/sin(helix angle), and although I'm not sure that it's absolutely right, > the result is pretty good: > > > > Here's a cross-section perpendicular to the helix angle: > > > Perfect circle? Maybe, maybe not. I'm not sure that I cut the > cross-section at exactly the right point, or took the screen shot from the > right angle. > > Here's the demo program, intended for use with the customizer. The best > algorithm, A, is in bananaA(). bananaB() is a different way to stretch the > circle that doesn't really work, but I don't understand why not because I > can't think well in polar coordinates. circle() is a straightforward > circle, like you would get from the "doesn't work" linear extrude > solution. (And yes, of course if I was really trying to write a helix > function everything would be an argument, not a global.) > > // Vertical distance from the centerline of one turn to the centerline of the next turn > pitch = 20; > // Radius of the helix, from XY=0 to the centerline of the extruded circle > helix_r = 15; > // Radius of the extruded circle > r = 5; > // Height of the helix > h = 100; > // Show cross-section (view from -Y orthogonal) > intersect = false; > // Which algorithm to use (a is best, b is interesting but not as good, 2 is simple circle) > algorithm = 0; // [ 0: a, 1: b, 2: circle ] > > module stop(); > > // This is a little interesting to animate: > // pitch = 20 + 10*abs($t*2-1); > > function torect2(p) = [ > p[0] * cos(p[1]), > p[0] * sin(p[1]) > ]; > function topolar2(p) = [ > norm(p), > atan2(p.y, p.x) > ]; > > // Helix angle > helix_a = atan2(pitch, helix_r*2*PI); > > // degrees per unit, along the circumference > theta_scale = 360/(2*PI*helix_r); > > // Banana, algorithm A > bananaA = function() [ > for (a=[0:359]) > torect2([helix_r, 0] + [r*cos(a), r * sin(a) * theta_scale / sin(helix_a)]) > ]; > > // Banana, algorithm B > bananaB = function() [ > for (a=[0:359]) > let(circ = torect2([r, a])) // circle > let(tcirc = circ + [helix_r, 0]) // translated > let(pcirc = topolar2(tcirc)) // in polar coords > let(smeared_circ = [pcirc[0], pcirc[1]/sin(helix_a)]) > torect2(smeared_circ) > ]; > > // Simple circle, for comparison > circle = function() [ > for (a=[0:359]) > torect2([r, a]) + [helix_r, 0] > ]; > > functions = [ bananaA, bananaB, circle ]; > > module helix() { > linear_extrude(height=h, twist=360*h/pitch, convexity=2) > polygon(functions[algorithm]()); > } > > if (intersect) { > intersection() { > #helix(); > translate([helix_r, 0, pitch]) rotate([-helix_a,0,0]) cube([r*2, 0.01, r*2], center=true); > } > } else { > helix(); > } > > // A few reference shapes > > // A circle. If helix_a is 90, this should match the bottom of the helix. > color("blue") translate([helix_r,0,-0.1]) linear_extrude(height=0.1) circle(r=r); > > // The circumference of the helix. > color("green") translate([0,0,-0.2]) linear_extrude(height=0.1) difference() { circle(r=helix_r+0.1); circle(r=helix_r-0.1); } > > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org >
GS
Guenther Sohler
Wed, Feb 28, 2024 7:21 AM

I am absolutely stunned that it's possible to create a flat cross-section
such that the final result  looks correctly, when extruded!

However, I feel a helix would look more natural when the top/bottom face of
the helix would be normal to the natural extrusion.
Being able to tilt the extruded 2d shape has its justification :)

On Tue, Feb 27, 2024 at 11:50 PM Hans L via Discuss <
discuss@lists.openscad.org> wrote:

Here is my solution from ~8.5 yrs ago
https://www.thingiverse.com/thing:1098806

On Mon, Feb 26, 2024 at 11:59 PM Jordan Brown via Discuss <
discuss@lists.openscad.org> wrote:

[ This was a math puzzle.  It's almost certainly not the right way to
generate a helix. I thought others might find it interesting. ]

Everybody knows that you can't generate a helix with linear_extrude.
Linear extrude works with horizontal cross sections, and a proper helix
needs a circle (or whatever shape) that's tilted to perpendicular to the
angle of the helix.  You end up with something like so:

Some time back, I realized that you could generate a helix with linear
extrude.  The problem is that the horizontal cross-section isn't simple.
It's sort of banana-shaped:

But what is that shape?

I got interested in this question again, and played with the idea that
it's a circle that's been stretched around the circumference of the helix.

I tried translating it into polar coordinates and scaling theta by
1/sin(helix angle), and although I'm not sure that it's absolutely right,
the result is pretty good:

Here's a cross-section perpendicular to the helix angle:

Perfect circle?  Maybe, maybe not.  I'm not sure that I cut the
cross-section at exactly the right point, or took the screen shot from the
right angle.

Here's the demo program, intended for use with the customizer.  The best
algorithm, A, is in bananaA().  bananaB() is a different way to stretch the
circle that doesn't really work, but I don't understand why not because I
can't think well in polar coordinates.  circle() is a straightforward
circle, like you would get from the "doesn't work" linear extrude
solution.  (And yes, of course if I was really trying to write a helix
function everything would be an argument, not a global.)

// Vertical distance from the centerline of one turn to the centerline of the next turn
pitch = 20;
// Radius of the helix, from XY=0 to the centerline of the extruded circle
helix_r = 15;
// Radius of the extruded circle
r = 5;
// Height of the helix
h = 100;
// Show cross-section (view from -Y orthogonal)
intersect = false;
// Which algorithm to use (a is best, b is interesting but not as good, 2 is simple circle)
algorithm = 0;  // [ 0: a, 1: b, 2: circle ]

module stop();

// This is a little interesting to animate:
// pitch = 20 + 10abs($t2-1);

function torect2(p) = [
p[0] * cos(p[1]),
p[0] * sin(p[1])
];
function topolar2(p) = [
norm(p),
atan2(p.y, p.x)
];

// Helix angle
helix_a = atan2(pitch, helix_r2PI);

// degrees per unit, along the circumference
theta_scale = 360/(2PIhelix_r);

// Banana, algorithm A
bananaA = function() [
for (a=[0:359])
torect2([helix_r, 0] + [r*cos(a), r * sin(a) * theta_scale / sin(helix_a)])
];

// Banana, algorithm B
bananaB = function() [
for (a=[0:359])
let(circ = torect2([r, a]))        // circle
let(tcirc = circ + [helix_r, 0])    // translated
let(pcirc = topolar2(tcirc))        // in polar coords
let(smeared_circ = [pcirc[0], pcirc[1]/sin(helix_a)])
torect2(smeared_circ)
];

// Simple circle, for comparison
circle = function() [
for (a=[0:359])
torect2([r, a]) + [helix_r, 0]
];

functions = [ bananaA, bananaB, circle ];

module helix() {
linear_extrude(height=h, twist=360*h/pitch, convexity=2)
polygon(functionsalgorithm);
}

if (intersect) {
intersection() {
#helix();
translate([helix_r, 0, pitch]) rotate([-helix_a,0,0]) cube([r2, 0.01, r2], center=true);
}
} else {
helix();
}

// A few reference shapes

// A circle.  If helix_a is 90, this should match the bottom of the helix.
color("blue") translate([helix_r,0,-0.1]) linear_extrude(height=0.1) circle(r=r);

// The circumference of the helix.
color("green") translate([0,0,-0.2]) linear_extrude(height=0.1) difference() { circle(r=helix_r+0.1); circle(r=helix_r-0.1); }


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

I am absolutely stunned that it's possible to create a flat cross-section such that the final result looks correctly, when extruded! However, I feel a helix would look more natural when the top/bottom face of the helix would be normal to the natural extrusion. Being able to tilt the extruded 2d shape has its justification :) On Tue, Feb 27, 2024 at 11:50 PM Hans L via Discuss < discuss@lists.openscad.org> wrote: > Here is my solution from ~8.5 yrs ago > https://www.thingiverse.com/thing:1098806 > > On Mon, Feb 26, 2024 at 11:59 PM Jordan Brown via Discuss < > discuss@lists.openscad.org> wrote: > >> [ This was a math puzzle. It's almost certainly not the *right* way to >> generate a helix. I thought others might find it interesting. ] >> >> Everybody knows that you can't generate a helix with linear_extrude. >> Linear extrude works with horizontal cross sections, and a proper helix >> needs a circle (or whatever shape) that's tilted to perpendicular to the >> angle of the helix. You end up with something like so: >> >> >> >> Some time back, I realized that you *could* generate a helix with linear >> extrude. The problem is that the horizontal cross-section isn't simple. >> It's sort of banana-shaped: >> >> >> But what *is* that shape? >> >> I got interested in this question again, and played with the idea that >> it's a circle that's been stretched around the circumference of the helix. >> >> I tried translating it into polar coordinates and scaling theta by >> 1/sin(helix angle), and although I'm not sure that it's absolutely right, >> the result is pretty good: >> >> >> >> Here's a cross-section perpendicular to the helix angle: >> >> >> Perfect circle? Maybe, maybe not. I'm not sure that I cut the >> cross-section at exactly the right point, or took the screen shot from the >> right angle. >> >> Here's the demo program, intended for use with the customizer. The best >> algorithm, A, is in bananaA(). bananaB() is a different way to stretch the >> circle that doesn't really work, but I don't understand why not because I >> can't think well in polar coordinates. circle() is a straightforward >> circle, like you would get from the "doesn't work" linear extrude >> solution. (And yes, of course if I was really trying to write a helix >> function everything would be an argument, not a global.) >> >> // Vertical distance from the centerline of one turn to the centerline of the next turn >> pitch = 20; >> // Radius of the helix, from XY=0 to the centerline of the extruded circle >> helix_r = 15; >> // Radius of the extruded circle >> r = 5; >> // Height of the helix >> h = 100; >> // Show cross-section (view from -Y orthogonal) >> intersect = false; >> // Which algorithm to use (a is best, b is interesting but not as good, 2 is simple circle) >> algorithm = 0; // [ 0: a, 1: b, 2: circle ] >> >> module stop(); >> >> // This is a little interesting to animate: >> // pitch = 20 + 10*abs($t*2-1); >> >> function torect2(p) = [ >> p[0] * cos(p[1]), >> p[0] * sin(p[1]) >> ]; >> function topolar2(p) = [ >> norm(p), >> atan2(p.y, p.x) >> ]; >> >> // Helix angle >> helix_a = atan2(pitch, helix_r*2*PI); >> >> // degrees per unit, along the circumference >> theta_scale = 360/(2*PI*helix_r); >> >> // Banana, algorithm A >> bananaA = function() [ >> for (a=[0:359]) >> torect2([helix_r, 0] + [r*cos(a), r * sin(a) * theta_scale / sin(helix_a)]) >> ]; >> >> // Banana, algorithm B >> bananaB = function() [ >> for (a=[0:359]) >> let(circ = torect2([r, a])) // circle >> let(tcirc = circ + [helix_r, 0]) // translated >> let(pcirc = topolar2(tcirc)) // in polar coords >> let(smeared_circ = [pcirc[0], pcirc[1]/sin(helix_a)]) >> torect2(smeared_circ) >> ]; >> >> // Simple circle, for comparison >> circle = function() [ >> for (a=[0:359]) >> torect2([r, a]) + [helix_r, 0] >> ]; >> >> functions = [ bananaA, bananaB, circle ]; >> >> module helix() { >> linear_extrude(height=h, twist=360*h/pitch, convexity=2) >> polygon(functions[algorithm]()); >> } >> >> if (intersect) { >> intersection() { >> #helix(); >> translate([helix_r, 0, pitch]) rotate([-helix_a,0,0]) cube([r*2, 0.01, r*2], center=true); >> } >> } else { >> helix(); >> } >> >> // A few reference shapes >> >> // A circle. If helix_a is 90, this should match the bottom of the helix. >> color("blue") translate([helix_r,0,-0.1]) linear_extrude(height=0.1) circle(r=r); >> >> // The circumference of the helix. >> color("green") translate([0,0,-0.2]) linear_extrude(height=0.1) difference() { circle(r=helix_r+0.1); circle(r=helix_r-0.1); } >> >> _______________________________________________ >> OpenSCAD mailing list >> To unsubscribe send an email to discuss-leave@lists.openscad.org >> > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org >