Need to do 2d affine transformations, or rotate stuff in 3d for your toy canvas library? look no further, the solution is here! Or at least it will be if you help improve this thing by forking it.
var mat3 = (function(){
function Mat3(a){
if(!a) return mat3.identity();
this.a = [];
for(var i = 0; i < 9; i++) this.a[i] = a[i] || 0;
};
Mat3.prototype.add = function(mat){
return mat3([
this.a[0] + mat.a[0], this.a[1] + mat.a[1], this.arr[2] + mat.a[2],
this.a[3] + mat.a[3], this.a[4] + mat.a[4], this.arr[5] + mat.a[5],
this.a[6] + mat.a[6], this.a[7] + mat.a[7], this.arr[8] + mat.a[8]
]);
};
Mat3.prototype.applyToCanvasContext = function(ctx){
ctx.transform(this.a[0], this.a[3], this.a[1], this.a[4], this.a[2], this.a[5]);
};
Mat3.prototype.equals = function(mat){
return this.a[0] === mat.a[0] && this.a[1] === mat.a[1] && this.a[2] === mat.a[2]
&& this.a[3] === mat.a[3] && this.a[4] === mat.a[4] && this.a[5] === mat.a[5]
&& this.a[6] === mat.a[6] && this.a[7] === mat.a[7] && this.a[8] === mat.a[8];
};
Mat3.prototype.toString = function(){
return '[' + this.a.join(',') + ']';
};
// Matrix-Vector multiplication
Mat3.prototype.transform3d = function(v){
return [
v[0] * this.a[0] + v[1] * this.a[1] + v[2] * this.a[2],
v[0] * this.a[3] + v[1] * this.a[4] + v[2] * this.a[5],
v[0] * this.a[6] + v[1] * this.a[7] + v[2] * this.a[8]
];
};
// 2d affine transformation
Mat3.prototype.transform2d = function(v){
var v2 = this.transform3d([v[0],v[1],1]);
return [v2[0],v2[1]];
};
Mat3.prototype.inverse = function(){
var f = this.a[0] * (this.a[4]*this.a[8] - this.a[5]*this.a[7])
+ this.a[1] * (this.a[5]*this.a[6] - this.a[3]*this.a[8])
+ this.a[2] * (this.a[3]*this.a[7] - this.a[4]*this.a[6]);
return mat3([
this.a[4]*this.a[8] - this.a[5]*this.a[7],
this.a[2]*this.a[7] - this.a[1]*this.a[8],
this.a[1]*this.a[5] - this.a[2]*this.a[4],
this.a[5]*this.a[6] - this.a[3]*this.a[8],
this.a[0]*this.a[8] - this.a[2]*this.a[6],
this.a[2]*this.a[3] - this.a[0]*this.a[5],
this.a[3]*this.a[7] - this.a[4]*this.a[6],
this.a[1]*this.a[6] - this.a[0]*this.a[7],
this.a[0]*this.a[4] - this.a[1]*this.a[3]
]).multiplyScalar(1/f);
};
Mat3.prototype.multiply = function(mat){
return mat3([
this.a[0] * mat.a[0] + this.a[1] * mat.a[3] + this.a[2] * mat.a[6],
this.a[0] * mat.a[1] + this.a[1] * mat.a[4] + this.a[2] * mat.a[7],
this.a[0] * mat.a[2] + this.a[1] * mat.a[5] + this.a[2] * mat.a[8],
this.a[3] * mat.a[0] + this.a[4] * mat.a[3] + this.a[5] * mat.a[6],
this.a[3] * mat.a[1] + this.a[4] * mat.a[4] + this.a[5] * mat.a[7],
this.a[3] * mat.a[2] + this.a[4] * mat.a[5] + this.a[5] * mat.a[8],
this.a[6] * mat.a[0] + this.a[7] * mat.a[3] + this.a[8] * mat.a[6],
this.a[6] * mat.a[1] + this.a[7] * mat.a[4] + this.a[8] * mat.a[7],
this.a[6] * mat.a[2] + this.a[7] * mat.a[5] + this.a[8] * mat.a[8]
]);
};
Mat3.prototype.multiplyScalar = function(s){
return mat3([
this.a[0] * s, this.a[1] * s, this.arr[2] * s,
this.a[3] * s, this.a[4] * s, this.arr[5] * s,
this.a[6] * s, this.a[7] * s, this.arr[8] * s
]);
};
Mat3.prototype.transpose = function(){
return mat3([
this.a[0], this.a[3], this.a[6],
this.a[1], this.a[4], this.a[7],
this.a[2], this.a[5], this.a[8]
]);
};
function mat3(a){
return new Mat3(a);
};
mat3.identity = function(){
return mat3([1,0,0,0,1,0,0,0,1]);
};
// 2d rotation
mat3.rotate = function(phi){
var s = Math.sin(phi), c = Math.cos(phi);
return mat3([c,s,0,-s,c,0,0,0,1]);
};
// 3d rotations
mat3.rotate.z = mat3.rotate;
mat3.rotate.y = function(phi){
var s = Math.sin(phi), c = Math.cos(phi);
return mat3([c,0,s,0,1,0,-s,0,c]);
};
mat3.rotate.x = function(phi){
var s = Math.sin(phi), c = Math.cos(phi);
return mat3([1,0,0,0,c,-s,0,s,c]);
};
mat3.scale = function(x, y, z){
return mat3([x,0,0,0,y,0,0,0,z||1]);
};
mat3.translate = function(x, y){
return mat3([1,0,x,0,1,y,0,0,1]);
};
mat3.Mat3 = Mat3;
return mat3;
})();
function random() { return Math.floor(Math.random()*10 - 3); }
function random2d() { return [random(), random()]; };
function random3d() { return [random(), random(), random()]; };
function randommat() { var r = random; return mat3([r(),r(),r(),r(),r(),r(),r(),r(),r()]); }
function toString(x){
return mat3.Mat3.prototype.toString.apply({'a' : x});
};
describe("equality", function(){
it("matrices should know when two matrices are equal", function(){
var a = mat3.identity(),
b = mat3.identity();
console.log('a', a.toString());
console.log('b', b.toString());
Test.assertEquals(
a.equals(b),
true
);
Test.assertEquals(
b.equals(a),
true
);
});
it("matrices should know when two matrices aren't equal", function(){
var a = randommat(),
b = randommat(),
i = Math.floor(Math.random()*9);
a[i] = 1;
b[i] = 2;
console.log('a', a.toString());
console.log('b', b.toString());
Test.assertEquals(
a.equals(b),
false
);
});
});
describe("2d transformations", function(){
it("matrices should translate points correctly", function(){
for(var i = 0; i < 5; i++){
var p = random2d(),
o = random2d(),
mat = mat3.translate(o[0], o[1]),
t = mat.transform2d(p);
console.log(
toString(o) + ' + ' + toString(p) + ' = ' + toString([o[0]+p[0],o[1]+p[1]]) + '?'
);
Test.assertEquals(
o[0]+p[0],
t[0]
);
Test.assertEquals(
o[1]+p[1],
t[1]
);
}
});
});
describe("programmers", function(){
it("are too lazy to add more tests", function(){
Test.assertEquals(true,true);
});
});