3 108 Codepen

Генератор коробки передач

Генератор случайных вариаций коробок передач, как бы странно это не звучало. В результате получаем занятные рандомные изображения напоминающие звенья зубчатой передачи.

HTML

<canvas></canvas>

CSS

html, body {
    overflow: hidden;
    touch-action: none;
    content-zooming: none;
    position: absolute;
    margin: 0;
    padding: 100px;
    width: 100%;
    height: 100%;
    background: #000;
}
canvas {
    position: absolute;
    width: 100%; 
    height: 100%;
    user-select: none;
    cursor: pointer;
    background: #000;
}

JS

CFDG библиотека
"use strict";
// CFDG library code
const cfdg = function () { 
    
    const canvas = document.querySelector("canvas");
    const ctx = canvas.getContext("2d");
    let width, height, scale, offsetX, offsetY, minSize, rect, seed, iter, minComplexity;
    const cpgrid = (window.location.href.indexOf("fullcpgrid") > -1);
    let oldColor = 0, zIndex = false;
    const drawCalls = [], stack = [];
    
    const transforms = {
        x(m, v) {
            m[4] += v * m[0];
            m[5] += v * m[1];
        },
        y(m, v) {
            m[4] += v * m[2];
            m[5] += v * m[3];
        },
        z(m, v) {
            zIndex = true;
            m[10] += v;
        },
        s(m, v) {
            const a = Array.isArray(v);
            const x = a ? v[0] : v;
            const y = a ? v[1] : x;
            m[0] *= x;
            m[1] *= x;
            m[2] *= y;
            m[3] *= y;
        },
        r(m, v) {
            const rad = Math.PI * v / 180;
            const cos = Math.cos(rad);
            const sin = Math.sin(rad);
            const r00 = cos * m[0] + sin * m[2];
            const r01 = cos * m[1] + sin * m[3];
            m[2] = cos * m[2] - sin * m[0];
            m[3] = cos * m[3] - sin * m[1];
            m[0] = r00;
            m[1] = r01;
        },
        f(m, v) {
            const rad = Math.PI * v / 180;
            const x = Math.cos(rad);
            const y = Math.sin(rad);
            const n = 1 / (x * x + y * y);
            const b00 = (x * x - y * y) / n;
            const b01 = 2 * x * y / n;
            const b10 = 2 * x * y / n;
            const b11 = (y * y - x * x) / n;
            const r00 = b00 * m[0] + b01 * m[2];
            const r01 = b00 * m[1] + b01 * m[3];
            m[2] = b10 * m[0] + b11 * m[2];
            m[3] = b10 * m[1] + b11 * m[3];
            m[0] = r00;
            m[1] = r01;
        },
        skew(m, v) {
            const x = Math.tan(Math.PI * v[0] / 180);
            const y = Math.tan(Math.PI * v[1] / 180);
            const r00 = m[0] + y * m[2];
            const r01 = m[1] + y * m[3];
            m[2] = x * m[0] + m[2];
            m[3] = x * m[1] + m[3];
            m[0] = r00;
            m[1] = r01;
        },
        h(m, v) {
            m[6] += v;
            m[6] %= 360;
        },
        sat(m, v) {
            this.col(m, v, 7);
        },
        b(m, v) {
            this.col(m, v, 8);
        },
        a(m, v) {
            this.col(m, v, 9);
        },
        col(m, v, p) {
            if (v > 0) {
                m[p] += v * (1 - m[p]);
            } else {
                m[p] += v * m[p];
            }
        },
        raf(m, v) {
            m[12] = cpgrid ? 0 : 1;
        }
    };
    transforms.hue = transforms.h;
    transforms.flip = transforms.f;
    transforms.scale = transforms.s;
    transforms.rotate = transforms.r;
    transforms.alpha = transforms.a;
    
    const PRIMITIVES = [
        // SQUARE
        s => {
            setTransform(s);
            hsla(s);
            ctx.fillRect(-0.5, -0.5, 1, 1);
        },
        // CIRCLE
        s => {
            setTransform(s);
            hsla(s);
            ctx.beginPath();
            ctx.arc(0, 0, 0.5, 0, 2 * Math.PI);
            ctx.fill();
        },
        // TRIANGLE
        s => {
            setTransform(s);
            hsla(s);
            ctx.beginPath();
            ctx.moveTo(0, 0.577350269);
            ctx.lineTo(-0.5, -0.28867513);
            ctx.lineTo(0.5, -0.28867513);
            ctx.lineTo(0.0, 0.577350269);
            ctx.closePath();
            ctx.fill();
        },
        s => {}
    ];
    const drawPush = (s, t, p) => {
        s = transform(s, t);
        if (!s) return;
        bbox(s);
        s[11] = p;
        drawCalls.push(s);
    };
    
    const random = _ => {
        seed = (seed * 16807) % 2147483647;
        return (seed - 1) / 2147483646;
    };
    const randint = (s, e = 0) => {
        if (e === 0) {
            e = s;
            s = 0;
        }
        return Math.floor(s + random() * (e - s + 1));
    };
    const randpos = a => {
        return a[Math.floor(random() * a.length)];
    };
    
    const copy = s => {
        return [
            s[0], // a00
            s[1], // a10
            s[2], // a01
            s[3], // a11
            s[4], // tx
            s[5], // ty
            s[6], // hue
            s[7], // saturation
            s[8], // brillance
            s[9], // alpha
            s[10], // zIndex
            s[11] // primitive
        ];
    };
    
    const setTransform = s => {
        ctx.setTransform(
            -scale * s[0],
            scale * s[1],
            scale * s[2],
            -scale * s[3],
            scale * s[4] + offsetX,
            -scale * s[5] + offsetY
        );
    };

    const hsla = m => {
        let s = m[7];
        const h = m[6];
        const v = m[8];
        const a = m[9];
        const c = h + s + v + a;
        if (c === oldColor ) return false;
        oldColor = c;
        const l = (2 - s) * v / 2;
        if (l != 0) {
            if (l == 1) {
                s = 0;
            } else if (l < 0.5) {
                s = s * v / (l * 2);
            } else {
                s = s * v / (2 - l * 2);
            }
        }
        ctx.fillStyle = `hsla(${Math.round(h)},${s * 100}%,${l * 100}%,${a})`;
        return true;
    };
    
    const bbox = s => {
        const x = s[4] * scale;
        const y = s[5] * scale;
        if (x < rect[0]) rect[0] = x;
        else if (x > rect[1]) rect[1] = x;
        if (y < rect[2]) rect[2] = y;
        else if (y > rect[3]) rect[3] = y;
    };
    
    const autoscale = (s) => {
        const ns =
            Math.min(width / (rect[1] - rect[0]), height / (rect[3] - rect[2])) * s;
        scale *= ns;
        offsetX = width * 0.5 - (rect[0] + rect[1]) * 0.5 * ns;
        offsetY = height * 0.5 + (rect[3] + rect[2]) * 0.5 * ns;
    };
    
    const transform = (s, p) => {
        let m = copy(s);
        for (const c in p) {
            if (transforms[c]) transforms[c](m, p[c]);
            else console.log('error: ' + c);
        }
        const x = (m[0] * m[0] + m[1] * m[1]) * scale;
        const y = (m[2] * m[2] + m[3] * m[3]) * scale;
        return x < minSize && y < minSize ? false : m;
    };
    
    const loop = (n, s, t, f) => {
        let ls = copy(s);
        for (let i = 0; i < n; i++) {
            f(ls, i);
            ls = transform(ls, t);
        }
    };
    
    const rule = (s, t, rule) => {
        s = transform(s, t);
        if (!s) return;
        stack.push([s, rule]);
    };
    
    const rules = (s, t, rules) => {
        s = transform(s, t);
        if (!s) return;
        let totalWeight = 0;
        for (const rule of rules) totalWeight += rule[0];
        let weight = 0;
        const r = random() * totalWeight;
        for (const rule of rules) {
            weight += rule[0];
            if (r <= weight) {
                stack.push([s, rule[1]]);
                return;
            }
        }
    };

    const startshape = (start, t, bkg, m) => {
        if (!seed) this.seed();
        for (const f in this.code) this[f] = this.code[f];        
        width = canvas.width = canvas.offsetWidth * 2;
        height = canvas.height = canvas.offsetHeight * 2;
        ctx.fillStyle = bkg;
        ctx.fillRect(0, 0, width, height);
        scale = 100;
        minComplexity = 100;
        minSize = m;
        rect = [0, 0, 0, 0];
        drawCalls.length = 0;
        let c = 0;
        do {
            c = 0;
            this[start]([1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0], t);
            do {
                const s = stack.shift();
                s[1](s[0]);
                c++;
            } while (stack.length);
        } while (c < minComplexity);
    };
    
    const next = _ => {
        requestAnimationFrame(_ => iter.next());
    };
    
    const run = function*() {
        for (const s of drawCalls) {
            PRIMITIVES[s[11]](s);
            if(s[12]) yield next();
        }
        yield next();
    };
    
    const render = _ => {
        if (zIndex) {
            drawCalls.sort(function (d0, d1) {
                return (d0[10] - d1[10]);
            });
        }
        iter && iter.return();
        iter = run();
        iter.next();
    };
    
    // Public functions
    this.ctx = ctx;
    this.random = random;
    this.randint = randint;
    this.randpos = randpos;
    this.startshape = startshape;
    this.autoscale = autoscale;
    this.transform = transform;
    this.loop = loop;
    this.rule = rule;
    this.rules = rules;
    this.SQUARE = (s, t) => drawPush(s, t, 0);
    this.CIRCLE = (s, t) => drawPush(s, t, 1);
    this.TRIANGLE = (s, t) => drawPush(s, t, 2);
    this.RAF = (s, t) => drawPush(s, {raf: 1}, 3);
    this.seed = (s = Math.round(Math.random() * 1000000)) => {seed = s};
    this.render = render;
    
};
"use strict";
//////////////////////////////////////////////////////////////////
// Adapted from a CFDG program
// https://www.contextfreeart.org/gallery2/index.html#design/618
// Untitled #11 by lakseru, April 15th, 2007
//////////////////////////////////////////////////////////////////
{
    const code = { 
        start() {
            this.startshape('A', {}, "#000", .1);
            this.autoscale(1);
            this.ctx.globalCompositeOperation = 'lighter';
            this.render();
        },
        code: {
            A(s, t) {
                this.rules(s, t, [
                    [1, (s) => {
                        this.CIRCLE(s, {sat: 1, hue: 200, a: -0.92});
                        this.A(s, {x: 0.1, s: 0.999, r: 1, b: 0.002});
                    }],
                    [0.002, (s) => {
                        this.CIRCLE(s, {s: 2.5, sat: 1, hue: 200, a: -0.2});
                        this.A(s, {flip: 180});
                        this.B(s);
                    }]
                ]);
            },
            B(s, t) {
                this.rule(s, t, s => {
                    this.C(s, {r: 90});
                    this.C(s, {r: -90});
                    this.RAF(s);
                });
            },
            C(s, t) {
                this.rules(s, t, [
                    [1, (s) => {
                        this.CIRCLE(s, {s: 0.5, sat: 1, hue: 200, a: -0.7});
                        this.C(s, {x: 0.1, s: 0.99});
                    }],
                    [0.002, (s) => {
                        this.B(s);
                    }]
                ]);
            }
        }
    };
    // import cfdg library
    cfdg.apply(code);
    // run code
    code.start();
    // Click canvas to generate a new image
    ["click", "touchdown"].forEach(event => {
        document.addEventListener(event, e => code.start(), false);
    });
}

Комментарии

  • Facebook
  • Вконтакте

Похожие статьи