roundabout

A bit of algorithmic 'art' from 1967. I was working as a vac job for IBM at the Technical University of Denmark in Lyngby, writing a package for the Calcomp plotter. I was asked to prepare a drawing of a super-egg for Piet Hein who had recently popularized the super-egg in a design for a Swedish roundabout. The idea was that a superegg has less sharp ends for cars than those seen in an elliptical roundabout. I liked the idea, but not that it was not continuously differentiable at the points; so I came up with an alternative based on the principle of a driver turning the steering wheel in a regular sine wave. The resulting curve is related to Lissajous figures and hypoid curves, but different. With parameters taken outside the original elongated roundabout it gives nice patterns. The fiddle above is rather more interactive than the 1 day turnaround (if lucky) that I had on runs with the plotter.

by sjpt

HTML

<!DOCTYPE html><html lang="en"><body>
turn offset: <input type="range" min="0" max="2" step="0.0001" id="_o" oninput="o=+this.value; draw();">
turn freq: <input type="range" min="0" max="1"  step="0.0001" id="_f" oninput="f=+this.value; draw();">
turn scale: <input type="range" min="0" max="1000"  step="0.01" id="_s" oninput="s=+this.value; draw();">
<br>
<!--- vel: <input type="range" min="0" max="500" id="_v" oninput="v=+this.value; draw();"> --->
num: <input type="range" min="0" max="2000" id="_n" oninput="n=+this.value; draw();">
dist: <input type="range" min="0.005" max="0.1"  step="0.001" id="_d" oninput="d=+this.value; draw();">
<button onclick="roundabout()">roundabout</button>
multicol: <input id="multicolc" type="checkbox" checked="true" onchange="draw()">
<p id="msg"></p>

<canvas id="canvas" style="background: black"></canvas>
<p>
A bit of algorithmic 'art' from 1967.  It wasn't interactive then. Stephen Todd.
</p>
<h4>hints</h4>
<ul>
<li>Using the cursor up/down keys often gives more control to the sliders than mouse drag. Quick clicks to fine tune, hold for continuous change.</li>
<li>If you set <b>num</b> relatively high and play with the other parameters
you will sometimes see an (almost) repeating pattern. You can then move <b>num</b> down to find just a single repeat. Fine tune <b>num</b> (cursor keys) to see red end of the curve nestle into the green start.</li>
</ul>

<h4>history</h4>
<p>
I was working as a vac job for IBM at the Technical University of Denmark in Lyngby, writing a package for the Calcomp plotter. I was asked to prepare a drawing of a super-egg for 
<a href="https://en.wikipedia.org/wiki/Piet_Hein_(scientist)">Piet Hein</a>
who had popularized the super-egg in a design for a Swedish roundabout. The idea was that a superegg has less sharp ends for cars than those seen in an elliptical roundabout.
</p><p>
I liked the idea, but I dind't like that it was not continuously differentiable at the points; so I came up with an...

JavaScript

var       // must be var so o is same as window.o
o = 0.118,   // turn offset 
f = 0.1967,  // turn frequency
s = 92,      // turn scale 
d=0.01,      // delta,granularity of integration steps
n = 155,     // number of segments
ncols = 25;  // number of distinct colours (change too often is slow)

function setsliders() {
	for (const x in {o, f, s, n, d}) window['_' + x].value = ''+window[x];
}
setsliders();
const {cos, sin, min, max} = Math;
onresize =draw;
function draw() {
	for (const x in {o, f, s, n, d}) window[x] = +window['_' + x].value;
    const canvas = document.querySelector('#canvas');
    canvas.width = innerWidth; canvas.height = innerHeight * 0.6
    const ctx = canvas.getContext('2d');
    if (!ctx) return;
    ctx.clearRect(0,0,9999,9999);
    let cols = 0;  // number of distinct colours used

    // set line stroke and line width
    ctx.strokeStyle = 'red';
    ctx.lineWidth = 1
    const multicol = multicolc.checked
    var minx, maxx, miny, maxy
    let v; // for autoscale
    for (const ii of [0,1]) {
    	var x, y;
		if (ii === 0) {  // find scale
		    minx = Infinity; maxx = -Infinity; miny = Infinity; maxy = -Infinity;
            v = 1;
            x = 0; y = 0;
        } else {		// run using scale
        	const sc = Math.max((maxx-minx)/canvas.width, (maxy-miny)/canvas.height) * 1.2;
            v /= sc; // _v.value = v;
            x = (maxx+minx) / sc * -0.5 + canvas.width / 2;
            y = (maxy+miny) / sc * -0.5 + canvas.height / 2;
            ctx.beginPath();
            ctx.moveTo(x, y);
        }
        for (let t = 0; t < n; t+=d) {
            const a = o * t - s * sin(f * t);
            x += d * v * cos(a);
            y += d * v * sin(a);
            if (ii === 0) {
                minx = min(minx, x); miny = min(miny, y)
                maxx = max(maxx, x); maxy = max(maxy, y)
            } else {
	            ctx.lineTo(x, y);
                if (multicol && Math.floor(t/n*ncols) !== Math.floor((t-d)/n*ncols)) {
    ...