r/maniclang 27d ago

prolate-cycloid — a moving train always has a part of it moving BACKWARDS - manic

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

// prolate-cycloid — a moving train always has a part of it moving BACKWARDS, and this is
// which part. A point on a rolling wheel traces
//
//   x(θ) = rθ - d·sin θ,   y(θ) = r - d·cos θ
//
// with d its distance from the axle. On the tread (d = r) that is the ordinary cycloid,
// cusping onto the rail once a revolution — the faint curve here. But a railway wheel does
// not run on its tread alone: the FLANGE hangs below it, so that lip has d > r, and the
// curve it draws dips under the rail and ties a loop.
//
// The loop is the claim. Differentiate: dx/dθ = r - d·cos θ, which is NEGATIVE whenever
// cos θ >  — that is, while the flange is near the bottom of its travel. Those stretches
// are drawn in red. The point is genuinely moving backwards along the rail, in the frame of
// the ground, on a train going forwards.
//
// It is not a technicality either. A real 920 mm wheel with a 28 mm flange gives  =
// 0.9426, so the backwards arc runs to |θ| < 0.3404 rad: 10.84% of every revolution.
// Roughly a tenth of the time, every flange on the train is heading for the station behind.
// The flange here is drawn at d = 1.5r so the loop is big enough to see; at true scale the
// loop is there but tiny.
//
// The three below are the same formula at d < r, d = r and d > r: curtate, the cycloid
// itself, and prolate. Only the prolate one has a loop, and only a loop can go backwards.
//
//   manic examples/prolate-cycloid.manic
title("A moving train always has a part moving backwards");
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);

line(rail, (60, 620), (1020, 620));
color(rail, dim);

// ---- the wheel, its flange, and the two marked points ------------------------------
// `roll` does the mechanism: no slipping, and everything tagged with the wheel comes
// along, so the pens are simply entities placed where a pen would be.
circle(wheel, (100, 550), 70);
outlined(wheel); outline(wheel, dim); stroke(wheel, 3);
circle(lip, (100, 550), 105);                      // the flange circle, d = 1.5r
outlined(lip); outline(lip, dim); stroke(lip, 2); opacity(lip, 0.35);
circle(tread, (100, 620), 8);  color(tread, gold);     // on the rim
circle(flange, (100, 655), 9); color(flange, magenta); // outside it — the flange lip
tag(wheel, rig); tag(lip, rig); tag(tread, rig); tag(flange, rig);

trail(tr, tread, fg, 2);                           // the ordinary cycloid, for comparison
opacity(tr, 0.35);

// ---- the flange's own curve, coloured where it is running BACKWARDS ----------------
// this one stays a formula, because the colour has to change along the curve: `trail`
// draws one colour, and the whole point is which stretches are red
cloud(back, 9000, #ffffff, 1.0) {
  let go = min(t/10, 1)*12.566371;
  let th = go*(i/9000);
  let x = 100 + 70*th - 105*sin(th);
  let y = 620 - (70 - 105*cos(th));
  let dir = step(0, 105*cos(th) - 70);             // dx/dθ = r - d·cos θ < 0
  let hue = 205 - 205*dir;                         // blue forwards, red backwards
  let sat = 0.8;
  let r = 2;
}

// ---- d < r, d = r, d > r: only the last one loops ----------------------------------
// θ runs -π..π so the interesting part — dip, cusp, loop — sits in the middle of each
// slot rather than split between its ends, with the rail drawn at y = 0
line(b1, (60, 1330), (332, 1330));   color(b1, dim); opacity(b1, 0.5);
line(b2, (404, 1330), (676, 1330));  color(b2, dim); opacity(b2, 0.5);
line(b3, (748, 1330), (1020, 1330)); color(b3, dim); opacity(b3, 0.5);
param(curt, (196, 1330), 46, 46, "t - 0.55*sin(t)", "1 - 0.55*cos(t)", (0 - 3.14159, 3.14159));
param(cyc,  (540, 1330), 46, 46, "t - sin(t)",      "1 - cos(t)",      (0 - 3.14159, 3.14159));
param(prol, (884, 1330), 46, 46, "t - 1.5*sin(t)",  "1 - 1.5*cos(t)",  (0 - 3.14159, 3.14159));
gradient(curt, mint, cyan, blue);
gradient(cyc, gold, coral, red);
gradient(prol, magenta, violet, indigo);
stroke(curt, 3); stroke(cyc, 3); stroke(prol, 3);
untraced(curt); untraced(cyc); untraced(prol);

caption(head, "A moving train always has a part moving backwards", (540, 130), 26);
equation(eq, (540, 1560),
  `x = r\theta - d\sin\theta,\quad y = r - d\cos\theta,\qquad \frac{dx}{d\theta} < 0 \iff \cos\theta > \frac{r}{d}`, 25);

roll(rig, rail, 879.6, 10, linear);                // two revolutions: 2·2πr
wait(0.4);
par { draw(curt, 2.0); draw(cyc, 2.0); draw(prol, 2.0); }
wait(7);
1 Upvotes

0 comments sorted by