/*
 * Hexagonal Rasterizer for Scriptographer.org
 * by xoihazard (http://xoihazard.com)
 * */

var params = {
    shape: 'Hexagon',
    size: 10,
    angle: 15,
    threshold: 0,
    cell_scale: 1.0,
    adaptive_size: false
};

var components = {
    ruler1: {
        type: 'ruler',
        label: 'Grid'
    },
    size: {
        type: 'number',
        label: 'Space',
        min: 1,
        steppers: true
    },
    angle: {
        type: 'number',
        label: 'Angle',
        steppers: true
    },
    ruler2: {
        type: 'ruler',
        label: 'Cell'
    },
    shape: {
        type: 'list',
        label: 'Shape',
        options: ['Hexagon', 'Circle', 'Incircle']
    },
    cell_scale: {
        type: 'number',
        label: 'Rescale',
        min: 0,
        steppers: true
    },
    threshold: {
        type: 'number',
        label: 'Threshold',
        range: [0, 1],
        steppers: true
    },
    adaptive_size: {
        type: 'checkbox',
        label: 'Adaptive rescale'
    },
    ruler3: {
        type: 'ruler'
    },
    btn1: {
        type: 'button',
        value: 'Draw',
        fullSize: true,
        onClick: function() {
            grid.run();
        }
    },
    btn2: {
        type: 'button',
        value: 'Reset',
        fullSize: true,
        onClick: function() {
            palette.reset();
        }
    },
    credits: {
        type: 'text',
        fullSize: true,
        value: 'xoihazard 20140303'
    }
};

var Grid = function() {};

Grid.prototype = {
    run: function() {
        try {
            this.draw();
        } catch (e) {
            Dialog.alert(e);
        }
    },
    draw: function() {
        var target;

        try {
            target = this.createTargetPath();
        } catch (e) {
            throw e;
        }

        if (params.size <= 0 || params.cell_scale <= 0) {
            throw 'Invalid parameter.';
        }

        var group = new Group();

        var bounds = new Rectangle(
                target.bounds.x - params.size,
                target.bounds.y - params.size,
                target.bounds.width + params.size * 2,
                target.bounds.height + params.size * 2
                );

        var radius = Math.ceil(bounds.center.getDistance(bounds.topLeft) / params.size * 2 / 3);

        for (var i = 0; i < radius; i++) {
            var ring = this.getRing(i);

            for (var j = 0; j < ring.length; j++) {
                var hex = ring[j];
                hex.setConvertion(
                        params.size,
                        bounds.center.x,
                        bounds.center.y,
                        params.angle / 180 * Math.PI
                        );

                if (bounds.contains(hex.getPoint())) {
                    var path;
                    var delFlag = false;
                    var scale = params.cell_scale;

                    switch (params.shape) {
                        case 'Hexagon':
                            path = new Path(hex.getHexSegments());
                            path.closePath();
                            break;
                        case 'Circle':
                            path = new Path.Circle(hex.getPoint(), params.size);
                            break;
                        case 'Incircle':
                            path = new Path.Circle(hex.getPoint(), params.size * Math.sqrt(3) / 2);
                            break;
                    }

                    if (path.intersects(target)) {
                        if (params.threshold > 0 || params.adaptive_size) {
                            var cellArea = path.area;
                            var intersect = path.intersect(target);
                            var intersectArea = intersect.area || 0;
                            intersect.remove();
                            var includeRatio = Math.abs(intersectArea / cellArea);

                            if (params.adaptive_size) {
                                scale *= includeRatio;
                            }

                            if (includeRatio < params.threshold) {
                                delFlag = true;
                            }
                        }
                    } else {
                        delFlag = true;
                    }

                    if (delFlag) {
                        path.remove();
                    } else {
                        if (scale !== 1) {
                            path.scale(scale);
                        }
                        group.appendTop(path);
                    }
                }
            }
            document.redraw();
        }
        target.remove();
    },
    createTargetPath: function() {
        var target;
        var paths = [];
        var selectedPaths = document.getItems({type: [Path], selected: true});

        if (!selectedPaths.length) {
            throw 'Please select a path item.';
        } else {
            for (var i = 0; i < selectedPaths.length; i++) {
                paths.push(selectedPaths[i].clone());
            }
        }

        target = new CompoundPath(paths);
        target.fillColor = null;
        target.strokeColor = null;

        return target;
    },
    getRing: function(radius) {
        var results = [];
        var hex = new Hex(-radius, radius);
        if (radius === 0) {
            results.push(hex);
        } else {
            for (var i = 0; i < 6; i++) {
                for (var j = 0; j < radius; j++) {
                    results.push(hex);
                    hex = hex.getNeighbor(i);
                }
            }
        }
        return results;
    }
};

var Hex = function(q, r) {
    this.q = 0 || q;
    this.r = 0 || r;
    this.x = 0;
    this.y = 0;
    this.size = 0;
    this.cx = 0;
    this.cy = 0;
    this.angle = 0;
};
Hex.prototype = {
    setConvertion: function(size, cx, cy, angle) {
        this.size = size;
        this.cx = cx;
        this.cy = cy;
        this.angle = angle;

        var x = this.size * Math.sqrt(3) * (this.q + this.r / 2);
        var y = this.size * 3 / 2 * this.r;
        this.x = x * Math.cos(this.angle) - y * Math.sin(this.angle) + this.cx;
        this.y = y * Math.cos(this.angle) + x * Math.sin(this.angle) + this.cy;
    },
    getPoint: function() {
        return new Point(this.x, this.y);
    },
    getHexSegments: function() {
        var results = [];
        for (var i = 0; i < 6; i++) {
            var angle = this.angle + 2 * Math.PI / 6 * (i + 0.5);
            var x = this.x + this.size * Math.cos(angle);
            var y = this.y + this.size * Math.sin(angle);
            results.push(new Segment(x, y));
        }
        return results;
    },
    getNeighbor: function(direction) {
        var neighbors = [[1, 0], [1, -1], [0, -1], [-1, 0], [-1, 1], [0, 1]];
        direction = ~~direction % 6;
        return new Hex(this.q + neighbors[direction][0], this.r + neighbors[direction][1]);
    }
};

var grid = new Grid();
var palette = new Palette('Hexagonal Rasterizer', components, params);
