git @ Cat's Eye Technologies HTML5-Gewgaws / master bezier-knots / bezier-knots.js
master

Tree @master (Download .tar.gz)

bezier-knots.js @masterraw · history · blame

function launch(prefix, containerId) {
    var deps = [
        "element-factory.js",
        "preset-manager.js"
    ];
    var loaded = 0;
    for (var i = 0; i < deps.length; i++) {
        var elem = document.createElement('script');
        elem.src = prefix + deps[i];
        elem.onload = function() {
            if (++loaded == deps.length) {
                var container = document.getElementById(containerId);

                var canvas = yoob.makeCanvas(container, 600, 600);

                var gewgaw = new BezierKnots().init({
                    canvas: canvas,
                    lineWidth: 10,
                });

                var panelContainer = yoob.makeDiv(container);
                var panel = yoob.makeDiv(panelContainer);
                panel.id = 'panel-container';

                var makeRangeControl = function(cfg) {
                    var bezel = yoob.makeDiv(panel);
                    return yoob.makeRangeControl(bezel, cfg);
                }

                var lineThicknessControl = makeRangeControl({
                    label: 'Line thickness:',
                    min: 1,
                    max: 30,
                    value: 10,
                    callback: function(v) {
                        gewgaw.lineWidth = v;
                        gewgaw.redraw();
                    }
                });
                var edgeThicknessControl = makeRangeControl({
                    label: 'Edge thickness:',
                    min: 0,
                    max: 20,
                    value: 3,
                    callback: function(v) {
                        gewgaw.edgeWidth = v;
                        gewgaw.redraw();
                    }
                });
                var numSegmentsControl = makeRangeControl({
                    label: 'Number of segments:',
                    min: 1,
                    max: 20,
                    value: 6,
                    callback: function(v) {
                        gewgaw.numSegments = v;
                        gewgaw.reroll();
                    }
                });
                var numRadiiControl = makeRangeControl({
                    label: 'Number of rings:',
                    min: 1,
                    max: 20,
                    value: 6,
                    callback: function(v) {
                        gewgaw.numRadii = v;
                        gewgaw.reroll();
                    }
                });
                var rerollButton = yoob.makeButton(panel, 'Re-roll', function() { gewgaw.reroll(); });
            }
        };
        document.body.appendChild(elem);
    }
}


var QUADRANT = Math.PI / 2;
var TWO_PI = Math.PI * 2;
var DEGREE = TWO_PI / 360.0;


Segment = function() {
    this.init = function(cx, cy, radius, angle1, angle2) {
        this.cx = cx;
        this.cy = cy;
        this.radius = radius;
        this.angle1 = angle1;
        this.angle2 = angle2;
        return this;
    }

    this.getLine = function(tweak) {
        var angle1 = this.angle1 + tweak;
        var angle2 = this.angle2 + tweak;
        var x1 = this.cx + Math.cos(angle1) * this.radius;
        var y1 = this.cy + Math.sin(angle1) * this.radius;
        var x2 = this.cx + Math.cos(angle2) * this.radius;
        var y2 = this.cy + Math.sin(angle2) * this.radius;
        return [[x1, y1], [x2, y2]];
    };

    this.drawConnectTo = function(ctx, nextSegment, tweak) {
        var l1 = this.getLine(DEGREE * -1 * tweak);
        var l2 = nextSegment.getLine(DEGREE * tweak);

        // the arguments represent 2 lines, (x1,y1)-(x2,y2) and (x3,y3)-(x4,y4)

        var x1 = l1[0][0];
        var y1 = l1[0][1];

        var x2 = l1[1][0];
        var y2 = l1[1][1];

        var x3 = l2[0][0];
        var y3 = l2[0][1];

        var x4 = l2[1][0];
        var y4 = l2[1][1];

        // find their midpoints:
        var xm1 = (x1 + x2) * 0.50;
        var ym1 = (y1 + y2) * 0.50;

        var xm2 = (x3 + x4) * 0.50;
        var ym2 = (y3 + y4) * 0.50;

        ctx.moveTo(xm1, ym1);
        ctx.bezierCurveTo(
            x2, y2,
            x3, y3,
            xm2, ym2
        );
    };
};


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

        this.lineWidth = cfg.lineWidth || 10;
        this.edgeWidth = cfg.edgeWidth || 3;
        this.numSegments = cfg.numSegments || 6;
        this.numRadii = cfg.numRadii || 6;

        this.reroll();

        return this;
    };

    this.reroll = function() {
        var cx = this.canvas.width / 2;
        var cy = this.canvas.height / 2;
        this.segmentSets = this.createSegmentSets(cx, cy, this.numSegments, this.numRadii);

        this.indexSets = [];
        for (var j = 0; j < this.segmentSets.length; j++) {
            // shuffle the indexes so we don't always draw the same colours over other colours
            var indexes = [];
            for (var i = 0; i < this.segmentSets[j].length; i++) {
                indexes.push(i);
            }
            indexes = this.shuffled(indexes);
            this.indexSets.push(indexes);
        }

        this.redraw();
    };

    this.redraw = function() {
        this.ctx.fillStyle = "black";
        this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);

        this.draw();
    };

    this.shuffled = function(a) {
        var b = [];
        while (a.length > 0) {
            b.push(a.splice(Math.random() * a.length, 1)[0]);
        }
        return b;
    };

    this.createSegmentSets = function(cx, cy, numSegments, numRadii) {
        var sets = [];

        for (var i = 0; i < numSegments; i++) {
            var segments = [];

            for (var pos = 0; pos < numRadii; pos++) {
                radius = (cx / numRadii) * (pos + 1);

                segments.push((new Segment()).init(
                    cx, cy, radius,
                    (i / numSegments) * TWO_PI - Math.PI/2,
                    ((i + 1) / numSegments) * TWO_PI - Math.PI/2
                ));

            }

            sets.push(this.shuffled(segments));
        }

        return sets;
    };

    this.draw = function() {
        var colours = ['red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'orange'];

        var minJ = 0;
        var maxJ = this.segmentSets.length - 1;

        for (var j = minJ; j <= maxJ; j++) {
            var segmentSet = this.segmentSets[j];
            var indexes = this.indexSets[j];

            for (var n = 0; n < segmentSet.length; n++) {
                var i = indexes[n];
                var segment = segmentSet[i];
                var nextSegment = this.segmentSets[(j+1) % this.segmentSets.length][i];

                this.ctx.strokeStyle = 'black';
                this.ctx.lineWidth = this.lineWidth + this.edgeWidth;

                this.ctx.beginPath();
                segment.drawConnectTo(this.ctx, nextSegment, 0.0);
                this.ctx.stroke();

                this.ctx.strokeStyle = colours[i % colours.length];
                this.ctx.lineWidth = this.lineWidth;

                this.ctx.beginPath();
                segment.drawConnectTo(this.ctx, nextSegment, 0.5);
                this.ctx.stroke();
            }
        }
    };
};