var Shape = require('./Shape')
, vec2 = require('../math/vec2')
, polyk = require('../math/polyk')
, decomp = require('poly-decomp')
module.exports = Convex;
/**
* Convex shape class.
* @class Convex
* @constructor
* @extends Shape
* @param {Array} vertices An array of Float32Array vertices that span this shape. Vertices are given in counter-clockwise (CCW) direction.
*/
function Convex(vertices){
/**
* Vertices defined in the local frame.
* @property vertices
* @type {Array}
*/
this.vertices = [];
// Copy the verts
for(var i=0; i<vertices.length; i++){
var v = vec2.create();
vec2.copy(v,vertices[i]);
this.vertices.push(v);
}
/**
* The center of mass of the Convex
* @property centerOfMass
* @type {Float32Array}
*/
this.centerOfMass = vec2.fromValues(0,0);
/**
* Triangulated version of this convex. The structure is Array of 3-Arrays, and each subarray contains 3 integers, referencing the vertices.
* @property triangles
* @type {Array}
*/
this.triangles = [];
if(this.vertices.length){
this.updateTriangles();
this.updateCenterOfMass();
}
/**
* The bounding radius of the convex
* @property boundingRadius
* @type {Number}
*/
this.boundingRadius = 0;
Shape.call(this,Shape.CONVEX);
this.updateBoundingRadius();
this.updateArea();
if(this.area < 0)
throw new Error("Convex vertices must be given in conter-clockwise winding.");
};
Convex.prototype = new Shape();
/**
* Update the .triangles property
* @method updateTriangles
*/
Convex.prototype.updateTriangles = function(){
this.triangles.length = 0;
// Rewrite on polyk notation, array of numbers
var polykVerts = [];
for(var i=0; i<this.vertices.length; i++){
var v = this.vertices[i];
polykVerts.push(v[0],v[1]);
}
// Triangulate
var triangles = polyk.Triangulate(polykVerts);
// Loop over all triangles, add their inertia contributions to I
for(var i=0; i<triangles.length; i+=3){
var id1 = triangles[i],
id2 = triangles[i+1],
id3 = triangles[i+2];
// Add to triangles
this.triangles.push([id1,id2,id3]);
}
};
var updateCenterOfMass_centroid = vec2.create(),
updateCenterOfMass_centroid_times_mass = vec2.create(),
updateCenterOfMass_a = vec2.create(),
updateCenterOfMass_b = vec2.create(),
updateCenterOfMass_c = vec2.create(),
updateCenterOfMass_ac = vec2.create(),
updateCenterOfMass_ca = vec2.create(),
updateCenterOfMass_cb = vec2.create(),
updateCenterOfMass_n = vec2.create();
/**
* Update the .centerOfMass property.
* @method updateCenterOfMass
*/
Convex.prototype.updateCenterOfMass = function(){
var triangles = this.triangles,
verts = this.vertices,
cm = this.centerOfMass,
centroid = updateCenterOfMass_centroid,
n = updateCenterOfMass_n,
a = updateCenterOfMass_a,
b = updateCenterOfMass_b,
c = updateCenterOfMass_c,
ac = updateCenterOfMass_ac,
ca = updateCenterOfMass_ca,
cb = updateCenterOfMass_cb,
centroid_times_mass = updateCenterOfMass_centroid_times_mass;
vec2.set(cm,0,0);
var totalArea = 0;
for(var i=0; i!==triangles.length; i++){
var t = triangles[i],
a = verts[t[0]],
b = verts[t[1]],
c = verts[t[2]];
vec2.centroid(centroid,a,b,c);
// Get mass for the triangle (density=1 in this case)
// http://math.stackexchange.com/questions/80198/area-of-triangle-via-vectors
var m = Convex.triangleArea(a,b,c)
totalArea += m;
// Add to center of mass
vec2.scale(centroid_times_mass, centroid, m);
vec2.add(cm, cm, centroid_times_mass);
}
vec2.scale(cm,cm,1/totalArea);
};
/**
* Compute the mass moment of inertia of the Convex.
* @method computeMomentOfInertia
* @param {Number} mass
* @return {Number}
* @see http://www.gamedev.net/topic/342822-moment-of-inertia-of-a-polygon-2d/
*/
Convex.prototype.computeMomentOfInertia = function(mass){
var denom = 0.0,
numer = 0.0,
N = this.vertices.length;
for(var j = N-1, i = 0; i < N; j = i, i ++){
var p0 = this.vertices[j];
var p1 = this.vertices[i];
var a = Math.abs(vec2.crossLength(p0,p1));
var b = vec2.dot(p1,p1) + vec2.dot(p1,p0) + vec2.dot(p0,p0);
denom += a * b;
numer += a;
}
return (mass / 6.0) * (denom / numer);
};
/**
* Updates the .boundingRadius property
* @method updateBoundingRadius
*/
Convex.prototype.updateBoundingRadius = function(){
var verts = this.vertices,
r2 = 0;
for(var i=0; i!==verts.length; i++){
var l2 = vec2.squaredLength(verts[i]);
if(l2 > r2) r2 = l2;
}
this.boundingRadius = Math.sqrt(r2);
};
/**
* Get the area of the triangle spanned by the three points a, b, c. The area is positive if the points are given in counter-clockwise order, otherwise negative.
* @static
* @method triangleArea
* @param {Array} a
* @param {Array} b
* @param {Array} c
* @return {Number}
*/
Convex.triangleArea = function(a,b,c){
return (((b[0] - a[0])*(c[1] - a[1]))-((c[0] - a[0])*(b[1] - a[1]))) * 0.5;
}
/**
* Update the .area
* @method updateArea
*/
Convex.prototype.updateArea = function(){
this.updateTriangles();
this.area = 0;
var triangles = this.triangles,
verts = this.vertices;
for(var i=0; i!==triangles.length; i++){
var t = triangles[i],
a = verts[t[0]],
b = verts[t[1]],
c = verts[t[2]];
// Get mass for the triangle (density=1 in this case)
var m = Convex.triangleArea(a,b,c);
this.area += m;
}
};
/**
* @method computeAABB
* @param {AABB} out
* @param {Array} position
* @param {Number} angle
*/
Convex.prototype.computeAABB = function(out, position, angle){
out.setFromPoints(this.vertices,position,angle);
};