f8g

Function Map

Ctrl + クリック長押し、離したときに実行。場所で覚えるGUI

// ==UserScript==
// @name          Function Map
// @namespace     http://d.hatena.ne.jp/arikui/
// @include       *
// ==/UserScript==

var fields = [
    new Field(
        new Circle(new Point(window.innerWidth / 2, window.innerHeight / 2), window.innerHeight / 2),
        function(){
            alert("center");
        }
    ),
    new Field(
        new Triangle(new Point(0, window.innerHeight), new Point(300, window.innerHeight), new Point(0, window.innerHeight - 300)),
        function(){
            alert("left bottom");
        }
    ),
    new Field(
        new Rectangle(new Point(window.innerWidth / 2, 0), window.innerWidth / 2, window.innerHeight),
        function(){
            alert("right");
        }
    )
];

fields.canvas = null;

fields.execute = function(x, y, e){
    var point = new Point(x, y);
    this.forEach(function(field){
        if(field.geom.inclusion(point))
            field.exec(field, e);
    });
};

fields.createMap = function(){
    var canvas = fields.canvas || document.createElement("canvas");

    canvas.style.left    = "0px";
    canvas.style.top     = window.pageYOffset + "px";
    canvas.style.display = "block";

    if(!fields.canvas){
        canvas.width  = window.innerWidth;
        canvas.height = window.innerHeight;
        canvas.style.position = "absolute";

        canvas.addEventListener("mouseup", function(e){
            fields.execute(e.clientX, e.clientY, e);
        }, false);
    
        this.forEach(function(field){
            var color = field.color || Field.color(Math.random());
            field.geom.draw(canvas, color);
        });
    
        fields.canvas = canvas;
        document.body.appendChild(canvas);
    }

    return canvas;
};

/* Field */
function Field(geom, exec){
    this.geom = geom;
    this.exec = exec;
};

Field.color = function(value){
    value  = value * 359;
    var ht = value * 6;
    var d  = ht % 360;
    
    var t2 = function(){
        return Math.round(((255 - d) / 360 * 255) / 255 * 255);
    };

    var t3 = function(){
        return Math.round((255 - (255 - d) / 360 * 255) / 255 * 255);
    };

    switch(Math.round(ht / 360)){
        case 0 : var r = [255, t3(), 0]; break;
        case 1 : var r = [t2(), 255, 0]; break;
        case 2 : var r = [0, 255, t3()]; break;
        case 3 : var r = [0, t2(), 255]; break;
        case 4 : var r = [t3(), 0, 255]; break;
        default: var r = [255, 0, t2()]; break;
    }

    return "rgba(" + r + ",0.3)";
}

/* key events */
var isMousedown = false;

var onmousedown = function(e){
    if(!e.ctrlKey || isMousedown)
        return;

    var callback = arguments.callee;
    isMousedown = true;

    setTimeout(function(){
        if(!isMousedown)
            return;

        document.removeEventListener("mousedown", callback, false);

            fields.createMap();
    }, 1000);
};

document.addEventListener("mousedown", onmousedown, false);

document.addEventListener("mouseup", function(e){
    isMousedown = false;
    document.addEventListener("mousedown", onmousedown, false);

    if(fields.canvas)
        fields.canvas.style.display = "none";
}, false);

/* geometories */
// point
function Point(x, y){
    this.x = x;
    this.y = y;
}

// polygon
function Polygon(){
    switch(arguments.length){
        case 3: return Triangle.apply(null, arguments);
    }

    this.points = Array.prototype.slice.apply(arguments, null);
    this.center = new Point(0, 0);

    this.points.forEach(function(point){
        this.center.x += point.x;
        this.center.y += point.y;
    });

    this.center.x /= this.points.length;
    this.center.y /= this.points.length;
}

Polygon.prototype.draw = function(canvas, color){
    var context = canvas.getContext("2d");

    context.fillStyle = color;
    context.beginPath();

    this.points.forEach(function(p, i){
        if(i == 0)
            context.moveTo(p.x, p.y);
        else
            context.lineTo(p.x, p.y);
    });

    context.lineTo(this.points[0].x, this.points[0].y);
    context.closePath();
    context.fill();
}

// triangle
function Triangle(){
    this.points = Array.prototype.slice.apply(arguments, null);
}

Triangle.prototype.inclusion = function(point){
    var rs = [
        (this.points[1].x - this.points[0].x) * (this.points[2].y - this.points[0].y)
      - (this.points[1].y - this.points[0].y) * (this.points[2].x - this.points[0].x),
        (this.points[2].y - this.points[0].y) * (point.x - this.points[0].x)
      - (this.points[2].x - this.points[0].x) * (point.y - this.points[0].y),
        (this.points[1].x - this.points[0].x) * (point.y - this.points[0].y)
      - (this.points[1].y - this.points[0].y) * (point.x - this.points[0].x)
    ];

    return (0 < rs[1] && 0 < rs[2] && 0 < rs[0] - rs[1] - rs[2])
        || (0 > rs[1] && 0 > rs[2] && 0 > rs[0] - rs[1] - rs[2]);
};

Triangle.prototype.draw = Polygon.prototype.draw;

// rectangle
function Rectangle(p, width, height){
    this.width  = width;
    this.height = height;
    this.points = [
        p,
        new Point(p.x + width, p.y),
        new Point(p.x + width, p.y + height),
        new Point(p.x, p.y + height)
    ];
}

Rectangle.prototype.inclusion = function(point){
    return point.x > Math.min(this.points[0].x, this.points[2].x)
        && point.x < Math.max(this.points[0].x, this.points[2].x)
        && point.y > Math.min(this.points[0].y, this.points[2].y)
        && point.y < Math.max(this.points[0].y, this.points[2].y);
}

Rectangle.prototype.draw = function(canvas, color){
    var context = canvas.getContext("2d");
    context.fillStyle = color;
    context.fillRect(this.points[0].x, this.points[0].y, this.width, this.height);
};

// circle
function Circle(point, r){
    this.center = point;
    this.r = r;
}

Circle.prototype.inclusion = function(point){
    var d = Math.sqrt(Math.pow(this.center.x - point.x, 2) + Math.pow(this.center.y - point.y, 2));
    return d <= this.r;
};

Circle.prototype.draw = function(canvas, color){
    var context = canvas.getContext("2d");
    context.fillStyle = color;
    context.beginPath();
    context.arc(this.center.x, this.center.y, this.r, 0, 2 * Math.PI, 1);
    context.fill();
}