git @ Cat's Eye Technologies Truchet-Knotwork / master src / truchet-knotwork.js
master

Tree @master (Download .tar.gz)

truchet-knotwork.js @masterraw · history · blame

// SPDX-FileCopyrightText: Chris Pressey, the creator of this work, has dedicated it to the public domain.
// For more information, please refer to <https://unlicense.org/>
// SPDX-License-Identifier: Unlicense


TruchetKnotwork = function() {
    this.init = function(cfg) {
        this.canvas = cfg.canvas;
        this.ctx = this.canvas.getContext('2d');
        this.width = cfg.width;
        this.height = cfg.height;

        /*
        this.gridWidth = 30;
        this.gridHeight = 30;
        this.diagonalThickness = 0.1;
        this.quarterCircleThickness = 0.1;
        */

        this.gridWidth = this.width / 30;
        this.gridHeight = this.height / 30;
        this.diagonalThickness = 0.125;
        this.quarterCircleThickness = 0.125;

        this.reset();
    };

    this.drawDiagonal = function(ctx) {
        const d = this.diagonalThickness;

        ctx.beginPath();

        ctx.moveTo(0, 0);
        ctx.lineTo(d, 0);
        ctx.lineTo(1, 1-d);
        ctx.lineTo(1, 1);
        ctx.lineTo(1-d, 1);
        ctx.lineTo(0, d);
        ctx.lineTo(0, 0);

        ctx.moveTo(1-d, 0);
        ctx.lineTo(1, 0);
        ctx.lineTo(1, d);
        ctx.lineTo(1-d, 0);

        ctx.moveTo(0, 1-d);
        ctx.lineTo(0, 1);
        ctx.lineTo(d, 1);
        ctx.lineTo(0, 1-d);

        ctx.fill();
    }

    this.drawUpperQuartercircle = function(ctx) {
        const d = this.quarterCircleThickness / 2;

        ctx.beginPath();
        ctx.arc(0, 0, 0.5 + d, 0, Math.PI / 2, false);
        ctx.lineTo(0, 0.5 - d);
        ctx.arc(0, 0, 0.5 - d, Math.PI / 2, 0, true);
        ctx.lineTo(0.5 - d, 0);
        ctx.fill();
    }

    this.drawLowerQuartercircle = function(ctx) {
        const d = this.quarterCircleThickness / 2;

        ctx.beginPath();
        ctx.arc(1, 1, 0.5 + d, Math.PI, Math.PI * (3/2), false);
        ctx.lineTo(1, 1-(0.5+d));
        ctx.arc(1, 1, 0.5 - d, Math.PI * (3/2), Math.PI, true);
        ctx.lineTo(1-(0.5+d), 1);
        ctx.fill();
    }

    this.reset = function() {
        let ctx = this.ctx;
        ctx.fillStyle = '#ffeedd';
        ctx.strokeStyle = '#000000';

        const cellWidth = this.width / this.gridWidth;
        const cellHeight = this.height / this.gridHeight;
        ctx.fillRect(0, 0, this.width, this.height);
        for (let gy = 0; gy < this.gridHeight; gy++) {
            for (let gx = 0; gx < this.gridWidth; gx++) {
                ctx.save();
                ctx.translate(gx * cellWidth, gy * cellHeight);
                ctx.scale(cellWidth, cellHeight);
                ctx.lineWidth = 1 / cellWidth;

                let rot1 = Math.random() > 0.5 ? Math.PI / 2 : 0;
                let rot2 = Math.random() > 0.5 ? Math.PI / 2 : 0;

                let srcComps = [[rot1, 'q1'], [rot1, 'q2'], [rot2, 'd']];

                let comps = [];
                while (srcComps.length > 0) {
                    comps.push(srcComps.splice(Math.random() * srcComps.length, 1)[0]);
                }

                for (let [rot, code] of comps) {
                    ctx.save();
                    if (rot) {
                        ctx.translate(0.5, 0.5);
                        ctx.rotate(Math.PI / 2);
                        ctx.translate(-0.5, -0.5);
                    }
                    if (code === 'd') {
                        ctx.fillStyle = '#008800';
                        this.drawDiagonal(ctx);
                    } else if (code === 'q1') {
                        ctx.fillStyle = '#aa0022';
                        this.drawUpperQuartercircle(ctx);
                    } else if (code === 'q2') {
                        ctx.fillStyle = '#aa0022';
                        this.drawLowerQuartercircle(ctx);
                    }
                    ctx.restore();
                }
                ctx.restore();
            }
        }
    }
}