Garciat
5/22/2016 - 2:03 PM

vortex.html

<body style="margin:0"></body>
<script src="http://gbrlgrct.com/gists/07d74ba686ce04eccb11faa44f2ae229/functional.js"></script>
<script>
'use strict';

/* Init */

const SPACEW = document.body.clientWidth;
const SPACEH = document.body.clientHeight;

const SPACE_QUAD = [[0, 0], [SPACEW, 0], [SPACEW, SPACEH], [0, SPACEH]];

const canvas = document.createElement('canvas');
canvas.width = SPACEW;
canvas.height = SPACEH;
document.body.appendChild(canvas);

const ctx = canvas.getContext('2d');

/* Maths */

function interp(seg, p) {
    const x0 = seg[0][0];
    const y0 = seg[0][1];
    
    const dx = seg[1][0] - x0;
    const dy = seg[1][1] - y0;
    
    return [x0 + p * dx, y0 + p * dy];
}

function uniform(a, b) {
    return a + (b - a) * Math.random();
}

function segmentLength(seg) {
    const dx = seg[1][0] - seg[0][0];
    const dy = seg[1][1] - seg[0][1];
    
    return Math.sqrt(dx * dx + dy * dy);
}

/* Transforms */

const fst = x => x[0];

const snd = x => x[1];

function quadSegments(q) {
    return chain(q).cycle().pairwise().take(4);
}

/* Drawing */

function drawSegment(s) {
    ctx.beginPath();
    ctx.moveTo(s[0][0], s[0][1]);
    ctx.lineTo(s[1][0], s[1][1]);
    ctx.lineWidth = 1;
    ctx.stroke();
}

function drawQuad(q) {
    quadSegments(q).forEach(drawSegment);
}

/* Vortexing */

function* split(quad, depth) {
    if (depth === 0) {
        yield quad;
        return;
    }
    
    const line0 = quad.slice(0, 2);
    const line1 = quad.slice(2, 4);
    
    const r0 = uniform(0.1, 0.9);
    const r1 = uniform(0.1, 0.9);
    
    const p0 = interp(line0, r0);
    const p1 = interp(line1, r1);
    
    const q0 = [p0, p1, quad[3], quad[0]];
    const q1 = [quad[1], quad[2], p1, p0];
    
    yield* split(q0, depth - 1);
    yield* split(q1, depth - 1);
}

function vortexInfinite(quad) {
    const points = memoized(function* () {
        yield quad[0];
        yield* steps.map(snd);
    });
    
    const steps = memoized(function* () {
        yield* zip(points, segments.tail().map(s => interp(s, 0.1)));
    });
    
    const segments = memoized(function* () {
        yield* quadSegments(quad);
        yield* steps;
    });
    
    return segments;
}

function vortex(quad) {
    return vortexInfinite(quad).takeWhile(seg => segmentLength(seg) > 1);
}

/* GO ! */

chain(split(SPACE_QUAD, 4))
    .flatMap(vortex)
    .forEach(drawSegment);

</script>