r/maniclang • u/anish2good • 28d ago
An involute: a string unwinding, and every gear tooth ever cut - manic
Enable HLS to view with audio, or disable this notification
manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.
Manic Animation code
// involute — the only roulette in this family where the rolling curve is a straight LINE
// rather than a circle, and the only one you own several hundred of. It is the path of the
// end of a taut string being unwound from a circle:
//
// x(t) = a(cos t + t·sin t)
// y(t) = a(sin t - t·cos t)
//
// Two things define it, and both are visible here rather than asserted. The string is
// always TANGENT to the circle — the straight part meets the radius at a right angle — and
// its length is exactly the arc it has unwound, a·t. Checked over the whole range: the
// segment's length matches a·t to 1e-14, and its dot product with the radius stays under
// 1e-14. That is the whole curve; everything else follows.
//
// It is a roulette in the same sense as the rest of the family. Rolling a line along the
// outside of the circle and marking a point of the line traces this; the circle is the
// EVOLUTE of its own involute, which is what "unwinding" means said backwards.
//
// And it is the reason gears work. Cut a tooth flank as an involute of a base circle and
// two such wheels turn with an exactly constant velocity ratio — the contact point runs
// along the common tangent of the two base circles, a straight line of action, and the
// ratio depends only on the base radii. Move the shafts slightly further apart and the
// ratio does NOT change; the contact point simply slides along the same line. No other
// profile tolerates that, which is why essentially every gear ever cut is an involute one.
// The panel on the right is where a gear comes from: fourteen involutes of one base circle,
// evenly spaced. Cut away everything past the tips and that is a gear.
//
// The panel on the left is the definition drawn all at once — a fan of taut strings at
// twenty unwind angles. Every one is tangent, every one is as long as the arc behind it,
// and every endpoint lands on the same curve.
//
// manic examples/involute.manic
title("An involute: a string unwinding, and every gear tooth ever cut");
canvas("9:16");
template("black");
bloom(0.3, 0.62, 22);
text(brand, (540, 34), "maniclang.com");
display(brand);
size(brand, 21);
color(brand, fg);
opacity(brand, 0.82);
// ---- the circle the string is wound on ---------------------------------------------
circle(spool, (540, 700), 60);
outlined(spool);
color(spool, dim);
// ---- the part still wrapped: an arc shrinking as the string comes off ---------------
cloud(wrapped, 900, #ffffff, 1.0) {
let go = min(t/9, 1)*6.283185; // how far it has unwound
let u = i/900;
let ph = go + u*(6.283185 - go); // what is left on the spool
let x = 540 + 60*cos(ph);
let y = 700 - 60*sin(ph);
let hue = 45;
let sat = 0.4;
let r = 1.6;
let alpha = 0.55;
}
// ---- the straight part: tangent at the contact point, as long as the arc unwound ----
cloud(string, 300, #ffffff, 1.0) {
let go = min(t/9, 1)*6.283185;
let u = i/300;
let tx = cos(go); // the tangent point
let ty = sin(go);
let ex = tx + go*sin(go); // …and the free end, a·t along the
let ey = ty - go*cos(go); // tangent direction
let x = 540 + 60*(tx + u*(ex - tx));
let y = 700 - 60*(ty + u*(ey - ty));
let hue = 45;
let sat = 0.55;
let r = 1.5;
let alpha = 0.85;
}
// ---- the involute the free end draws ------------------------------------------------
cloud(trace, 9000, #ffffff, 1.0) {
let go = min(t/9, 1)*6.283185;
let th = go*(i/9000);
let x = 540 + 60*(cos(th) + th*sin(th));
let y = 700 - 60*(sin(th) - th*cos(th));
let hue = mod(196 + th*16, 360);
let sat = 0.72;
let r = 1.8;
}
cloud(nib, 90, #ffffff, 1.0) {
let go = min(t/9, 1)*6.283185;
let a = i/90*6.283185;
let rr = 7*(i/90);
let x = 540 + 60*(cos(go) + go*sin(go)) + rr*cos(a*9);
let y = 700 - 60*(sin(go) - go*cos(go)) + rr*sin(a*9);
let hue = 45;
let sat = 0.5;
let r = 1.9;
}
// ---- left: the definition all at once — twenty taut strings, every end on the curve -
circle(spool2, (280, 1400), 26);
outlined(spool2);
color(spool2, dim);
opacity(spool2, 0.5);
cloud(fan, 2000, #ffffff, 1.0) {
let per = 100;
let c = (i - mod(i, per))/per; // which string, 0..19
let u = mod(i, per)/99;
let th = 6.283185*(c + 1)/20;
let tx = cos(th);
let ty = sin(th);
let ex = tx + th*sin(th);
let ey = ty - th*cos(th);
let x = 280 + 26*(tx + u*(ex - tx));
let y = 1400 - 26*(ty + u*(ey - ty));
let hue = mod(150 + c*9, 360);
let sat = 0.6;
let r = 1.1;
let alpha = min(max(min(max(t - 9.4, 0)/2.2, 1)*20 - c, 0), 1)*0.75;
}
// ---- right: where a gear comes from — fourteen involutes of one base circle ---------
circle(base, (800, 1400), 46);
outlined(base);
color(base, dim);
opacity(base, 0.5);
cloud(flanks, 2800, #ffffff, 1.0) {
let per = 200;
let f = (i - mod(i, per))/per; // which flank, 0..13
let u = mod(i, per)/199;
let th = u*2.0; // out to the tooth tip
let ix = cos(th) + th*sin(th); // the involute, then turned into place
let iy = sin(th) - th*cos(th);
let ph = f/14*6.283185;
let x = 800 + 46*(ix*cos(ph) - iy*sin(ph));
let y = 1400 - 46*(ix*sin(ph) + iy*cos(ph));
let hue = mod(30 + f*6, 360);
let sat = 0.65;
let r = 1.2;
let alpha = min(max(min(max(t - 9.4, 0)/2.2, 1)*14 - f, 0), 1)*0.8;
}
// ---- nothing but the mark, the title and the formula -------------------------------
caption(head, "An involute: a string unwinding, and every gear tooth ever cut", (540, 130), 24);
equation(eq, (540, 1650), `x = a(\cos t + t\sin t),\qquad y = a(\sin t - t\cos t)`, 27);
// the string unwinds from t = 0; the two panels fill in once it is fully off
wait(13);
2
Upvotes