Pojostick/ipsr-mosaic

View on GitHub
app/assets/javascripts/mosaics_show.js

Summary

Maintainability
B
4 hrs
Test Coverage
$( function() {
    var steps = $("#APPDATA").attr("steps");
    var WIDTH = parseInt($("#APPDATA").attr("width"));
    var HEIGHT = parseInt($("#APPDATA").attr("height"));

    /** Initialize Variables */
    var COLOR = ['#0173b5', '#edbd10', '#995159', '#f7bb62', '#4c306d', '#9b0a2f', '#413096', '#93d6a7', '#8c6248', '#d65a31', '#ffffff', '#0a0a0a', '#560636', '#9bbddb', '#f4ed90', '#380d09', '#8e8e99', "#c17d58", '#2b6855', '#f7d9e3', '#b57dc6', '#173f37'];
    var grid = [];
    var tiles = [];
    var ID = 0;
    var SIZE = 60;
    var STEPS = 250;
    var DASHES = [];
    var canvas = document.getElementById("display");
    var ctx = canvas.getContext("2d");
    canvas.width = WIDTH * SIZE;
    canvas.height = HEIGHT * SIZE;

    var stepRecord = [];
    var current = -1;
    
    var Tile = function(color, x, y, z) {
        this.id = ++ID;
        tiles[this.id] = this;
        this.x = x;
        this.y = y;
        this.z = z;
        this.color = color;
        this.animation = false;
        this.draw = function(ctx) {
            if (this.z <= 0.01) return;
            if (this.z >= 0.99) this.z = 1;
            var x = Math.round(SIZE * this.x);
            var y = Math.round(SIZE * this.y);
            ctx.globalAlpha = this.z;
            ctx.setLineDash([]);
            ctx.strokeStyle = '#fff';
            ctx.lineWidth = 0.5;
            ctx.strokeRect(x, y, SIZE, SIZE);
            ctx.fillStyle = this.color;
            ctx.fillRect(x, y, SIZE, SIZE);
            ctx.globalAlpha = 1;
        };
    };
    
    var Step = function(tile, ax, ay, az, bx, by, bz, instant) {
        this.id = undefined;
        this.tile = tile;
        this.ax = ax;
        this.ay = ay;
        this.az = az;
        this.bx = bx;
        this.by = by;
        this.bz = bz;
        this.instant = instant;
        this.progress = 0;
        this.do = function() {
            var success = true;
            if (this.progress < 0 || this.progress > 1) success = false;
            if (this.progress < 0) this.progress = 0;
            if (this.progress > 1) this.progress = 1;
            this.tile.x = this.ax + (this.bx - this.ax) * this.progress;
            this.tile.y = this.ay + (this.by - this.ay) * this.progress;
            this.tile.z = this.az + (this.bz - this.az) * this.progress;
            return success;
        };
    };
    
    var redo = function(instant) {
        if (current < stepRecord.length - 1) {
            current++;
            var records = stepRecord[current];
            for (var key in records) {
                var record = records[key];
                record.progress = 0;
                if (record.tile.animation) {
                    var steps = stepRecord[record.tile.animation];
                    for (var key2 in steps) {
                        var step = steps[key2];
                        clearTimeout(step.id);
                        step.progress = 1;
                        step.do();
                    }
                    record.tile.animation = false;
                }
                if (instant || record.instant) {
                    record.progress = 1;
                    record.do();
                } else {
                    var repeat = 0;
                    var speed = Math.abs(record.ax - record.bx) + Math.abs(record.ay - record.by);
                    speed = 0.25 - 0.2 * Math.min(speed / 10, 1);
                    var interpolate = function() {
                        repeat++;
                        record.progress += speed;
                        if (record.do() && repeat < 20) {
                            record.id = setTimeout(interpolate, 25);
                        } else {
                            record.tile.animation = false;
                        }
                    };
                    record.tile.animation = current;
                    record.id = setTimeout(interpolate, 25);
                }
            }
            return true;
        }
        return false;
    };
    
    var undo = function(instant) {
        if (current >= 0) {
            var records = stepRecord[current];
            for (var key in records) {
                var record = records[key];
                record.progress = 1;
                if (record.tile.animation) {
                    var steps = stepRecord[record.tile.animation];
                    for (var key2 in steps) {
                        var step = steps[key2];
                        clearTimeout(step.id);
                        step.progress = 0;
                        step.do();
                    }
                    record.tile.animation = false;
                }
                if (instant || record.instant) {
                    record.progress = 0;
                    record.do();
                } else {
                    var repeat = 0;
                    var speed = Math.abs(record.ax - record.bx) + Math.abs(record.ay - record.by);
                    speed = 0.25 - 0.2 * Math.min(speed / 10, 1);
                    var interpolate = function() {
                        repeat++;
                        record.progress -= speed;
                        if (record.do() && repeat < 20) {
                            record.id = setTimeout(interpolate, 25);
                        } else {
                            record.tile.animation = false;
                        }
                    };
                    record.tile.animation = current;
                    record.id = setTimeout(interpolate, 25);
                }
            }
            current--;
            return true;
        }
        return false;
    };
    
    steps = steps.split(',');
    for (var i = 0; i < steps.length; i++) {
        var step = steps[i].split(' ');
        if (step.length == 1) continue;
        var tileFrom = parseInt(step[0]);
        var tileTo = parseInt(step[1]);
        var xi = tileFrom % WIDTH;
        var yi = Math.floor(tileFrom / WIDTH);
        var xf = tileTo % WIDTH;
        var yf = Math.floor(tileTo / WIDTH);
        if (tileFrom == -1 && tileTo != -1) {
            var tile = new Tile(step[2], xf, yf, 0);
            grid[tileTo] = tile;
            stepRecord.push([new Step(tile, xf, yf, 0, xf, yf, 1)]);
        } else if (tileFrom != -1 && tileTo == -1) {
            var tile = grid[tileFrom];
            stepRecord.push([new Step(tile, xi, yi, 1, xi, yi, 0)]);
        } else if (tileFrom != -1 && tileTo != -1) {
            var tilei = grid[tileFrom];
            var tilef = grid[tileTo];
            grid[tileFrom] = tilef;
            grid[tileTo] = tilei;
            if (tilei && tilef) {
                stepRecord.push([new Step(tilef, xf, yf, 1, xf, yf, 0, true), new Step(tilei, xi, yi, 1, xf, yf, 1)]);
            } else if (tilei) {
                stepRecord.push([new Step(tilei, xi, yi, 1, xf, yf, 1)]);
            } else if (tilef) {
                stepRecord.push([new Step(tilef, xf, yf, 1, xi, yi, 1)]);
            }
        }
        redo(true);
    }
    redo(true);
    STEPS = stepRecord.length;

    var handle = $('#handle');
    var slider = $('#slider');
    $('#slider').slider({
        min: 1,
        max: STEPS + 1,
        value: STEPS + 1
    });
        
    var diff = 0;
    var animate = function() {
        diff = $('#slider').slider('value')- 2 - current;
        if (diff < -10) { // skip for faster progress
            for (var i = 0; i < Math.abs(diff / 10); i++) {
                undo(true);
            }
        } else if (diff > 10) {
            for (var i = 0; i < Math.abs(diff / 10); i++) {
                redo(true);
            }
        }
        if (diff < 0) {
            undo();
        } else if (diff > 0) {
            redo();
        }
        handle.text((current + 2) + ' [' + slider.slider('value') + ']');
        setTimeout(animate, 100);
    };
    setTimeout(animate, 100);
    
    var render = function() {
        requestAnimationFrame(render);
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        for (var i = 0; i < WIDTH * HEIGHT; i++) {
            if (grid[i]) grid[i].draw(ctx);
        }
        for (var key in tiles) {
            tiles[key].draw(ctx);
        }
    };
    
    render();
});