mirror of
https://github.com/Doodle3D/Doodle3D-Slicer.git
synced 2024-11-16 11:07:57 +01:00
2330 lines
63 KiB
JavaScript
2330 lines
63 KiB
JavaScript
//canvas cheat sheet
|
|
//http://cheatsheetworld.com/programming/html5-canvas-cheat-sheet/
|
|
|
|
//TODO
|
|
//Global Composite Operation
|
|
//Linear Gradient
|
|
//Radial Gradient
|
|
//Bezier Curve
|
|
//Circle
|
|
//Touch support
|
|
|
|
"use strict";
|
|
var CAL = {
|
|
name: "Canvas Abstraction Layer",
|
|
version: "1.0",
|
|
author: "Casper Lamboo",
|
|
contact: "casperlamboo@gmail.com"
|
|
};
|
|
|
|
CAL.Math = {
|
|
clamb: function (value, min, max) {
|
|
return (value > min) ? ((value < max) ? value : max) : min;
|
|
},
|
|
randomInt: function (min, max) {
|
|
return Math.round(CAL.Math.random(min, max));
|
|
},
|
|
random: function (min, max) {
|
|
min = min || 0;
|
|
max = max === undefined ? 1 : max;
|
|
|
|
return Math.random()*(max - min) + min;
|
|
},
|
|
sign: function (value) {
|
|
return (value > 0) ? 1 : ((value < 0) ? -1 : 0);
|
|
},
|
|
lineCollision: function (v1, v2, v3, v4) {
|
|
//bron: http://mathworld.wolfram.com/Line-LineIntersection.html
|
|
var intersection = new CAL.Vector(
|
|
((v1.x*v2.y-v1.y*v2.x)*(v3.x-v4.x)-(v1.x-v2.x)*(v3.x*v4.y-v3.y*v4.x)) / ((v1.x-v2.x)*(v3.y-v4.y)-(v1.y-v2.y)*(v3.x-v4.x)),
|
|
((v1.x*v2.y-v1.y*v2.x)*(v3.y-v4.y)-(v1.y-v2.y)*(v3.x*v4.y-v3.y*v4.x)) / ((v1.x-v2.x)*(v3.y-v4.y)-(v1.y-v2.y)*(v3.x-v4.x))
|
|
);
|
|
|
|
var line1 = v1.subtract(v2).length();
|
|
var line2 = v3.subtract(v4).length();
|
|
|
|
var a = line1 >= v1.subtract(intersection).length();
|
|
var b = line1 >= v2.subtract(intersection).length();
|
|
var c = line2 >= v3.subtract(intersection).length();
|
|
var d = line2 >= v4.subtract(intersection).length();
|
|
|
|
return (a && b && c && d) ? intersection : false;
|
|
}
|
|
};
|
|
|
|
CAL.Easings = {
|
|
bounceEaseOut: function (dt, b, c, d) {
|
|
if ((dt /= d) < (1 / 2.75)) {
|
|
return c * (7.5625 * dt * dt) + b;
|
|
}
|
|
else if (dt < (2 / 2.75)) {
|
|
return c * (7.5625 * (dt -= (1.5 / 2.75)) * dt + 0.75) + b;
|
|
}
|
|
else if (dt < (2.5 / 2.75)) {
|
|
return c * (7.5625 * (dt -= (2.25 / 2.75)) * dt + 0.9375) + b;
|
|
}
|
|
else {
|
|
return c * (7.5625 * (dt -= (2.625 / 2.75)) * dt + 0.984375) + b;
|
|
}
|
|
},
|
|
easeIn: function (dt, b, c, d) {
|
|
return c * (dt /= d) * dt + b;
|
|
},
|
|
easeOut: function (dt, b, c, d) {
|
|
return -c * (dt /= d) * (dt - 2) + b;
|
|
},
|
|
easeInOut: function (dt, b, c, d) {
|
|
if ((dt /= d / 2) < 1) {
|
|
return c / 2 * dt * dt + b;
|
|
}
|
|
return -c / 2 * ((--dt) * (dt - 2) - 1) + b;
|
|
},
|
|
strongEaseIn: function (dt, b, c, d) {
|
|
return c * (dt /= d) * dt * dt * dt * dt + b;
|
|
},
|
|
strongEaseOut: function (dt, b, c, d) {
|
|
return c * (( dt = dt / d - 1) * dt * dt * dt * dt + 1) + b;
|
|
},
|
|
strongEaseInOut: function (dt, b, c, d) {
|
|
if ((dt /= d / 2) < 1) {
|
|
return c / 2 * dt * dt * dt * dt * dt + b;
|
|
}
|
|
return c / 2 * ((dt -= 2) * dt * dt * dt * dt + 2) + b;
|
|
},
|
|
linear: function (dt, b, c, d) {
|
|
return c * dt / d + b;
|
|
}
|
|
};
|
|
|
|
|
|
//this doesn't work, everything is an instance of Object in JavaScript
|
|
/*Object.prototype.clone = function () {
|
|
var object = {};
|
|
for (var i in this) {
|
|
var element = this[i];
|
|
|
|
object[i] = element.clone ? element.clone() : element;
|
|
}
|
|
return object;
|
|
};
|
|
Object.prototype.foreach = function (callback) {
|
|
for (var i in this) {
|
|
var element = this[i];
|
|
|
|
callback(element, i);
|
|
}
|
|
};*/
|
|
|
|
|
|
Array.prototype.foreach = function (callback, scope) {
|
|
for (var i = 0; i < this.length; i ++) {
|
|
var element = this[i];
|
|
|
|
if ((scope !== undefined) ? callback.call(scope, element, i) : callback(element, i)) {
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
Array.prototype.foreachReverse = function (callback, scope) {
|
|
for (var i = this.length-1; i >= 0; i --) {
|
|
var element = this[i];
|
|
|
|
if ((scope !== undefined) ? callback.call(scope, element, i) : callback(element, i)) {
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
Array.prototype.max = function () {
|
|
var max = -Infinity;
|
|
this.foreach(function (element) {
|
|
if (element > max) {
|
|
max = element;
|
|
}
|
|
});
|
|
return max;
|
|
};
|
|
Array.prototype.min = function () {
|
|
var min = Infinity;
|
|
this.foreach(function (element) {
|
|
if (element < min) {
|
|
min = element;
|
|
}
|
|
});
|
|
return min;
|
|
};
|
|
Array.prototype.clone = function () {
|
|
var array = [];
|
|
this.foreach(function (element) {
|
|
array.push((element.clone !== undefined) ? element.clone() : element);
|
|
});
|
|
return array;
|
|
};
|
|
Array.prototype.remove = function () {
|
|
for (var i = 0; i < arguments.length; i ++) {
|
|
var element = arguments[i];
|
|
var index = this.indexOf(element);
|
|
if (index !== -1) {
|
|
this.splice(index, 1);
|
|
}
|
|
}
|
|
};
|
|
|
|
var requestAnimFrame = (function () {
|
|
return requestAnimationFrame || webkitRequestAnimationFrame || mozRequestAnimationFrame || function (callback) {
|
|
setTimeout(callback, 1000/60);
|
|
};
|
|
})();
|
|
|
|
CAL.Vector = function (x, y) {
|
|
this.x = x || 0;
|
|
this.y = y || 0;
|
|
};
|
|
CAL.Vector.prototype.add = function (vector) {
|
|
var x = this.x + vector.x;
|
|
var y = this.y + vector.y;
|
|
|
|
return new CAL.Vector(x, y);
|
|
};
|
|
CAL.Vector.prototype.subtract = function (vector) {
|
|
var x = this.x - vector.x;
|
|
var y = this.y - vector.y;
|
|
|
|
return new CAL.Vector(x, y);
|
|
};
|
|
CAL.Vector.prototype.scale = function (scalar) {
|
|
var x = this.x * scalar;
|
|
var y = this.y * scalar;
|
|
|
|
return new CAL.Vector(x, y);
|
|
};
|
|
CAL.Vector.prototype.rotate = function (angle) {
|
|
var cos = Math.cos(angle);
|
|
var sin = Math.sin(angle);
|
|
|
|
var x = cos*this.x - sin*this.y;
|
|
var y = sin*this.x + cos*this.y;
|
|
|
|
return new CAL.Vector(x, y);
|
|
};
|
|
CAL.Vector.prototype.multiply = function (vector) {
|
|
var x = this.x * vector.x;
|
|
var y = this.y * vector.y;
|
|
|
|
return new CAL.Vector(x, y);
|
|
};
|
|
CAL.Vector.prototype.length = function () {
|
|
return Math.sqrt(this.x*this.x + this.y*this.y);
|
|
};
|
|
CAL.Vector.prototype.normal = function () {
|
|
return new CAL.Vector(this.y, -this.x);
|
|
};
|
|
CAL.Vector.prototype.normalize = function () {
|
|
var length = this.length();
|
|
|
|
var x = this.x/length;
|
|
var y = this.y/length;
|
|
|
|
return new CAL.Vector(x, y);
|
|
};
|
|
CAL.Vector.prototype.angle = function () {
|
|
return Math.atan2(this.y, this.x);
|
|
};
|
|
CAL.Vector.prototype.dot = function (vector) {
|
|
return this.x * vector.x + this.y * vector.y;
|
|
};
|
|
CAL.Vector.prototype.cross = function (vector) {
|
|
return this.x * vector.y - this.y * vector.x;
|
|
};
|
|
CAL.Vector.prototype.round = function () {
|
|
var x = Math.round(this.x);
|
|
var y = Math.round(this.y);
|
|
|
|
return new CAL.Vector(x, y);
|
|
};
|
|
CAL.Vector.prototype.applyMatrix = function (matrix) {
|
|
var m = matrix.matrix;
|
|
|
|
var x = m[0]*this.x + m[1]*this.y + m[2];
|
|
var y = m[3]*this.x + m[4]*this.y + m[5];
|
|
|
|
return new CAL.Vector(x, y);
|
|
};
|
|
CAL.Vector.prototype.clone = function () {
|
|
return new CAL.Vector(this.x, this.y);
|
|
};
|
|
CAL.Vector.prototype.draw = function (context, x, y) {
|
|
var end = new CAL.Vector(this.x + x, this.y + y);
|
|
var arrowOrigin = new CAL.Vector(x, y).add(this.subtract(this.normalize().scale(10)));
|
|
var left = this.normal().normalize().scale(10).add(arrowOrigin);
|
|
var right = this.normal().normalize().scale(-10).add(arrowOrigin);
|
|
|
|
context.beginPath();
|
|
context.moveTo(x, y);
|
|
context.lineTo(end.x, end.y);
|
|
context.moveTo(left.x, left.y);
|
|
context.lineTo(end.x, end.y);
|
|
context.lineTo(right.x, right.y);
|
|
|
|
context.stroke();
|
|
};
|
|
|
|
CAL.Matrix = function (options) {
|
|
options = options || {};
|
|
|
|
if (options.matrix !== undefined && options.sx !== undefined && options.sy !== undefined && options.rotation !== undefined && options.x !== undefined && options.y !== undefined) {
|
|
this.matrix = options.matrix;
|
|
this._sx = options.sx;
|
|
this._sy = options.sy;
|
|
this._rotation = options.rotation;
|
|
this._x = options.x;
|
|
this._y = options.y;
|
|
}
|
|
else if (options instanceof Array || options.matrix) {
|
|
this.matrix = options.matrix || options;
|
|
|
|
this._sx = CAL.Math.sign(this.matrix[0])*Math.sqrt(Math.pow(this.matrix[0], 2) + Math.pow(this.matrix[1], 2));
|
|
this._sy = CAL.Math.sign(this.matrix[4])*Math.sqrt(Math.pow(this.matrix[3], 2) + Math.pow(this.matrix[4], 2));
|
|
this._rotation = Math.atan2(-this.matrix[1], this.matrix[0]);
|
|
this._x = this.matrix[2];
|
|
this._y = this.matrix[5];
|
|
//source: http://math.stackexchange.com/questions/13150/extracting-rotation-scale-values-from-2d-transformation-matrix
|
|
//BUG doesn't convert right when both sx and sy aren't 1
|
|
}
|
|
else {
|
|
this._sx = options.sx !== undefined ? options.sx : 1;
|
|
this._sy = options.sy !== undefined ? options.sy : 1;
|
|
this._x = options.x || 0;
|
|
this._y = options.y || 0;
|
|
|
|
if (options.rotation === undefined) {
|
|
this._rotation = 0;
|
|
|
|
this.matrix = [
|
|
this._sx, 0, this._x,
|
|
0, this._sy, this._y
|
|
];
|
|
}
|
|
else {
|
|
this._rotation = options.rotation;
|
|
this.updateMatrix();
|
|
}
|
|
}
|
|
};
|
|
CAL.Matrix.prototype = {
|
|
get sx () {
|
|
return this._sx;
|
|
},
|
|
set sx (sx) {
|
|
this._sx = sx;
|
|
this.updateMatrix();
|
|
},
|
|
get sy () {
|
|
return this._sy;
|
|
},
|
|
set sy (sy) {
|
|
this._sy = sy;
|
|
this.updateMatrix();
|
|
},
|
|
get rotation () {
|
|
return this._rotation;
|
|
},
|
|
set rotation (rotation) {
|
|
this._rotation = rotation;
|
|
this.updateMatrix();
|
|
},
|
|
get x () {
|
|
return this._x;
|
|
},
|
|
set x (x) {
|
|
this._x = this.matrix[2] = x;
|
|
},
|
|
get y () {
|
|
return this._y;
|
|
},
|
|
set y (y) {
|
|
this._y = this.matrix[5] = y;
|
|
}
|
|
};
|
|
CAL.Matrix.prototype.updateMatrix = function () {
|
|
this.matrix = [
|
|
this._sx * Math.cos(this._rotation), this._sy * -Math.sin(this._rotation), this._x,
|
|
this._sx * Math.sin(this._rotation), this._sy * Math.cos(this._rotation), this._y
|
|
];
|
|
};
|
|
CAL.Matrix.prototype.multiplyMatrix = function (matrix) {
|
|
var a = this.matrix;
|
|
var b = matrix.matrix;
|
|
var translation = new CAL.Vector(b[2], b[5]).applyMatrix(this);
|
|
|
|
return new CAL.Matrix([
|
|
a[0]*b[0] + a[3]*b[1], a[1]*b[0] + a[4]*b[1], translation.x,
|
|
a[0]*b[3] + a[3]*b[4], a[1]*b[3] + a[4]*b[4], translation.y,
|
|
]);
|
|
};
|
|
CAL.Matrix.prototype.inverse = function () {
|
|
var m = this.matrix;
|
|
return new CAL.Matrix([
|
|
m[4], -m[1], -m[2],
|
|
-m[3], m[0], -m[5]
|
|
]);
|
|
};
|
|
CAL.Matrix.prototype.translate = function (x, y) {
|
|
this.x += x;
|
|
this.y += y;
|
|
|
|
this.matrix[2] = this.x;
|
|
this.matrix[5] = this.y;
|
|
|
|
return this;
|
|
};
|
|
CAL.Matrix.prototype.setMatrix = function (matrix) {
|
|
this.matrix = matrix.matrix.clone();
|
|
this._x = matrix._x;
|
|
this._y = matrix._y;
|
|
this._sx = matrix._sx;
|
|
this._sy = matrix._sy;
|
|
this._rotation = matrix._rotation;
|
|
};
|
|
CAL.Matrix.prototype.setContext = function (context) {
|
|
var m = this.matrix;
|
|
context.transform(m[0], m[3], m[1], m[4], m[2], m[5]);
|
|
};
|
|
CAL.Matrix.prototype.rotateAroundAbsolute = function (angle, center) {
|
|
if (angle !== 0) {
|
|
var center = center
|
|
.subtract(new CAL.Vector(this.x, this.y))
|
|
.rotate(-this.rotation)
|
|
.multiply(new CAL.Vector(1/this.sx, 1/this.sy));
|
|
|
|
this.rotateAroundRelative(angle, center);
|
|
}
|
|
};
|
|
CAL.Matrix.prototype.rotateAroundRelative = function (angle, center) {
|
|
if (angle !== 0) {
|
|
var before = center.applyMatrix(this);
|
|
|
|
this.rotation += angle;
|
|
var after = center.applyMatrix(this);
|
|
|
|
var offset = before.subtract(after);
|
|
|
|
this._x += offset.x;
|
|
this._y += offset.y;
|
|
this.updateMatrix();
|
|
}
|
|
};
|
|
CAL.Matrix.prototype.clone = function () {
|
|
return new CAL.Matrix({
|
|
matrix: this.matrix.clone(),
|
|
sx: this._sx,
|
|
sy: this._sy,
|
|
rotation: this._rotation,
|
|
x: this._x,
|
|
y: this._y
|
|
});
|
|
};
|
|
|
|
CAL.Draw = function (centerX, centerY, numberWidth, numberHeight, options) {
|
|
CAL.Matrix.call(this, options);
|
|
|
|
this.visible = options.visible !== undefined ? options.visible : true;
|
|
this.active = options.active || false;
|
|
this.depth = options.depth || 0;
|
|
|
|
this.alpha = (typeof options.alpha === "number") ? options.alpha : 1;
|
|
|
|
this.centerX = centerX || 0;
|
|
this.centerY = centerY || 0;
|
|
this.index = 0;
|
|
|
|
this.numberWidth = numberWidth || 1;
|
|
this.numberHeight = numberHeight || 1;
|
|
this.length = this.numberWidth*this.numberHeight;
|
|
};
|
|
CAL.Draw.prototype = Object.create(CAL.Matrix.prototype);
|
|
CAL.Draw.prototype.draw = function (context, matrix) {
|
|
context.save();
|
|
(matrix || this).setContext(context);
|
|
context.globalAlpha = this.alpha;
|
|
this.drawSimple(context, this.index, 0, 0);
|
|
context.restore();
|
|
};
|
|
CAL.Draw.prototype.drawSimple = function (context, number, x, y) {
|
|
var sx = (number % this.numberWidth)*this.width;
|
|
var sy = Math.floor(number/this.numberWidth)*this.height;
|
|
|
|
context.drawImage(this.image, sx, sy, this.width, this.height, x-this.centerX, y-this.centerY, this.width, this.height);
|
|
};
|
|
CAL.Draw.prototype.drawAlpha = function (context, number, x, y, alpha) {
|
|
context.globalAlpha = alpha;
|
|
this.drawSimple(context, number, x, y);
|
|
context.globalAlpha = 1;
|
|
};
|
|
CAL.Draw.prototype.drawAngle = function (context, number, x, y, angle) {
|
|
context.save();
|
|
context.translate(x, y);
|
|
context.rotate(angle);
|
|
this.drawSimple(context, number, 0, 0);
|
|
context.restore();
|
|
};
|
|
CAL.Draw.prototype.drawScale = function (context, number, x, y, width, height) {
|
|
var sx = (number % this.numberWidth)*this.width;
|
|
var sy = Math.floor(number/this.numberWidth)*this.height;
|
|
|
|
context.drawImage(this.image, sx, sy, this.width, this.height, x-this.centerX, y-this.centerY, width, height);
|
|
};
|
|
CAL.Draw.prototype.drawContain = function (context, number, x, y, width, height) {
|
|
if (width/height > this.width/this.height) {
|
|
x = x + (width-height/this.height*this.width)/2;
|
|
width = height/this.height*this.width;
|
|
this.drawScale(context, number, x, y, width, height);
|
|
}
|
|
else {
|
|
y = y + (height-width/this.width*this.height)/2;
|
|
height = width/this.width*this.height;
|
|
this.drawScale(context, number, x, y, width, height);
|
|
}
|
|
};
|
|
|
|
CAL.Surface = function (options) {
|
|
options = options || {};
|
|
CAL.Draw.call(this, options.centerX, options.centerY, options.numberWidth, options.numberHeight, options);
|
|
|
|
this.clearColor = options.clearColor || false;
|
|
|
|
this.setCanvas(options.canvas || document.createElement("canvas"));
|
|
|
|
this.setSize(options.width, options.height);
|
|
};
|
|
CAL.Surface.prototype = Object.create(CAL.Draw.prototype);
|
|
CAL.Surface.prototype.setSize = function (width, height) {
|
|
this.image.width = width || this.image.width;
|
|
this.image.height = height || this.image.height;
|
|
|
|
this.width = this.image.width/this.numberWidth;
|
|
this.height = this.image.height/this.numberHeight;
|
|
};
|
|
CAL.Surface.prototype.setCanvas = function (canvas) {
|
|
this.image = canvas;
|
|
this.context = canvas.getContext("2d");
|
|
};
|
|
CAL.Surface.prototype.clear = function () {
|
|
if (this.clearColor) {
|
|
this.clearColor.setColor(this.context);
|
|
this.context.fillRect(0, 0, this.image.width, this.image.height);
|
|
}
|
|
else {
|
|
this.context.clearRect(0, 0, this.image.width, this.image.height);
|
|
}
|
|
};
|
|
CAL.Surface.prototype.getImageData = function (x, y, width, height) {
|
|
var x = x || 0;
|
|
var y = y || 0;
|
|
var width = width || this.image.width;
|
|
var height = height || this.image.height;
|
|
|
|
return this.context.getImageData(x, y, width, height);
|
|
};
|
|
CAL.Surface.prototype.getDataURL = function () {
|
|
return this.image.toDataURL();
|
|
};
|
|
CAL.Surface.prototype.blur = (function () {
|
|
//source: http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html
|
|
//author: Mario Klingemann
|
|
|
|
var mul_table = [512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,289,287,285,282,280,278,275,273,271,269,267,265,263,261,259];
|
|
var shg_table = [9,11,12,13,13,14,14,15,15,15,15,16,16,16,16,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24 ];
|
|
|
|
return function (radius, x, y, width, height) {
|
|
x = x || 0;
|
|
y = y || 0;
|
|
width = this.image.width || 0;
|
|
height = this.image.height || 0;
|
|
var imageData = this.getImageData(x, y, width, height);
|
|
|
|
var x, y, i, p, yp, yi, yw, r_sum, g_sum, b_sum, a_sum,
|
|
r_out_sum, g_out_sum, b_out_sum, a_out_sum,
|
|
r_in_sum, g_in_sum, b_in_sum, a_in_sum,
|
|
pr, pg, pb, pa, rbs;
|
|
|
|
var div = radius + radius + 1;
|
|
var w4 = width << 2;
|
|
var widthMinus1 = width - 1;
|
|
var heightMinus1 = height - 1;
|
|
var radiusPlus1 = radius + 1;
|
|
var sumFactor = radiusPlus1 * (radiusPlus1 + 1) / 2;
|
|
var pixels = imageData.data;
|
|
|
|
var stackStart = {r: 0, g: 0, b: 0, next: null};
|
|
var stack = stackStart;
|
|
for (i = 1; i < div; i ++) {
|
|
stack = stack.next = {r: 0, g: 0, b: 0, next: null};
|
|
if (i == radiusPlus1) {
|
|
var stackEnd = stack;
|
|
}
|
|
}
|
|
stack.next = stackStart;
|
|
var stackIn = null;
|
|
var stackOut = null;
|
|
|
|
yw = yi = 0;
|
|
|
|
var mul_sum = mul_table[radius];
|
|
var shg_sum = shg_table[radius];
|
|
|
|
for (y = 0; y < height; y ++) {
|
|
r_in_sum = g_in_sum = b_in_sum = a_in_sum = r_sum = g_sum = b_sum = a_sum = 0;
|
|
|
|
r_out_sum = radiusPlus1 * (pr = pixels[yi]);
|
|
g_out_sum = radiusPlus1 * (pg = pixels[yi+1]);
|
|
b_out_sum = radiusPlus1 * (pb = pixels[yi+2]);
|
|
a_out_sum = radiusPlus1 * (pa = pixels[yi+3]);
|
|
|
|
r_sum += sumFactor * pr;
|
|
g_sum += sumFactor * pg;
|
|
b_sum += sumFactor * pb;
|
|
a_sum += sumFactor * pa;
|
|
|
|
stack = stackStart;
|
|
|
|
for (i = 0; i < radiusPlus1; i ++) {
|
|
stack.r = pr;
|
|
stack.g = pg;
|
|
stack.b = pb;
|
|
stack.a = pa;
|
|
stack = stack.next;
|
|
}
|
|
|
|
for (i = 1; i < radiusPlus1; i ++) {
|
|
p = yi + ((widthMinus1 < i ? widthMinus1 : i) << 2);
|
|
r_sum += (stack.r = (pr = pixels[p])) * (rbs = radiusPlus1 - i);
|
|
g_sum += (stack.g = (pg = pixels[p+1])) * rbs;
|
|
b_sum += (stack.b = (pb = pixels[p+2])) * rbs;
|
|
a_sum += (stack.a = (pa = pixels[p+3])) * rbs;
|
|
|
|
r_in_sum += pr;
|
|
g_in_sum += pg;
|
|
b_in_sum += pb;
|
|
a_in_sum += pa;
|
|
|
|
stack = stack.next;
|
|
}
|
|
|
|
stackIn = stackStart;
|
|
stackOut = stackEnd;
|
|
for (x = 0; x < width; x ++) {
|
|
pixels[yi+3] = pa = (a_sum * mul_sum) >> shg_sum;
|
|
if (pa === 0) {
|
|
pixels[yi] = pixels[yi+1] = pixels[yi+2] = 0;
|
|
}
|
|
else {
|
|
pa = 255 / pa;
|
|
pixels[yi] = ((r_sum * mul_sum) >> shg_sum) * pa;
|
|
pixels[yi+1] = ((g_sum * mul_sum) >> shg_sum) * pa;
|
|
pixels[yi+2] = ((b_sum * mul_sum) >> shg_sum) * pa;
|
|
}
|
|
|
|
|
|
r_sum -= r_out_sum;
|
|
g_sum -= g_out_sum;
|
|
b_sum -= b_out_sum;
|
|
a_sum -= a_out_sum;
|
|
|
|
r_out_sum -= stackIn.r;
|
|
g_out_sum -= stackIn.g;
|
|
b_out_sum -= stackIn.b;
|
|
a_out_sum -= stackIn.a;
|
|
|
|
p = (yw + ((p = x + radius + 1) < widthMinus1 ? p : widthMinus1)) << 2;
|
|
|
|
r_in_sum += (stackIn.r = pixels[p]);
|
|
g_in_sum += (stackIn.g = pixels[p+1]);
|
|
b_in_sum += (stackIn.b = pixels[p+2]);
|
|
a_in_sum += (stackIn.a = pixels[p+3]);
|
|
|
|
r_sum += r_in_sum;
|
|
g_sum += g_in_sum;
|
|
b_sum += b_in_sum;
|
|
a_sum += a_in_sum;
|
|
|
|
stackIn = stackIn.next;
|
|
|
|
r_out_sum += (pr = stackOut.r);
|
|
g_out_sum += (pg = stackOut.g);
|
|
b_out_sum += (pb = stackOut.b);
|
|
a_out_sum += (pa = stackOut.a);
|
|
|
|
r_in_sum -= pr;
|
|
g_in_sum -= pg;
|
|
b_in_sum -= pb;
|
|
a_in_sum -= pa;
|
|
|
|
stackOut = stackOut.next;
|
|
|
|
yi += 4;
|
|
}
|
|
yw += width;
|
|
}
|
|
|
|
|
|
for (x = 0; x < width; x ++) {
|
|
g_in_sum = b_in_sum = a_in_sum = r_in_sum = g_sum = b_sum = a_sum = r_sum = 0;
|
|
|
|
yi = x << 2;
|
|
r_out_sum = radiusPlus1 * (pr = pixels[yi]);
|
|
g_out_sum = radiusPlus1 * (pg = pixels[yi+1]);
|
|
b_out_sum = radiusPlus1 * (pb = pixels[yi+2]);
|
|
a_out_sum = radiusPlus1 * (pa = pixels[yi+3]);
|
|
|
|
r_sum += sumFactor * pr;
|
|
g_sum += sumFactor * pg;
|
|
b_sum += sumFactor * pb;
|
|
a_sum += sumFactor * pa;
|
|
|
|
stack = stackStart;
|
|
|
|
for (i = 0; i < radiusPlus1; i ++) {
|
|
stack.r = pr;
|
|
stack.g = pg;
|
|
stack.b = pb;
|
|
stack.a = pa;
|
|
stack = stack.next;
|
|
}
|
|
|
|
yp = width;
|
|
|
|
for (i = 1; i <= radius; i ++) {
|
|
yi = (yp + x) << 2;
|
|
|
|
r_sum += (stack.r = (pr = pixels[yi])) * (rbs = radiusPlus1 - i);
|
|
g_sum += (stack.g = (pg = pixels[yi+1])) * rbs;
|
|
b_sum += (stack.b = (pb = pixels[yi+2])) * rbs;
|
|
a_sum += (stack.a = (pa = pixels[yi+3])) * rbs;
|
|
|
|
r_in_sum += pr;
|
|
g_in_sum += pg;
|
|
b_in_sum += pb;
|
|
a_in_sum += pa;
|
|
|
|
stack = stack.next;
|
|
|
|
if (i < heightMinus1) {
|
|
yp += width;
|
|
}
|
|
}
|
|
|
|
yi = x;
|
|
stackIn = stackStart;
|
|
stackOut = stackEnd;
|
|
for (y = 0; y < height; y ++) {
|
|
p = yi << 2;
|
|
pixels[p+3] = pa = (a_sum * mul_sum) >> shg_sum;
|
|
if (pa > 0) {
|
|
pa = 255 / pa;
|
|
pixels[p] = ((r_sum * mul_sum) >> shg_sum) * pa;
|
|
pixels[p+1] = ((g_sum * mul_sum) >> shg_sum) * pa;
|
|
pixels[p+2] = ((b_sum * mul_sum) >> shg_sum) * pa;
|
|
}
|
|
else {
|
|
pixels[p] = pixels[p+1] = pixels[p+2] = 0;
|
|
}
|
|
|
|
r_sum -= r_out_sum;
|
|
g_sum -= g_out_sum;
|
|
b_sum -= b_out_sum;
|
|
a_sum -= a_out_sum;
|
|
|
|
r_out_sum -= stackIn.r;
|
|
g_out_sum -= stackIn.g;
|
|
b_out_sum -= stackIn.b;
|
|
a_out_sum -= stackIn.a;
|
|
|
|
p = (x + (((p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1) * width)) << 2;
|
|
|
|
r_sum += (r_in_sum += (stackIn.r = pixels[p]));
|
|
g_sum += (g_in_sum += (stackIn.g = pixels[p+1]));
|
|
b_sum += (b_in_sum += (stackIn.b = pixels[p+2]));
|
|
a_sum += (a_in_sum += (stackIn.a = pixels[p+3]));
|
|
|
|
stackIn = stackIn.next;
|
|
|
|
r_out_sum += (pr = stackOut.r);
|
|
g_out_sum += (pg = stackOut.g);
|
|
b_out_sum += (pb = stackOut.b);
|
|
a_out_sum += (pa = stackOut.a);
|
|
|
|
r_in_sum -= pr;
|
|
g_in_sum -= pg;
|
|
b_in_sum -= pb;
|
|
a_in_sum -= pa;
|
|
|
|
stackOut = stackOut.next;
|
|
|
|
yi += width;
|
|
}
|
|
}
|
|
return imageData;
|
|
};
|
|
})();
|
|
|
|
CAL.Draw.prototype.drawBlur = (function () {
|
|
var surface = new CAL.Surface();
|
|
|
|
return function (context, number, x, y, radius) {
|
|
if (radius > 0) {
|
|
surface.setSize(this.width + 2*radius, this.height + 2*radius);
|
|
this.drawSimple(surface.context, number, this.centerX+radius, this.centerY+radius);
|
|
var imageData = surface.blur(radius);
|
|
|
|
context.putImageData(imageData, x-this.centerX - radius, y-this.centerY - radius);
|
|
|
|
/*surface.setSize(this.width, this.height);
|
|
this.drawSimple(surface.context, number, this.centerX, this.centerY);
|
|
var imageData = surface.blur(radius);
|
|
|
|
context.putImageData(imageData, x-this.centerX, y-this.centerY);*/
|
|
}
|
|
else {
|
|
this.drawSimple(context, number, x, y);
|
|
}
|
|
};
|
|
})();
|
|
|
|
CAL.Group = function (options) {
|
|
options = options || {};
|
|
CAL.Surface.call(this, options);
|
|
|
|
this.active = true;
|
|
this.visible = true;
|
|
|
|
this.objects = [];
|
|
this.useCanvas = options.useCanvas || false;
|
|
|
|
this.clearCanvas = true;
|
|
this.drawCanvas = true;
|
|
|
|
this.mouse = {
|
|
x: null,
|
|
y: null,
|
|
startX: null,
|
|
startY: null,
|
|
deltaX: null,
|
|
deltaY: null,
|
|
down: false,
|
|
moved: false
|
|
};
|
|
};
|
|
CAL.Group.prototype = Object.create(CAL.Surface.prototype);
|
|
CAL.Group.prototype.updateEvents = function () {
|
|
var scope = this;
|
|
this.image.onmousedown = function (event) {
|
|
if (scope.useCanvas) {
|
|
scope.mouse.x = scope.mouse.startX = Math.round(scope.image.width / scope.image.clientWidth * event.offsetX);
|
|
scope.mouse.y = scope.mouse.startY = Math.round(scope.image.height / scope.image.clientHeight * event.offsetY);
|
|
scope.mouse.deltaX = 0;
|
|
scope.mouse.deltaY = 0;
|
|
scope.mouse.down = true;
|
|
scope.mouse.moved = false;
|
|
|
|
scope.mouseDown(scope.mouse, this);
|
|
}
|
|
};
|
|
this.image.onmouseup = function (event) {
|
|
if (scope.useCanvas) {
|
|
scope.mouse.x = Math.round(scope.image.width / scope.image.clientWidth * event.offsetX);
|
|
scope.mouse.y = Math.round(scope.image.height / scope.image.clientHeight * event.offsetY);
|
|
scope.mouse.down = false;
|
|
|
|
scope.mouseUp(scope.mouse, this);
|
|
|
|
scope.mouse.startX = null;
|
|
scope.mouse.startY = null;
|
|
scope.mouse.deltaX = null;
|
|
scope.mouse.deltaY = null;
|
|
scope.mouse.moved = false;
|
|
}
|
|
};
|
|
this.image.onmousemove = function (event) {
|
|
if (scope.useCanvas) {
|
|
scope.mouse.x = Math.round(scope.image.width / scope.image.clientWidth * event.offsetX);
|
|
scope.mouse.y = Math.round(scope.image.height / scope.image.clientHeight * event.offsetY);
|
|
if (scope.mouse.down) {
|
|
scope.mouse.moved = true;
|
|
scope.mouse.deltaX = scope.mouse.x - scope.mouse.startX;
|
|
scope.mouse.deltaY = scope.mouse.y - scope.mouse.startY;
|
|
}
|
|
}
|
|
};
|
|
//this.image.ontouchstart = function (event) {scope.touchStart(event);};
|
|
//this.image.ontouchmove = function (event) {scope.touchMove(event);};
|
|
//this.image.ontouchend = function (event) {scope.touchEnd(event);};
|
|
};
|
|
CAL.Group.prototype.setCanvas = function (canvas) {
|
|
/*this.image.onmousedown = null;
|
|
this.image.onmouseup = null;
|
|
this.image.onmousemove = null;
|
|
this.image.ontouchstart = null;
|
|
this.image.ontouchmove = null;
|
|
this.image.ontouchend = null;*/
|
|
|
|
this.image = canvas;
|
|
this.context = canvas.getContext("2d");
|
|
|
|
this.drawCanvas = true;
|
|
this.updateEvents();
|
|
};
|
|
CAL.Group.prototype.add = function () {
|
|
for (var i = 0; i < arguments.length; i ++) {
|
|
var object = arguments[i];
|
|
if (this.objects.indexOf(object) === -1) {
|
|
this.objects.push(object);
|
|
if (object.init) {
|
|
object.init(this);
|
|
}
|
|
}
|
|
}
|
|
this.sort();
|
|
this.drawCanvas = true;
|
|
};
|
|
CAL.Group.prototype.remove = function () {
|
|
for (var i = 0; i < arguments.length; i ++) {
|
|
var object = arguments[i];
|
|
this.objects.remove(object);
|
|
if (object.active && object.remove !== undefined) {
|
|
object.remove(this);
|
|
}
|
|
}
|
|
this.drawCanvas = true;
|
|
};
|
|
CAL.Group.prototype.sort = function () {
|
|
this.objects.sort(function (a, b) {
|
|
return (a.depth || 0) - (b.depth || 0);
|
|
});
|
|
};
|
|
CAL.Group.prototype.keyDown = function (keyCode) {
|
|
for (var i = this.objects.length-1; i >= 0; i --) {
|
|
var object = this.objects[i];
|
|
if (object.active && object.keyDown !== undefined) {
|
|
if (object.keyDown(keyCode, this)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
CAL.Group.prototype.keyUp = function (keyCode) {
|
|
for (var i = this.objects.length-1; i >= 0; i --) {
|
|
var object = this.objects[i];
|
|
if (object.active && object.keyUp !== undefined) {
|
|
if (object.keyUp(keyCode, this)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
CAL.Group.prototype.mouseDown = function (mouse) {
|
|
for (var i = this.objects.length-1; i >= 0; i --) {
|
|
var object = this.objects[i];
|
|
if (object.useCanvas !== true && object.active && object.mouseDown !== undefined) {
|
|
if (object.mouseDown(mouse, this)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
CAL.Group.prototype.mouseUp = function (mouse) {
|
|
for (var i = this.objects.length-1; i >= 0; i --) {
|
|
var object = this.objects[i];
|
|
if (object.useCanvas !== true && object.active && object.mouseUp !== undefined) {
|
|
if (object.mouseUp(mouse, this)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
CAL.Group.prototype.step = function (deltaTime) {
|
|
for (var i = 0; i < this.objects.length; i ++) {
|
|
var object = this.objects[i];
|
|
if (object.active && object.step !== undefined) {
|
|
object.step(deltaTime, this);
|
|
}
|
|
}
|
|
|
|
if (this.clearCanvas && this.useCanvas) {
|
|
this.clear();
|
|
}
|
|
|
|
if (this.drawCanvas && this.useCanvas) {
|
|
this.draw();
|
|
}
|
|
|
|
this.clearCanvas = false;
|
|
this.drawCanvas = false;
|
|
};
|
|
CAL.Group.prototype.draw = function (context, matrix) {
|
|
context = this.useCanvas ? this.context : context;
|
|
matrix = this.useCanvas ? this : matrix;
|
|
|
|
for (var i = 0; i < this.objects.length; i ++) {
|
|
var object = this.objects[i];
|
|
if (object.useCanvas !== true && object.visible && object.draw !== undefined) {
|
|
if (object instanceof CAL.Matrix) {
|
|
object.draw(context, matrix.multiplyMatrix(object));
|
|
}
|
|
else {
|
|
object.draw(context, matrix);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
CAL.Scene = function () {
|
|
CAL.Group.call(this, {useCanvas: true});
|
|
|
|
this.lastTime = new Date().getTime();
|
|
|
|
this.keysDown = [];
|
|
this.focus = true;
|
|
|
|
var scope = this;
|
|
window.onkeydown = function (event) {
|
|
if (!scope.keysDown[event.keyCode]) {
|
|
scope.keysDown[event.keyCode] = true;
|
|
scope.keyDown(event.keyCode);
|
|
}
|
|
};
|
|
window.onkeyup = function (event) {
|
|
scope.keysDown[event.keyCode] = false;
|
|
|
|
scope.keyUp(event.keyCode);
|
|
};
|
|
window.onblur = function (event) {
|
|
this.focus = false;
|
|
};
|
|
window.onfocus = function (event) {
|
|
scope.lastTime = new Date().getTime();
|
|
this.focus = true;
|
|
};
|
|
};
|
|
CAL.Scene.prototype = Object.create(CAL.Group.prototype);
|
|
CAL.Scene.prototype.cycle = function () {
|
|
if (this.focus) {
|
|
var currentTime = new Date().getTime();
|
|
var deltaTime = currentTime-this.lastTime;
|
|
this.lastTime = currentTime;
|
|
|
|
this.step(deltaTime);
|
|
}
|
|
};
|
|
CAL.Scene = new CAL.Scene();
|
|
|
|
CAL.Image = function (source, centerX, centerY, numberWidth, numberHeight, options) {
|
|
options = options || {};
|
|
CAL.Draw.call(this, centerX, centerY, numberWidth, numberHeight, options);
|
|
|
|
this.image = new Image();
|
|
this.source = source;
|
|
};
|
|
CAL.Image.prototype = Object.create(CAL.Draw.prototype);
|
|
CAL.Image.prototype.load = function (callback) {
|
|
var scope = this;
|
|
this.image.onload = function () {
|
|
scope.loaded = true;
|
|
|
|
scope.width = scope.image.width/scope.numberWidth;
|
|
scope.height = scope.image.height/scope.numberHeight;
|
|
|
|
if (callback !== undefined) {
|
|
callback();
|
|
}
|
|
};
|
|
this.image.src = this.source;
|
|
};
|
|
|
|
CAL.ImageLoader = function () {
|
|
this.images = [];
|
|
|
|
for (var i = 0; i < arguments.length; i ++) {
|
|
var image = arguments[i];
|
|
this.images.push(image);
|
|
}
|
|
};
|
|
CAL.ImageLoader.prototype.add = function () {
|
|
for (var i = 0; i < arguments.length; i ++) {
|
|
var image = arguments[i];
|
|
if (this.images.indexOf(image) === -1) {
|
|
this.images.push(image);
|
|
}
|
|
}
|
|
};
|
|
CAL.ImageLoader.prototype.remove = function () {
|
|
for (var i = 0; i < arguments.length; i ++) {
|
|
var image = arguments[i];
|
|
this.images.remove(image);
|
|
}
|
|
};
|
|
CAL.ImageLoader.prototype.load = function (callback) {
|
|
var imagesToLoad = this.images.length;
|
|
for (var i = 0; i < this.images.length; i ++) {
|
|
var image = this.images[i];
|
|
image.load(function () {
|
|
imagesToLoad --;
|
|
if (imagesToLoad === 0 && callback !== undefined) {
|
|
callback();
|
|
}
|
|
}, this);
|
|
};
|
|
};
|
|
|
|
CAL.Color = function () {
|
|
if (typeof arguments[0] === "number" && typeof arguments[1] === "number" && typeof arguments[2] === "number") {
|
|
this.r = arguments[0];
|
|
this.g = arguments[1];
|
|
this.b = arguments[2];
|
|
this.a = typeof arguments[3] === "number" ? arguments[3] : 1;
|
|
}
|
|
else if (typeof arguments[0] === "number") {
|
|
var hex = Math.floor(arguments[0]);
|
|
|
|
this.r = hex >> 16 & 255;
|
|
this.g = hex >> 8 & 255;
|
|
this.b = hex & 255;
|
|
this.a = 1;
|
|
}
|
|
else {
|
|
this.r = 0;
|
|
this.g = 0;
|
|
this.b = 0;
|
|
this.a = 1;
|
|
}
|
|
};
|
|
CAL.Color.prototype.setStroke = function (context) {
|
|
context.strokeStyle = "rgba("+this.r+", "+this.g+", "+this.b+", "+this.a+")";
|
|
};
|
|
CAL.Color.prototype.setFill = function (context) {
|
|
context.fillStyle = "rgba("+this.r+", "+this.g+", "+this.b+", "+this.a+")";
|
|
};
|
|
CAL.Color.prototype.setColor = function (context) {
|
|
this.setStroke(context);
|
|
this.setFill(context);
|
|
};
|
|
|
|
CAL.Tween = function (object, attributes, duration, options) {
|
|
options = options || {};
|
|
this.visible = false;
|
|
this.active = true;
|
|
this.depth = -10000;
|
|
|
|
this.object = object;
|
|
this.attributes = attributes;
|
|
this.timer = 0;
|
|
this.duration = duration;
|
|
this.easing = options.easing || CAL.Easings.linear;
|
|
this.callback = options.callback;
|
|
|
|
this.begin = {};
|
|
for (var i in attributes) {
|
|
this.begin[i] = this.object[i];
|
|
}
|
|
|
|
this.change = {};
|
|
for (var i in attributes) {
|
|
this.change[i] = attributes[i] - this.begin[i];
|
|
}
|
|
|
|
this.drawCanvas = options.drawCanvas !== undefined ? options.drawCanvas : true;
|
|
this.clearCanvas = options.clearCanvas !== undefined ? options.clearCanvas : false;
|
|
};
|
|
CAL.Tween.prototype.start = function () {
|
|
this.t = 0;
|
|
this.active = true;
|
|
};
|
|
CAL.Tween.prototype.stop = function () {
|
|
this.t = 0;
|
|
this.active = false;
|
|
};
|
|
CAL.Tween.prototype.pause = function () {
|
|
this.active = false;
|
|
};
|
|
CAL.Tween.prototype.resume = function () {
|
|
this.active = true;
|
|
};
|
|
CAL.Tween.prototype.step = function (deltaTime, group) {
|
|
this.timer += deltaTime;
|
|
|
|
if (this.timer < this.duration) {
|
|
|
|
for (var i in this.attributes) {
|
|
var dt = this.timer;
|
|
var d = this.duration;
|
|
var b = this.begin[i];
|
|
var c = this.change[i];
|
|
|
|
this.object[i] = this.easing(dt, b, c, d);
|
|
}
|
|
}
|
|
else {
|
|
for (var i in this.attributes) {
|
|
this.object[i] = this.attributes[i];
|
|
}
|
|
if (this.callback !== undefined) {
|
|
this.callback();
|
|
}
|
|
group.remove(this);
|
|
}
|
|
|
|
if (this.clearCanvas) {
|
|
group.clearCanvas = true;
|
|
}
|
|
if (this.drawCanvas) {
|
|
group.drawCanvas = true;
|
|
}
|
|
};
|
|
|
|
CAL.TimeLine = function (options) {
|
|
options = options || {};
|
|
|
|
this.visible = false;
|
|
this.active = true;
|
|
this.depth = -10000;
|
|
|
|
this.moments = [];
|
|
this.autoRemove = (options.autoRemove !== undefined) ? options.autoremove : true;
|
|
this.loop = (options.loop !== undefined) ? options.loop : false;
|
|
this.t = 0;
|
|
|
|
CAL.Scene.add(this);
|
|
};
|
|
CAL.TimeLine.prototype = {
|
|
addMoment: function (time, callback) {
|
|
this.moments.push({
|
|
time: time,
|
|
callback: callback
|
|
});
|
|
},
|
|
removeMoment: function (remove) {
|
|
for (var i = 0; i < this.moments.length; i ++) {
|
|
var moment = this.moments[i];
|
|
|
|
if (moment === remove || moment.time === remove || moment.callback === remove) {
|
|
this.moments.remove(moment);
|
|
}
|
|
}
|
|
},
|
|
start: function () {
|
|
this.t = 0;
|
|
this.active = true;
|
|
},
|
|
stop: function () {
|
|
this.t = 0;
|
|
this.active = false;
|
|
},
|
|
pause: function () {
|
|
this.active = false;
|
|
},
|
|
resume: function () {
|
|
this.active = true;
|
|
},
|
|
step: function (dt) {
|
|
var newTime = this.t + dt;
|
|
var remove = true;
|
|
|
|
for (var i = 0; i < this.moments.length; i ++) {
|
|
var moment = this.moments[i];
|
|
if (moment.time >= this.t) {
|
|
if (moment.time < newTime) {
|
|
moment.callback();
|
|
}
|
|
else {
|
|
remove = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (remove && this.loop) {
|
|
this.t = 0;
|
|
}
|
|
else if (remove && this.autoRemove) {
|
|
CAL.Scene.remove(this);
|
|
}
|
|
this.t = newTime;
|
|
}
|
|
};
|
|
|
|
CAL.Shape = function (options) {
|
|
options = options || {};
|
|
CAL.Matrix.call(this, options);
|
|
|
|
this.visible = options.visible !== undefined ? options.visible : true;
|
|
this.active = false;
|
|
this.depth = options.depth || 0;
|
|
this.lines = [];
|
|
|
|
this.closePath = options.closePath !== undefined ? options.closePath : true;
|
|
this.lineColor = options.lineColor !== undefined ? options.lineColor : new CAL.Color();
|
|
this.shapeColor = options.shapeColor !== undefined ? options.shapeColor : new CAL.Color();
|
|
this.lineWidth = options.lineWidth || 1;
|
|
this.lineJoin = options.lineJoin || "miter";
|
|
this.lineCap = options.lineCap || "square";
|
|
|
|
this.points = options.points || [];
|
|
};
|
|
CAL.Shape.prototype = Object.create(CAL.Matrix.prototype);
|
|
CAL.Shape.prototype.addPoint = function () {
|
|
for (var i = 0; i < arguments.length; i ++) {
|
|
var point = arguments[i];
|
|
this.points.push(point);
|
|
}
|
|
//this.update();
|
|
};
|
|
CAL.Shape.prototype.hit = function (x, y) {
|
|
|
|
for (var i = 0; i < this.points.length; i ++) {
|
|
if (new CAL.Vector(x, y).subtract(this.points[i].applyMatrix(this)).dot(this.getNormal(i)) > 0) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
};
|
|
CAL.Shape.prototype.setContext = function (context, matrix) {
|
|
var matrix = matrix || this;
|
|
context.beginPath();
|
|
for (var i = 0; i < this.points.length; i ++) {
|
|
var point = this.points[i].applyMatrix(matrix);
|
|
context.lineTo(point.x, point.y);
|
|
}
|
|
if (this.closePath) {
|
|
context.closePath();
|
|
}
|
|
};
|
|
CAL.Shape.prototype.getBoundingBox = function () {
|
|
var minX = Infinity;
|
|
var minY = Infinity;
|
|
var maxX = -Infinity;
|
|
var maxY = -Infinity;
|
|
|
|
for (var i = 0; i < this.points.length; i ++) {
|
|
var point = this.points[i];
|
|
minX = point.x < minX ? point.x : minX;
|
|
minY = point.y < minY ? point.y : minY;
|
|
maxX = point.x > maxX ? point.x : maxX;
|
|
maxY = point.y > maxY ? point.y : maxY;
|
|
};
|
|
|
|
return {x: minX, y: minY, width: maxX-minX, height: maxY-minY};
|
|
};
|
|
CAL.Shape.prototype.getNormal = function (i) {
|
|
var pointA = this.points[(i+1)%this.points.length].applyMatrix(this);
|
|
var pointB = this.points[i].applyMatrix(this);
|
|
return pointA.subtract(pointB).normal().normalize();
|
|
};
|
|
CAL.Shape.prototype.clip = function (context, matrix) {
|
|
this.setContext(context, matrix);
|
|
context.clip();
|
|
};
|
|
CAL.Shape.prototype.fill = function (context, matrix) {
|
|
this.setContext(context, matrix);
|
|
|
|
this.shapeColor.setFill(context);
|
|
|
|
context.fill();
|
|
};
|
|
CAL.Shape.prototype.stroke = function (context, matrix) {
|
|
this.setContext(context, matrix);
|
|
|
|
context.lineColor = this.lineColor;
|
|
context.lineWidth = this.lineWidth;
|
|
context.lineJoin = this.lineJoin;
|
|
context.lineCap = this.lineCap;
|
|
|
|
this.lineColor.setStroke(context);
|
|
|
|
context.stroke();
|
|
};
|
|
CAL.Shape.prototype.draw = function (context, matrix) {
|
|
this.setContext(context, matrix);
|
|
|
|
if (this.shapeColor) {
|
|
this.shapeColor.setFill(context);
|
|
context.fill();
|
|
}
|
|
|
|
if (this.lineColor) {
|
|
context.lineColor = this.lineColor;
|
|
context.lineWidth = this.lineWidth;
|
|
context.lineJoin = this.lineJoin;
|
|
context.lineCap = this.lineCap;
|
|
this.lineColor.setStroke(context);
|
|
context.stroke();
|
|
}
|
|
};
|
|
|
|
CAL.BezierPoint = function (position, controlPointA, controlPointB) {
|
|
this.position = position || new CAL.Vector(0, 0);
|
|
this.controlPointA = controlPointA || new CAL.Vector(0, 0);
|
|
this.controlPointB = controlPointB || new CAL.Vector(0, 0);
|
|
}
|
|
CAL.BezierPoint.prototype.applyMatrix = function (matrix) {
|
|
var position = this.position.applyMatrix(matrix);
|
|
var controlPointA = this.controlPointA.applyMatrix(matrix);
|
|
var controlPointB = this.controlPointB.applyMatrix(matrix);
|
|
|
|
return new CAL.BezierPoint(position, controlPointA, controlPointB);
|
|
};
|
|
CAL.BezierPoint.prototype.draw = function (context) {
|
|
var leftHandle = this.controlPointA.add(this.position);
|
|
var rightHandle = this.controlPointB.add(this.position);
|
|
|
|
context.strokeStyle = "#09F";
|
|
|
|
context.beginPath();
|
|
context.moveTo(leftHandle.x, leftHandle.y);
|
|
context.lineTo(this.position.x, this.position.y);
|
|
context.lineTo(rightHandle.x, rightHandle.y);
|
|
context.stroke();
|
|
|
|
context.beginPath();
|
|
context.arc(this.position.x, this.position.y, 10, 0, Math.PI*2, true);
|
|
context.stroke();
|
|
|
|
context.beginPath();
|
|
context.arc(leftHandle.x, leftHandle.y, 5, 0, Math.PI*2, true);
|
|
context.stroke();
|
|
|
|
context.beginPath();
|
|
context.arc(rightHandle.x, rightHandle.y, 5, 0, Math.PI*2, true);
|
|
context.stroke();
|
|
};
|
|
/*CAL.Shape = function (options) {
|
|
options = options || {};
|
|
CAL.Matrix.call(this, options);
|
|
|
|
this.visible = options.visible !== undefined ? options.visible : true;
|
|
this.active = false;
|
|
this.depth = options.depth || 0;
|
|
|
|
this.closePath = options.closePath !== undefined ? options.closePath : true;
|
|
this.lineColor = options.lineColor !== undefined ? options.lineColor : new CAL.Color();
|
|
this.shapeColor = options.shapeColor !== undefined ? options.shapeColor : new CAL.Color();
|
|
this.lineWidth = options.lineWidth || 1;
|
|
this.lineJoin = options.lineJoin || "miter";
|
|
this.lineCap = options.lineCap || "square";
|
|
|
|
this.points = options.points || [];
|
|
this.precision = options.precision || 3;
|
|
|
|
this.lines = [];
|
|
this.length = 0;
|
|
this.closed = false;
|
|
}
|
|
CAL.Shape.prototype.init = function (group) {
|
|
for (var i = 0; i < this.points.length; i ++) {
|
|
var point = this.points[i];
|
|
if (point instanceof CAL.Vector) {
|
|
point = new CAL.BezierPoint(point);
|
|
}
|
|
this.points[i] = point;
|
|
}
|
|
|
|
this.update();
|
|
};
|
|
CAL.Shape.prototype.addPoint = function () {
|
|
for (var i = 0; i < arguments.length; i ++) {
|
|
var point = arguments[i];
|
|
|
|
if (point instanceof CAL.Vector) {
|
|
point = new CAL.BezierPoint(point);
|
|
}
|
|
if (point instanceof CAL.BezierPoint) {
|
|
this.points.push(point);
|
|
}
|
|
}
|
|
|
|
this.update();
|
|
};
|
|
CAL.Shape.prototype.collisionBox = function (vec1, vec2) {
|
|
for (var i = 0; i < this.lines.length; i ++) {
|
|
var point = this.lines[i];
|
|
|
|
if (point.x > vec1.x && point.y > vec1.y && point.x < vec2.x && point.y < vec2.y) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
CAL.Shape.prototype.getNormal = function (i) {
|
|
var pointA = this.lines[(i+1)%this.lines.length].applyMatrix(this);
|
|
var pointB = this.lines[i].applyMatrix(this);
|
|
return pointA.subtract(pointB).normal().normalize();
|
|
};
|
|
CAL.Shape.prototype.collisionPoint = function (x, y) {
|
|
for (var i = 0; i < this.lines.length; i ++) {
|
|
if (new CAL.Vector(x, y).subtract(this.lines[i].applyMatrix(this)).dot(this.getNormal(i)) > 0) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
};
|
|
CAL.Shape.prototype.removePoint = function () {
|
|
for (var i = 0; i < arguments.length; i ++) {
|
|
|
|
var argument = arguments[i];
|
|
|
|
if (typeof argument === "number") {
|
|
var index = argument;
|
|
}
|
|
else if (argument instanceof CAL.BezierPoint || argument instanceof CAL.Vector) {
|
|
var index = this.getPointIndex(argument);
|
|
}
|
|
|
|
if (index !== -1) {
|
|
this.points.splice(index, 1);
|
|
}
|
|
}
|
|
|
|
this.update();
|
|
};
|
|
|
|
CAL.Shape.prototype.getPointIndex = function (point) {
|
|
if (point instanceof CAL.BezierPoint) {
|
|
return this.points.indexOf(argument);
|
|
}
|
|
else if (point instanceof CAL.Vector) {
|
|
for (i = 0; i < this.points.length; i ++) {
|
|
var length = this.points[i].position.subtract(point).length();
|
|
|
|
if (length === 0) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
};
|
|
|
|
CAL.Shape.prototype.numberPoints = function () {
|
|
return this.points.length;
|
|
};
|
|
|
|
CAL.Shape.prototype.insertPoint = function (point, index) {
|
|
if (point instanceof CAL.Vector) {
|
|
point = new CAL.BezierPoint(point);
|
|
}
|
|
if (point instanceof CAL.BezierPoint) {
|
|
this.points.splice(index, 0, point);
|
|
|
|
this.update();
|
|
}
|
|
};
|
|
CAL.Shape.prototype.setClosed = function (closed) {
|
|
this.closed = closed;
|
|
this.update();
|
|
};
|
|
|
|
CAL.Shape.prototype.setPrecision = function (precision) {
|
|
this.precision = precision;
|
|
this.update();
|
|
};
|
|
|
|
CAL.Shape.prototype.makeSmooth = function (strength, start, end) {
|
|
start = start || 0;
|
|
end = end || this.points.length;
|
|
|
|
if (this.closed === false) {
|
|
start = Math.max(start, 1);
|
|
end = Math.min(end, this.points.length-1);
|
|
}
|
|
else {
|
|
start = Math.max(start, 0);
|
|
end = Math.min(end, this.points.length);
|
|
}
|
|
|
|
for (var i = start; i < end; i ++) {
|
|
var p0 = this.points[i-1];
|
|
var p1 = this.points[i];
|
|
var p2 = this.points[i+1];
|
|
|
|
if (typeof(p0) === "undefined") {
|
|
p0 = this.points[this.points.length-1];
|
|
}
|
|
else if (typeof(p2) === "undefined") {
|
|
p2 = this.points[0];
|
|
}
|
|
|
|
var a = p0.position.subtract(p1.position);
|
|
var b = p2.position.subtract(p1.position);
|
|
|
|
var length = (a.length()+b.length())/4;
|
|
|
|
var direction = a.normalize().add(b.scale(-1).normalize()).normalize();
|
|
|
|
p1.controlPointA = direction.scale(length*strength);
|
|
p1.controlPointB = direction.scale(-length*strength);
|
|
}
|
|
|
|
this.update();
|
|
};
|
|
|
|
CAL.Shape.prototype.calculateBezierPoints = function (p0, p1) {
|
|
var array = [];
|
|
|
|
if (p0.controlPointB.length() === 0 && p1.controlPointA.length() === 0) {
|
|
var angle = p1.position.subtract(p0.position).angle();
|
|
|
|
array.push({
|
|
x: p0.position.x,
|
|
y: p0.position.y,
|
|
angle: angle
|
|
}, {
|
|
x: p1.position.x,
|
|
y: p1.position.y,
|
|
angle: angle
|
|
});
|
|
}
|
|
else {
|
|
var point = {
|
|
x: p1.position.x,
|
|
y: p1.position.y,
|
|
angle: p1.controlPointB.direction()
|
|
}
|
|
|
|
array = array.concat(
|
|
recursiveBezier(
|
|
p0.position,
|
|
p0.position.add(p0.controlPointB),
|
|
p1.position.add(p1.controlPointA),
|
|
p1.position,
|
|
this.precision
|
|
),
|
|
[point]
|
|
);
|
|
}
|
|
|
|
return array;
|
|
};
|
|
|
|
CAL.Shape.prototype.update = function () {
|
|
if (this.points.length >= 2) {
|
|
if (this.points[0].controlPointB.length() !== 0) {
|
|
var point = {
|
|
x: this.points[0].position.x,
|
|
y: this.points[0].position.y,
|
|
angle: this.points[0].controlPointB.direction()
|
|
};
|
|
this.lines = [point];
|
|
}
|
|
|
|
for (var i = 0; i < this.points.length-1; i ++) {
|
|
var p0 = this.points[i];
|
|
var p1 = this.points[i+1];
|
|
|
|
this.lines = this.lines.concat(this.calculateBezierPoints(p0, p1));
|
|
}
|
|
|
|
if (this.closed === true) {
|
|
|
|
var p0 = this.points[this.points.length-1];
|
|
var p1 = this.points[0];
|
|
|
|
this.lines = this.lines.concat(this.calculateBezierPoints(p0, p1));
|
|
}
|
|
|
|
var pathLength = 0;
|
|
this.lines[0].position = 0;
|
|
|
|
for (var i = 0; i < this.lines.length-1; i ++) {
|
|
|
|
var p0 = this.lines[i];
|
|
var p1 = this.lines[i+1];
|
|
var line = new CAL.Vector(p1.x, p1.y).subtract(p0);
|
|
pathLength += line.length();
|
|
|
|
p1.position = pathLength;
|
|
}
|
|
|
|
this.length = pathLength;
|
|
}
|
|
};
|
|
|
|
CAL.Shape.prototype.getPosition = function (t) {
|
|
var t = Math.max(Math.min(t, this.length), 0);
|
|
|
|
for (var i = 1; true; i ++) {
|
|
var p0 = this.lines[i-1];
|
|
var p1 = this.lines[i];
|
|
|
|
if (p1.position >= t) {
|
|
t = (t-p0.position) / (p1.position-p0.position);
|
|
|
|
var position = new CAL.Vector(p0.x, p0.y).scale(1-t).add(new CAL.Vector(p1.x, p1.y).scale(t));
|
|
var angle = p0.angle + ((((p1.angle-p0.angle)%360)+540)%360-180)*t;
|
|
|
|
return {
|
|
x: position.x,
|
|
y: position.y,
|
|
angle: angle
|
|
};
|
|
}
|
|
}
|
|
};
|
|
|
|
CAL.Shape.prototype.setContextPart = function (context, begin, end) {
|
|
var beginPos = this.getPosition(begin);
|
|
var endPos = this.getPosition(end);
|
|
|
|
context.beginPath();
|
|
context.moveTo(beginPos.x, beginPos.y);
|
|
|
|
for (var i = 0; i < this.lines.length; i ++) {
|
|
var line = this.lines[i];
|
|
|
|
if (line.position > begin) {
|
|
|
|
context.lineTo(line.x, line.y);
|
|
if (line.position > end) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
context.lineTo(endPos.x, endPos.y);
|
|
};
|
|
|
|
CAL.Shape.prototype.setContext = function (context) {
|
|
this.setContextPart(context, 0, this.length);
|
|
};
|
|
|
|
CAL.Shape.prototype.setClippingPart = function (context, begin, end) {
|
|
context.save();
|
|
this.setContextPart(begin, end);
|
|
context.clip();
|
|
};
|
|
|
|
CAL.Shape.prototype.setClipping = function (context) {
|
|
this.setClippingPart(context, 0, this.length);
|
|
};
|
|
|
|
CAL.Shape.prototype.drawPart = function (context, begin, end, color, width, cap) {
|
|
this.setContextPart(context, begin, end);
|
|
|
|
context.strokeStyle = color || "black";
|
|
context.lineWidth = width || 1;
|
|
context.lineCap = cap || 'butt';
|
|
context.stroke();
|
|
};
|
|
CAL.Shape.prototype.setContext = function (context, matrix) {
|
|
var matrix = matrix || this;
|
|
context.beginPath();
|
|
for (var i = 0; i < this.lines.length; i ++) {
|
|
var point = this.lines[i]//.applyMatrix(matrix);
|
|
context.lineTo(point.x, point.y);
|
|
}
|
|
if (this.closePath) {
|
|
context.closePath();
|
|
}
|
|
};
|
|
|
|
CAL.Shape.prototype.fill = function (context, matrix) {
|
|
this.setContext(context, matrix);
|
|
|
|
this.shapeColor.setFill(context);
|
|
|
|
context.fill();
|
|
};
|
|
CAL.Shape.prototype.stroke = function (context, matrix) {
|
|
this.setContext(context, matrix);
|
|
context.lineColor = this.lineColor;
|
|
context.lineWidth = this.lineWidth;
|
|
context.lineJoin = this.lineJoin;
|
|
context.lineCap = this.lineCap;
|
|
|
|
this.lineColor.setStroke(context);
|
|
|
|
context.stroke();
|
|
};
|
|
|
|
CAL.Shape.prototype.draw = function (context, matrix) {
|
|
if (this.shapeColor) {
|
|
this.shapeColor.setFill(context);
|
|
context.fill();
|
|
}
|
|
|
|
if (this.lineColor) {
|
|
this.stroke(context, matrix);
|
|
}
|
|
this.debugDraw(context);
|
|
};
|
|
CAL.Shape.prototype.debugDraw = function (context) {
|
|
context.beginPath();
|
|
context.lineWidth = 1;
|
|
|
|
for (var i = 0; i < this.points.length; i ++) {
|
|
var point = this.points[i];
|
|
|
|
point.draw(context);
|
|
}
|
|
|
|
context.beginPath();
|
|
|
|
for (var i = 0; i < this.lines.length; i ++) {
|
|
var point = this.lines[i];
|
|
|
|
context.lineTo(point.x, point.y);
|
|
context.arc(point.x, point.y, 2, 0, Math.PI*2, true);
|
|
context.lineTo(point.x, point.y);
|
|
}
|
|
|
|
context.strokeStyle = "black";
|
|
context.stroke();
|
|
};
|
|
|
|
CAL.Shape.prototype.getSize = function () {
|
|
var width = 0;
|
|
var height = 0;
|
|
|
|
for (var i = 0; i < this.lines.length; i ++) {
|
|
var line = this.lines[i];
|
|
|
|
width = Math.max(width, Math.ceil(line.x));
|
|
height = Math.max(width, Math.ceil(line.y));
|
|
}
|
|
|
|
return {
|
|
width: width,
|
|
height: height
|
|
};
|
|
};
|
|
|
|
|
|
CAL.BezierPoint = function (position, controlPointA, controlPointB) {
|
|
this.position = position || new CAL.Vector(0, 0);
|
|
this.controlPointA = controlPointA || new CAL.Vector(0, 0);
|
|
this.controlPointB = controlPointB || new CAL.Vector(0, 0);
|
|
}
|
|
CAL.BezierPoint.prototype.applyMatrix = function (matrix) {
|
|
var position = this.position.applyMatrix(matrix);
|
|
var controlPointA = this.controlPointA.applyMatrix(matrix);
|
|
var controlPointB = this.controlPointB.applyMatrix(matrix);
|
|
|
|
return new CAL.BezierPoint(position, controlPointA, controlPointB);
|
|
};
|
|
CAL.BezierPoint.prototype.draw = function (context) {
|
|
var leftHandle = this.controlPointA.add(this.position);
|
|
var rightHandle = this.controlPointB.add(this.position);
|
|
|
|
context.strokeStyle = "#09F";
|
|
|
|
context.beginPath();
|
|
context.moveTo(leftHandle.x, leftHandle.y);
|
|
context.lineTo(this.position.x, this.position.y);
|
|
context.lineTo(rightHandle.x, rightHandle.y);
|
|
context.stroke();
|
|
|
|
context.beginPath();
|
|
context.arc(this.position.x, this.position.y, 10, 0, Math.PI*2, true);
|
|
context.stroke();
|
|
|
|
context.beginPath();
|
|
context.arc(leftHandle.x, leftHandle.y, 5, 0, Math.PI*2, true);
|
|
context.stroke();
|
|
|
|
context.beginPath();
|
|
context.arc(rightHandle.x, rightHandle.y, 5, 0, Math.PI*2, true);
|
|
context.stroke();
|
|
};
|
|
|
|
function recursiveBezier (p1, p2, p3, p4, precision) {
|
|
//source: http://antigrain.com/research/adaptive_bezier/
|
|
//source: http://en.wikipedia.org/wiki/B%C3%A9zier_curve#Derivative
|
|
|
|
var p12 = p1.add(p2).scale(0.5);
|
|
var p23 = p2.add(p3).scale(0.5);
|
|
var p34 = p3.add(p4).scale(0.5);
|
|
var p123 = p12.add(p23).scale(0.5);
|
|
var p234 = p23.add(p34).scale(0.5);
|
|
var p1234 = p123.add(p234).scale(0.5);
|
|
|
|
var d = p4.subtract(p1);
|
|
|
|
var d2 = Math.abs((p2.x - p4.x) * d.y - (p2.y - p4.y) * d.x);
|
|
var d3 = Math.abs((p3.x - p4.x) * d.y - (p3.y - p4.y) * d.x);
|
|
|
|
//if (Math.abs(p1.x + p3.x - p2.x - p2.x) + Math.abs(p1.y + p3.y - p2.y - p2.y) + Math.abs(p2.x + p4.x - p3.x - p3.x) + Math.abs(p2.y + p4.y - p3.y - p3.y) <= precision) {
|
|
if (Math.pow((d2 + d3), 2) < precision * d.dot(d)) {
|
|
|
|
//var t = 0.5;
|
|
//var derivative = (p2.subtract(p1)).scale(Math.pow(3*(1-t), 2)).add((p3.subtract(p2)).scale(6*(1-t)*t)).add((p4.subtract(p3)).scale(Math.pow(3*t, 2)));
|
|
var derivative = (p2.subtract(p1)).scale(2.25).add((p3.subtract(p2)).scale(1.5)).add((p4.subtract(p3)).scale(2.25));
|
|
|
|
var point = {
|
|
x: p1234.x,
|
|
y: p1234.y,
|
|
angle: derivative.direction()
|
|
}
|
|
|
|
return [point];
|
|
}
|
|
else {
|
|
|
|
return [].concat(
|
|
recursiveBezier(p1, p12, p123, p1234, precision),
|
|
recursiveBezier(p1234, p234, p34, p4, precision)
|
|
);
|
|
}
|
|
}*/
|
|
|
|
CAL.Text = function (options) {
|
|
options = options || {};
|
|
CAL.Matrix.call(this, options);
|
|
|
|
this.visible = options.visible !== undefined ? options.visible : true;
|
|
this.active = options.active !== undefined ? options.active : true;
|
|
this.depth = options.depth || 0;
|
|
this.text = options.text || "";
|
|
this.style = options.style || "normal";
|
|
this.variant = options.variant || "normal";
|
|
this.weight = options.weight || "normal";
|
|
this.size = options.size || 12;
|
|
this.font = options.font || "Arial";
|
|
|
|
this.textAlign = options.textAlign || "left";
|
|
|
|
this.color = options.color || new CAL.Color();
|
|
this.alpha = typeof options.alpha === "number" ? options.alpha : 1;
|
|
};
|
|
CAL.Text.prototype = Object.create(CAL.Matrix.prototype);
|
|
CAL.Text.prototype.drawText = function (context, text, x, y) {
|
|
context.font = [this.style, this.variant, this.weight, this.size+"px", this.font].join(" ");
|
|
context.textAlign = this.textAlign;
|
|
this.color.setColor(context);
|
|
context.fillText(text, x, y);
|
|
};
|
|
CAL.Text.prototype.drawTextAlpha = function (context, text, x, y, apha) {
|
|
context.font = [this.style, this.variant, this.weight, this.size+"px", this.font].join(" ");
|
|
context.globalAlpha = apha;
|
|
this.color.setColor(context);
|
|
context.fillText(text, x, y);
|
|
context.globalAlpha = 1;
|
|
};
|
|
CAL.Text.prototype.draw = function (context, matrix) {
|
|
context.save();
|
|
matrix.setContext(context);
|
|
context.globalAlpha = this.alpha;
|
|
this.drawText(context, this.text, 0, 0);
|
|
context.restore();
|
|
};
|
|
CAL.Text.prototype.clone = function () {
|
|
return new CAL.text({
|
|
style : this.style,
|
|
variant : this.variant,
|
|
weight : this.weight,
|
|
size : this.size,
|
|
font : this.font,
|
|
color : this.color.clone()
|
|
});
|
|
};
|
|
|
|
CAL.KeyListener = function (options) {
|
|
options = options || {};
|
|
|
|
this.visible = false;
|
|
this.active = options.active !== undefined ? option.active : true;
|
|
this.depth = -10000;
|
|
|
|
this.actions = options.actions || {};
|
|
|
|
CAL.Scene.add(this);
|
|
};
|
|
CAL.KeyListener.prototype.add = function (key, callback) {
|
|
this.actions[key] = callback;
|
|
};
|
|
CAL.KeyListener.prototype.keyDown = function (key) {
|
|
if (this.actions[key]) {
|
|
this.actions[key]();
|
|
}
|
|
};
|
|
|
|
CAL.Physics = function () {
|
|
CAL.Scene.add(this);
|
|
this.depth = -1000;
|
|
this.active = true;
|
|
this.visible = false;
|
|
|
|
this.objects = [];
|
|
this.manifolds = [];
|
|
this.forces = [];
|
|
}
|
|
CAL.Physics.prototype.keyDown = function () {
|
|
//DEBUG REASONS
|
|
//EASIER TO DEBUG ON KEYDOWN
|
|
|
|
CAL.Scene.clear();
|
|
|
|
this.test(120);
|
|
|
|
polygon.debugDraw(CAL.Scene.context);
|
|
box.debugDraw(CAL.Scene.context);
|
|
|
|
};
|
|
CAL.Physics.prototype.add = function () {
|
|
for (var i = 0; i < arguments.length; i ++) {
|
|
var object = arguments[i];
|
|
|
|
if (object instanceof CAL.PhysicsObject && this.objects.indexOf(object) === -1) {
|
|
for (var j = 0; j < this.objects.length; j ++) {
|
|
this.manifolds.push(new CAL.Manifold(object, this.objects[j]));
|
|
this.manifolds.push(new CAL.Manifold(this.objects[j], object));
|
|
}
|
|
|
|
this.objects.push(object);
|
|
}
|
|
else if (object instanceof CAL.Force && this.forces.indexOf(object) === -1) {
|
|
this.forces.push(object);
|
|
}
|
|
}
|
|
};
|
|
CAL.Physics.prototype.test = function (dt) {
|
|
//var dt = 120;
|
|
|
|
this.objects.foreach(function (object) {
|
|
object.step(dt);
|
|
}, this);
|
|
|
|
this.forces.foreach(function (force) {
|
|
force.step(dt);
|
|
}, this);
|
|
|
|
this.manifolds.foreach(function (manifold) {
|
|
manifold.step(dt);
|
|
}, this);
|
|
|
|
};
|
|
|
|
CAL.Manifold = function (a, b) {
|
|
this.a = a;
|
|
this.b = b;
|
|
|
|
this.normal;
|
|
};
|
|
CAL.Manifold.prototype.checkCollision = function (dt) {
|
|
var collisionData = {collision: false};
|
|
|
|
this.b.shape.points.foreach(function (pointA, i) {
|
|
var pointA = pointA.applyMatrix(this.b.shape);
|
|
var collision = true;
|
|
var bestDistance = -Infinity;
|
|
var bestNormal;
|
|
|
|
this.a.shape.points.foreach(function (pointB, j) {
|
|
var pointB = pointB.applyMatrix(this.a.shape);
|
|
var normal = this.a.shape.getNormal(j);
|
|
var distance = normal.dot(pointA.subtract(pointB));
|
|
|
|
if (distance > 0) {
|
|
collision = false;
|
|
return true;
|
|
}
|
|
else if (distance > bestDistance) {
|
|
bestDistance = distance;
|
|
bestNormal = normal;
|
|
}
|
|
}, this);
|
|
|
|
if (collision) {
|
|
collisionData = {collision: true, penetrationDepth: -bestDistance, normal: bestNormal, impactPoint: pointA};
|
|
|
|
return true;
|
|
}
|
|
|
|
}, this);
|
|
|
|
return collisionData;
|
|
|
|
/*this.a.shape.points.foreach(function (point, i) {
|
|
var normal = this.a.shape.getNormal(i);
|
|
var support = this.b.getSupport(normal.scale(-1));
|
|
|
|
var point = point.applyMatrix(this.a.shape);
|
|
var distance = normal.dot(support.subtract(point));
|
|
|
|
//if (distance > 0) {
|
|
// console.log("test");
|
|
// bestDistance = 1;
|
|
// return true;
|
|
//}
|
|
if (distance > bestDistance || bestDistance === false) {
|
|
|
|
//var velocity = this.b.velocity.add(this.b.getPointVelocity(support));
|
|
//if (velocity.dot(normal) < 0 || velocity.length === 0) {
|
|
|
|
bestDistance = distance;
|
|
bestNormal = normal;
|
|
bestImpactPoint = support;
|
|
//bestVelocity = velocity;
|
|
//}
|
|
}
|
|
}, this);
|
|
|
|
var i = 2;
|
|
var normal = this.a.shape.getNormal(i);
|
|
normal.scale(100).draw(CAL.Scene.context, 200, 200);
|
|
var support = this.b.getSupport(normal.scale(-1));
|
|
|
|
var point = this.a.shape.points[i].applyMatrix(this.a.shape);
|
|
var distance = normal.dot(support.subtract(point));
|
|
|
|
console.log(distance);
|
|
|
|
CAL.Scene.context.beginPath();
|
|
CAL.Scene.context.arc(support.x, support.y, 10, 0, Math.PI*2);
|
|
CAL.Scene.context.stroke();
|
|
|
|
return {collision: bestDistance < 0, penetrationDepth: bestDistance, normal: bestNormal, impactPoint: bestImpactPoint};*/
|
|
};
|
|
CAL.Manifold.prototype.positionalCorrection = function (collisionData) {
|
|
var percent = 0.2; // usually 20% to 80%
|
|
var slop = 0.1; // usually 0.01 to 0.1
|
|
|
|
var correction = collisionData.normal.scale(Math.max(collisionData.penetrationDepth - slop, 0) / (1/this.a.mass + 1/this.b.mass) * percent);
|
|
|
|
var aPos = correction.scale(1/this.a.mass);
|
|
this.a.shape._x += aPos.x;
|
|
this.a.shape._y += aPos.y;
|
|
this.a.shape.updateMatrix();
|
|
|
|
var bPos = correction.scale(1/this.b.mass);
|
|
this.b.shape._x -= bPos.x;
|
|
this.b.shape._y -= bPos.y;
|
|
this.b.shape.updateMatrix();
|
|
};
|
|
CAL.Manifold.prototype.resolveCollision = function (collisionData) {
|
|
var restVelocity = this.b.velocity.subtract(this.a.velocity);
|
|
|
|
var velAlongNormal = restVelocity.dot(collisionData.normal);
|
|
if (velAlongNormal > 0) {
|
|
return;
|
|
}
|
|
var restitution = Math.min(this.a.restitution, this.b.restitution);
|
|
|
|
var force = -(1 + restitution) * velAlongNormal / (1/this.a.mass + 1/this.b.mass);
|
|
|
|
var impulse = collisionData.normal.scale(force);
|
|
|
|
this.a.addForce(impulse.scale(-1), collisionData.impactPoint);
|
|
this.b.addForce(impulse, collisionData.impactPoint);
|
|
};
|
|
CAL.Manifold.prototype.step = function (dt) {
|
|
var collisionData = this.checkCollision();
|
|
|
|
if (collisionData.collision) {
|
|
this.resolveCollision(collisionData);
|
|
this.positionalCorrection(collisionData);
|
|
}
|
|
};
|
|
|
|
CAL.PhysicsObject = function (options) {
|
|
options = options || {};
|
|
|
|
this.shape = options.shape || new CAL.Shape();
|
|
this.velocity = options.velocity || new CAL.Vector();
|
|
this.angularVelocity = options.angularVelocity !== undefined ? options.angularVelocity : 0;
|
|
this.restitution = options.restitution !== undefined ? options.restitution : 0.5;
|
|
this.density = options.density || 1;
|
|
this.updateMass();
|
|
};
|
|
CAL.PhysicsObject.prototype.calculateBoundingBox = function (matrix) {
|
|
matrix = matrix || this.shape;
|
|
|
|
var minX = Infinity;
|
|
var minY = Infinity;
|
|
var maxX = -Infinity;
|
|
var maxY = -Infinity;
|
|
this.shape.points.foreach(function (point) {
|
|
var point = point.applyMatrix(matrix);
|
|
|
|
minX = (point.x < minX) ? point.x : minX;
|
|
minY = (point.y < minY) ? point.y : minY;
|
|
maxX = (point.x > maxX) ? point.x : maxX;
|
|
maxY = (point.y > maxY) ? point.y : maxY;
|
|
}, this);
|
|
|
|
return {minX: minX, minY: minY, maxX: maxX, maxY: maxY};
|
|
};
|
|
CAL.PhysicsObject.prototype.addForce = function (impulse, contactVector) {
|
|
this.velocity = this.velocity.add(impulse.scale(1.0/this.mass));
|
|
|
|
/*if (contactVector !== undefined) {
|
|
this.angularVelocity += contactVector.subtract(this.centerOfMass.applyMatrix(this.shape)).cross(impulse)*(1.0/this.inertia);
|
|
//this.angularVelocity = contactVector.subtract(this.centerOfMass.applyMatrix(this.shape)).cross(impulse)*(1.0/this.inertia);
|
|
}*/
|
|
};
|
|
CAL.PhysicsObject.prototype.pointCollision = function (point) {
|
|
var point = point
|
|
.subtract(new CAL.Vector(this.shape.x, this.shape.y))
|
|
.rotate(-this.shape.rotation)
|
|
.multiply(new CAL.Vector(1/this.shape.sx, 1/this.shape.sy))
|
|
.subtract(new CAL.Vector(this.collisionMask.offsetX, this.collisionMask.offsetY));
|
|
|
|
if (point.x < 0) return false;
|
|
if (point.y < 0) return false;
|
|
if (point.x > this.collisionMask.width) return false;
|
|
if (point.y > this.collisionMask.height) return false;
|
|
|
|
var i = Math.round(point.y)*this.collisionMask.width + Math.round(point.x);
|
|
return this.collisionMask.data[i];
|
|
};
|
|
CAL.PhysicsObject.prototype.setDensity = function (density) {
|
|
this.density = density;
|
|
this.mass = this.area*density;
|
|
this.inertia = this.mass*this.area;
|
|
};
|
|
CAL.PhysicsObject.prototype.updateMass = function () {
|
|
var boundingBox = this.calculateBoundingBox(new CAL.Matrix());
|
|
var surface = new CAL.Surface({x: boundingBox.maxX - boundingBox.minX, y: boundingBox.maxY - boundingBox.minY});
|
|
this.shape.clip(surface.context, new CAL.Matrix({x: -boundingBox.minX, y: -boundingBox.minY}));
|
|
surface.context.fillStyle = "black";
|
|
surface.context.fillRect(0, 0, surface.width, surface.height);
|
|
|
|
var imageData = surface.getImageData();
|
|
|
|
var point = new CAL.Vector();
|
|
var area = 0;
|
|
this.collisionMask = {
|
|
data: [],
|
|
width: imageData.width,
|
|
height: imageData.height,
|
|
offsetX: boundingBox.minX,
|
|
offsetY: boundingBox.minY
|
|
};
|
|
for (var i = 0; i < imageData.data.length; i += 4) {
|
|
var alpha = imageData.data[i + 3];
|
|
if (alpha > 0) {
|
|
point = point.add(new CAL.Vector(i/4 % imageData.width, Math.floor(i/4/imageData.width)));
|
|
area ++;
|
|
this.collisionMask.data.push(true);
|
|
}
|
|
else {
|
|
this.collisionMask.data.push(false);
|
|
}
|
|
}
|
|
this.area = area/**this.sx*this.sy*/;
|
|
this.mass = area*this.density;
|
|
this.inertia = this.mass*area;
|
|
this.centerOfMass = point.scale(1/area).add(new CAL.Vector(boundingBox.minX, boundingBox.minY));
|
|
};
|
|
CAL.PhysicsObject.prototype.getSupport = function (n) {
|
|
var bestProjection = -Infinity;
|
|
var bestPoint;
|
|
|
|
for (var i = 0; i < this.shape.points.length; i ++) {
|
|
var point = this.shape.points[i].applyMatrix(this.shape);
|
|
var projection = point.dot(n);
|
|
|
|
if (projection > bestProjection) {
|
|
bestPoint = point;
|
|
bestProjection = projection;
|
|
}
|
|
}
|
|
|
|
return bestPoint;
|
|
};
|
|
CAL.PhysicsObject.prototype.getPointVelocity = function (point) {
|
|
|
|
var relativePoint = point.subtract(this.centerOfMass.applyMatrix(this.shape));
|
|
var rotatedPoint = relativePoint.rotate(this.angularVelocity);
|
|
|
|
return rotatedPoint.subtract(relativePoint);
|
|
};
|
|
CAL.PhysicsObject.prototype.findCollisionFace = function (point) {
|
|
|
|
};
|
|
CAL.PhysicsObject.prototype.step = function (dt) {
|
|
//this.rotateAroundRelative(this.torque*dt, this.centerOfMass);
|
|
|
|
//this.angularVelocity += torque * (1.0 / this.inertia) * dt
|
|
|
|
/*if (this.velocity.length < 0.0001) {
|
|
this.velocity = new CAL.Vector();
|
|
}
|
|
if (Math.abs(this.angularVelocity) < 0.00001) {
|
|
this.angularVelocity = 0;
|
|
}*/
|
|
|
|
this.shape._x += this.velocity.x*dt;
|
|
this.shape._y += this.velocity.y*dt;
|
|
this.shape.updateMatrix();
|
|
|
|
//this.shape.rotateAroundRelative(this.angularVelocity*dt, this.centerOfMass);
|
|
};
|
|
|
|
CAL.PhysicsObject.prototype.debugDraw = function (context) {
|
|
context.lineWidth = 1;
|
|
context.strokeStyle = "black";
|
|
|
|
var minX = Infinity;
|
|
var minY = Infinity;
|
|
var maxX = -Infinity;
|
|
var maxY = -Infinity;
|
|
context.beginPath();
|
|
this.shape.points.foreach(function (point) {
|
|
var point = point.applyMatrix(this.shape);
|
|
|
|
context.lineTo(point.x, point.y);
|
|
context.arc(point.x, point.y, 3, 0, 2*Math.PI*2);
|
|
context.lineTo(point.x, point.y);
|
|
|
|
minX = (point.x < minX) ? point.x : minX
|
|
minY = (point.y < minY) ? point.y : minY
|
|
maxX = (point.x > maxX) ? point.x : maxX
|
|
maxY = (point.y > maxY) ? point.y : maxY
|
|
}, this);
|
|
context.closePath();
|
|
context.stroke();
|
|
|
|
this.shape.points.foreach(function (point, i) {
|
|
var point = point.applyMatrix(this.shape);
|
|
var text = new CAL.Text();
|
|
text.drawText(context, i, point.x + 10, point.y - 10);
|
|
//this.getPointVelocity(point).scale(1000).draw(context, point.x, point.y);
|
|
}, this);
|
|
|
|
|
|
context.beginPath();
|
|
context.moveTo(minX, minY);
|
|
context.lineTo(minX, maxY);
|
|
context.lineTo(maxX, maxY);
|
|
context.lineTo(maxX, minY);
|
|
context.closePath();
|
|
|
|
var centerOfMass = this.centerOfMass.applyMatrix(this.shape);
|
|
context.moveTo(centerOfMass.x-5, centerOfMass.y-5);
|
|
context.lineTo(centerOfMass.x+5, centerOfMass.y+5);
|
|
context.moveTo(centerOfMass.x+5, centerOfMass.y-5);
|
|
context.lineTo(centerOfMass.x-5, centerOfMass.y+5);
|
|
context.strokeStyle = "red";
|
|
context.stroke();
|
|
|
|
var center = this.centerOfMass.applyMatrix(this.shape);
|
|
this.velocity.scale(1000).draw(context, center.x, center.y);
|
|
};
|
|
|
|
CAL.Force = function (options) {
|
|
|
|
this.objects = options.objects || [];
|
|
this.velocity = options.velocity || new CAL.Vector();
|
|
}
|
|
CAL.Force.prototype.add = function () {
|
|
for (var i = 0; i < arguments.length; i ++) {
|
|
var argument = arguments[i];
|
|
|
|
this.objects.push(argument);
|
|
}
|
|
};
|
|
CAL.Force.prototype.step = function (dt) {
|
|
|
|
for (var i = 0; i < this.objects.length; i ++) {
|
|
var object = this.objects[i];
|
|
|
|
object.velocity = object.velocity.add(this.velocity.scale(dt));
|
|
}
|
|
};
|