/*
* Copyright (c) 2006-2007 Erin Catto http:
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked, and must not be
* misrepresented the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
/**
* A convex polygon
*
* The position of the polygon (m_position) is the
* position of the centroid. The vertices of the incoming polygon are pre-rotated
* according to the local rotation. The vertices are also shifted to be centered
* on the centroid. Since the local rotation is absorbed into the vertex
* coordinates, the polygon rotation is equal to the body rotation. However,
* the polygon position is centered on the polygon centroid. This simplifies
* some collision algorithms.
*
* @class b2PolyShape
* @constructor
*/
var b2PolyShape = function (def, body, newOrigin) {
// initialize instance variables for references
this.m_R = new b2Mat22();
this.m_position = new b2Vec2();
//
// The constructor for b2Shape
this.m_userData = def.userData;
this.m_friction = def.friction;
this.m_restitution = def.restitution;
this.m_body = body;
this.m_proxyId = b2Pair.b2_nullProxy;
this.m_maxRadius = 0.0;
this.m_categoryBits = def.categoryBits;
this.m_maskBits = def.maskBits;
this.m_groupIndex = def.groupIndex;
//
// initialize instance variables for references
this.syncAABB = new b2AABB();
this.syncMat = new b2Mat22();
this.m_localCentroid = new b2Vec2();
this.m_localOBB = new b2OBB();
//
//super(def, body);
var i = 0;
var hX;
var hY;
var tVec;
var aabb = new b2AABB();
// Vertices
this.m_vertices = new Array(b2Settings.b2_maxPolyVertices);
this.m_coreVertices = new Array(b2Settings.b2_maxPolyVertices);
//for (i = 0; i < b2Settings.b2_maxPolyVertices; i++)
// this.m_vertices[i] = new b2Vec2();
// Normals
this.m_normals = new Array(b2Settings.b2_maxPolyVertices);
//for (i = 0; i < b2Settings.b2_maxPolyVertices; i++)
// this.m_normals[i] = new b2Vec2();
//b2Settings.b2Assert(def.type == b2Shape.e_boxShape || def.type == b2Shape.e_polyShape);
this.m_type = b2Shape.e_polyShape;
var localR = new b2Mat22(def.localRotation);
// Get the vertices transformed into the body frame.
if (def.type == b2Shape.e_boxShape)
{
//this.m_localCentroid = def.localPosition - newOrigin;
this.m_localCentroid.x = def.localPosition.x - newOrigin.x;
this.m_localCentroid.y = def.localPosition.y - newOrigin.y;
var box = def;
this.m_vertexCount = 4;
hX = box.extents.x;
hY = box.extents.y;
//hc.x = b2Max(0.0f, h.x - 2.0f * b2_linearSlop);
var hcX = Math.max(0.0, hX - 2.0 * b2Settings.b2_linearSlop);
//hc.y = b2Max(0.0f, h.y - 2.0f * b2_linearSlop);
var hcY = Math.max(0.0, hY - 2.0 * b2Settings.b2_linearSlop);
//this.m_vertices[0] = b2Mul(localR, b2Vec2(h.x, h.y));
tVec = this.m_vertices[0] = new b2Vec2();
tVec.x = localR.col1.x * hX + localR.col2.x * hY;
tVec.y = localR.col1.y * hX + localR.col2.y * hY;
//this.m_vertices[1] = b2Mul(localR, b2Vec2(-h.x, h.y));
tVec = this.m_vertices[1] = new b2Vec2();
tVec.x = localR.col1.x * -hX + localR.col2.x * hY;
tVec.y = localR.col1.y * -hX + localR.col2.y * hY;
//this.m_vertices[2] = b2Mul(localR, b2Vec2(-h.x, -h.y));
tVec = this.m_vertices[2] = new b2Vec2();
tVec.x = localR.col1.x * -hX + localR.col2.x * -hY;
tVec.y = localR.col1.y * -hX + localR.col2.y * -hY;
//this.m_vertices[3] = b2Mul(localR, b2Vec2(h.x, -h.y));
tVec = this.m_vertices[3] = new b2Vec2();
tVec.x = localR.col1.x * hX + localR.col2.x * -hY;
tVec.y = localR.col1.y * hX + localR.col2.y * -hY;
//this.m_coreVertices[0] = b2Mul(localR, b2Vec2(hc.x, hc.y));
tVec = this.m_coreVertices[0] = new b2Vec2();
tVec.x = localR.col1.x * hcX + localR.col2.x * hcY;
tVec.y = localR.col1.y * hcX + localR.col2.y * hcY;
//this.m_coreVertices[1] = b2Mul(localR, b2Vec2(-hc.x, hc.y));
tVec = this.m_coreVertices[1] = new b2Vec2();
tVec.x = localR.col1.x * -hcX + localR.col2.x * hcY;
tVec.y = localR.col1.y * -hcX + localR.col2.y * hcY;
//this.m_coreVertices[2] = b2Mul(localR, b2Vec2(-hc.x, -hc.y));
tVec = this.m_coreVertices[2] = new b2Vec2();
tVec.x = localR.col1.x * -hcX + localR.col2.x * -hcY;
tVec.y = localR.col1.y * -hcX + localR.col2.y * -hcY;
//this.m_coreVertices[3] = b2Mul(localR, b2Vec2(hc.x, -hc.y));
tVec = this.m_coreVertices[3] = new b2Vec2();
tVec.x = localR.col1.x * hcX + localR.col2.x * -hcY;
tVec.y = localR.col1.y * hcX + localR.col2.y * -hcY;
}
else
{
var poly = def;
this.m_vertexCount = poly.vertexCount;
//b2Settings.b2Assert(3 <= this.m_vertexCount && this.m_vertexCount <= b2Settings.b2_maxPolyVertices);
//b2Vec2 centroid = b2Shape.PolyCentroid(poly->vertices, poly->vertexCount);
b2Shape.PolyCentroid(poly.vertices, poly.vertexCount, b2PolyShape.tempVec);
var centroidX = b2PolyShape.tempVec.x;
var centroidY = b2PolyShape.tempVec.y;
//this.m_localCentroid = def->localPosition + b2Mul(localR, centroid) - newOrigin;
this.m_localCentroid.x = def.localPosition.x + (localR.col1.x * centroidX + localR.col2.x * centroidY) - newOrigin.x;
this.m_localCentroid.y = def.localPosition.y + (localR.col1.y * centroidX + localR.col2.y * centroidY) - newOrigin.y;
for (i = 0; i < this.m_vertexCount; ++i)
{
this.m_vertices[i] = new b2Vec2();
this.m_coreVertices[i] = new b2Vec2();
//this.m_vertices[i] = b2Mul(localR, poly->vertices[i] - centroid);
hX = poly.vertices[i].x - centroidX;
hY = poly.vertices[i].y - centroidY;
this.m_vertices[i].x = localR.col1.x * hX + localR.col2.x * hY;
this.m_vertices[i].y = localR.col1.y * hX + localR.col2.y * hY;
//b2Vec2 u = this.m_vertices[i];
var uX = this.m_vertices[i].x;
var uY = this.m_vertices[i].y;
//float32 length = u.Length();
var length = Math.sqrt(uX*uX + uY*uY);
if (length > Number.MIN_VALUE)
{
uX *= 1.0 / length;
uY *= 1.0 / length;
}
//this.m_coreVertices[i] = this.m_vertices[i] - 2.0f * b2_linearSlop * u;
this.m_coreVertices[i].x = this.m_vertices[i].x - 2.0 * b2Settings.b2_linearSlop * uX;
this.m_coreVertices[i].y = this.m_vertices[i].y - 2.0 * b2Settings.b2_linearSlop * uY;
}
}
// Compute bounding box. TODO_ERIN optimize OBB
//var minVertex = new b2Vec2(Number.MAX_VALUE, Number.MAX_VALUE);
var minVertexX = Number.MAX_VALUE;
var minVertexY = Number.MAX_VALUE;
var maxVertexX = -Number.MAX_VALUE;
var maxVertexY = -Number.MAX_VALUE;
this.m_maxRadius = 0.0;
for (i = 0; i < this.m_vertexCount; ++i)
{
var v = this.m_vertices[i];
//minVertex = b2Math.b2MinV(minVertex, this.m_vertices[i]);
minVertexX = Math.min(minVertexX, v.x);
minVertexY = Math.min(minVertexY, v.y);
//maxVertex = b2Math.b2MaxV(maxVertex, this.m_vertices[i]);
maxVertexX = Math.max(maxVertexX, v.x);
maxVertexY = Math.max(maxVertexY, v.y);
//this.m_maxRadius = b2Max(this.m_maxRadius, v.Length());
this.m_maxRadius = Math.max(this.m_maxRadius, v.Length());
}
this.m_localOBB.R.SetIdentity();
//this.m_localOBB.center = 0.5 * (minVertex + maxVertex);
this.m_localOBB.center.Set((minVertexX + maxVertexX) * 0.5, (minVertexY + maxVertexY) * 0.5);
//this.m_localOBB.extents = 0.5 * (maxVertex - minVertex);
this.m_localOBB.extents.Set((maxVertexX - minVertexX) * 0.5, (maxVertexY - minVertexY) * 0.5);
// Compute the edge normals and next index map.
var i1 = 0;
var i2 = 0;
for (i = 0; i < this.m_vertexCount; ++i)
{
this.m_normals[i] = new b2Vec2();
i1 = i;
i2 = i + 1 < this.m_vertexCount ? i + 1 : 0;
//b2Vec2 edge = this.m_vertices[i2] - this.m_vertices[i1];
//var edgeX = this.m_vertices[i2].x - this.m_vertices[i1].x;
//var edgeY = this.m_vertices[i2].y - this.m_vertices[i1].y;
//this.m_normals[i] = b2Cross(edge, 1.0f);
this.m_normals[i].x = this.m_vertices[i2].y - this.m_vertices[i1].y;
this.m_normals[i].y = -(this.m_vertices[i2].x - this.m_vertices[i1].x);
this.m_normals[i].Normalize();
}
// Ensure the polygon in convex. TODO_ERIN compute convex hull.
for (i = 0; i < this.m_vertexCount; ++i)
{
i1 = i;
i2 = i + 1 < this.m_vertexCount ? i + 1 : 0;
//b2Settings.b2Assert(b2Math.b2CrossVV(this.m_normals[i1], this.m_normals[i2]) > Number.MIN_VALUE);
}
this.m_R.SetM(this.m_body.m_R);
//this.m_position.SetV( this.m_body.m_position + b2Mul(this.m_body->this.m_R, this.m_localCentroid) );
this.m_position.x = this.m_body.m_position.x + (this.m_R.col1.x * this.m_localCentroid.x + this.m_R.col2.x * this.m_localCentroid.y);
this.m_position.y = this.m_body.m_position.y + (this.m_R.col1.y * this.m_localCentroid.x + this.m_R.col2.y * this.m_localCentroid.y);
//var R = b2Math.b2MulMM(this.m_R, this.m_localOBB.R);
//R.col1 = b2MulMV(this.m_R, this.m_localOBB.R.col1);
b2PolyShape.tAbsR.col1.x = this.m_R.col1.x * this.m_localOBB.R.col1.x + this.m_R.col2.x * this.m_localOBB.R.col1.y;
b2PolyShape.tAbsR.col1.y = this.m_R.col1.y * this.m_localOBB.R.col1.x + this.m_R.col2.y * this.m_localOBB.R.col1.y;
//R.col2 = b2MulMV(this.m_R, this.m_localOBB.R.col2)
b2PolyShape.tAbsR.col2.x = this.m_R.col1.x * this.m_localOBB.R.col2.x + this.m_R.col2.x * this.m_localOBB.R.col2.y;
b2PolyShape.tAbsR.col2.y = this.m_R.col1.y * this.m_localOBB.R.col2.x + this.m_R.col2.y * this.m_localOBB.R.col2.y;
//var absR = b2Math.b2AbsM(R);
b2PolyShape.tAbsR.Abs()
//h = b2Math.b2MulMV(b2PolyShape.tAbsR, this.m_localOBB.extents);
hX = b2PolyShape.tAbsR.col1.x * this.m_localOBB.extents.x + b2PolyShape.tAbsR.col2.x * this.m_localOBB.extents.y;
hY = b2PolyShape.tAbsR.col1.y * this.m_localOBB.extents.x + b2PolyShape.tAbsR.col2.y * this.m_localOBB.extents.y;
//var position = this.m_position + b2Mul(this.m_R, this.m_localOBB.center);
var positionX = this.m_position.x + (this.m_R.col1.x * this.m_localOBB.center.x + this.m_R.col2.x * this.m_localOBB.center.y);
var positionY = this.m_position.y + (this.m_R.col1.y * this.m_localOBB.center.x + this.m_R.col2.y * this.m_localOBB.center.y);
//aabb.minVertex = b2Math.SubtractVV(this.m_position, h);
aabb.minVertex.x = positionX - hX;
aabb.minVertex.y = positionY - hY;
//aabb.maxVertex = b2Math.AddVV(this.m_position, h);
aabb.maxVertex.x = positionX + hX;
aabb.maxVertex.y = positionY + hY;
var broadPhase = this.m_body.m_world.m_broadPhase;
if (broadPhase.InRange(aabb))
{
this.m_proxyId = broadPhase.CreateProxy(aabb, this);
}
else
{
this.m_proxyId = b2Pair.b2_nullProxy;
}
if (this.m_proxyId == b2Pair.b2_nullProxy)
{
this.m_body.Freeze();
}
};
Object.extend(b2PolyShape.prototype, b2Shape.prototype);
Object.extend(b2PolyShape.prototype,
{
TestPoint: function(p){
//var pLocal = b2Math.b2MulTMV(this.m_R, b2Math.SubtractVV(p, this.m_position));
var pLocal = new b2Vec2();
pLocal.SetV(p);
pLocal.Subtract(this.m_position);
pLocal.MulTM(this.m_R);
for (var i = 0; i < this.m_vertexCount; ++i)
{
//var dot = b2Math.b2Dot(this.m_normals[i], b2Math.SubtractVV(pLocal, this.m_vertices[i]));
var tVec = new b2Vec2();
tVec.SetV(pLocal);
tVec.Subtract(this.m_vertices[i]);
var dot = b2Math.b2Dot(this.m_normals[i], tVec);
if (dot > 0.0)
{
return false;
}
}
return true;
},
//--------------- Internals Below -------------------
// Temp vec for b2Shape.PolyCentroid
// Temp AABB for Synch function
syncAABB: new b2AABB(),
syncMat: new b2Mat22(),
Synchronize: function(position1, R1, position2, R2){
// The body transform is copied for convenience.
this.m_R.SetM(R2);
//this.m_position = this.m_body->this.m_position + b2Mul(this.m_body->this.m_R, this.m_localCentroid)
this.m_position.x = this.m_body.m_position.x + (R2.col1.x * this.m_localCentroid.x + R2.col2.x * this.m_localCentroid.y);
this.m_position.y = this.m_body.m_position.y + (R2.col1.y * this.m_localCentroid.x + R2.col2.y * this.m_localCentroid.y);
if (this.m_proxyId == b2Pair.b2_nullProxy)
{
return;
}
//b2AABB aabb1, aabb2;
var hX;
var hY;
//b2Mat22 obbR = b2Mul(R1, this.m_localOBB.R);
var v1 = R1.col1;
var v2 = R1.col2;
var v3 = this.m_localOBB.R.col1;
var v4 = this.m_localOBB.R.col2;
//this.syncMat.col1 = b2MulMV(R1, this.m_localOBB.R.col1);
this.syncMat.col1.x = v1.x * v3.x + v2.x * v3.y;
this.syncMat.col1.y = v1.y * v3.x + v2.y * v3.y;
//this.syncMat.col2 = b2MulMV(R1, this.m_localOBB.R.col2);
this.syncMat.col2.x = v1.x * v4.x + v2.x * v4.y;
this.syncMat.col2.y = v1.y * v4.x + v2.y * v4.y;
//b2Mat22 absR = b2Abs(obbR);
this.syncMat.Abs();
//b2Vec2 center = position1 + b2Mul(R1, this.m_localCentroid + this.m_localOBB.center);
hX = this.m_localCentroid.x + this.m_localOBB.center.x;
hY = this.m_localCentroid.y + this.m_localOBB.center.y;
var centerX = position1.x + (R1.col1.x * hX + R1.col2.x * hY);
var centerY = position1.y + (R1.col1.y * hX + R1.col2.y * hY);
//b2Vec2 h = b2Mul(this.syncMat, this.m_localOBB.extents);
hX = this.syncMat.col1.x * this.m_localOBB.extents.x + this.syncMat.col2.x * this.m_localOBB.extents.y;
hY = this.syncMat.col1.y * this.m_localOBB.extents.x + this.syncMat.col2.y * this.m_localOBB.extents.y;
//aabb1.minVertex = center - h;
this.syncAABB.minVertex.x = centerX - hX;
this.syncAABB.minVertex.y = centerY - hY;
//aabb1.maxVertex = center + h;
this.syncAABB.maxVertex.x = centerX + hX;
this.syncAABB.maxVertex.y = centerY + hY;
//b2Mat22 obbR = b2Mul(R2, this.m_localOBB.R);
v1 = R2.col1;
v2 = R2.col2;
v3 = this.m_localOBB.R.col1;
v4 = this.m_localOBB.R.col2;
//this.syncMat.col1 = b2MulMV(R1, this.m_localOBB.R.col1);
this.syncMat.col1.x = v1.x * v3.x + v2.x * v3.y;
this.syncMat.col1.y = v1.y * v3.x + v2.y * v3.y;
//this.syncMat.col2 = b2MulMV(R1, this.m_localOBB.R.col2);
this.syncMat.col2.x = v1.x * v4.x + v2.x * v4.y;
this.syncMat.col2.y = v1.y * v4.x + v2.y * v4.y;
//b2Mat22 absR = b2Abs(obbR);
this.syncMat.Abs();
//b2Vec2 center = position2 + b2Mul(R2, this.m_localCentroid + this.m_localOBB.center);
hX = this.m_localCentroid.x + this.m_localOBB.center.x;
hY = this.m_localCentroid.y + this.m_localOBB.center.y;
centerX = position2.x + (R2.col1.x * hX + R2.col2.x * hY);
centerY = position2.y + (R2.col1.y * hX + R2.col2.y * hY);
//b2Vec2 h = b2Mul(absR, this.m_localOBB.extents);
hX = this.syncMat.col1.x * this.m_localOBB.extents.x + this.syncMat.col2.x * this.m_localOBB.extents.y;
hY = this.syncMat.col1.y * this.m_localOBB.extents.x + this.syncMat.col2.y * this.m_localOBB.extents.y;
//aabb2.minVertex = center - h;
//aabb2.maxVertex = center + h;
//aabb.minVertex = b2Min(aabb1.minVertex, aabb2.minVertex);
this.syncAABB.minVertex.x = Math.min(this.syncAABB.minVertex.x, centerX - hX);
this.syncAABB.minVertex.y = Math.min(this.syncAABB.minVertex.y, centerY - hY);
//aabb.maxVertex = b2Max(aabb1.maxVertex, aabb2.maxVertex);
this.syncAABB.maxVertex.x = Math.max(this.syncAABB.maxVertex.x, centerX + hX);
this.syncAABB.maxVertex.y = Math.max(this.syncAABB.maxVertex.y, centerY + hY);
var broadPhase = this.m_body.m_world.m_broadPhase;
if (broadPhase.InRange(this.syncAABB))
{
broadPhase.MoveProxy(this.m_proxyId, this.syncAABB);
}
else
{
this.m_body.Freeze();
}
},
QuickSync: function(position, R){
//this.m_R = R;
this.m_R.SetM(R);
//this.m_position = position + b2Mul(R, this.m_localCentroid);
this.m_position.x = position.x + (R.col1.x * this.m_localCentroid.x + R.col2.x * this.m_localCentroid.y);
this.m_position.y = position.y + (R.col1.y * this.m_localCentroid.x + R.col2.y * this.m_localCentroid.y);
},
ResetProxy: function(broadPhase){
if (this.m_proxyId == b2Pair.b2_nullProxy)
{
return;
}
var proxy = broadPhase.GetProxy(this.m_proxyId);
broadPhase.DestroyProxy(this.m_proxyId);
proxy = null;
var R = b2Math.b2MulMM(this.m_R, this.m_localOBB.R);
var absR = b2Math.b2AbsM(R);
var h = b2Math.b2MulMV(absR, this.m_localOBB.extents);
//var position = this.m_position + b2Mul(this.m_R, this.m_localOBB.center);
var position = b2Math.b2MulMV(this.m_R, this.m_localOBB.center);
position.Add(this.m_position);
var aabb = new b2AABB();
//aabb.minVertex = position - h;
aabb.minVertex.SetV(position);
aabb.minVertex.Subtract(h);
//aabb.maxVertex = position + h;
aabb.maxVertex.SetV(position);
aabb.maxVertex.Add(h);
if (broadPhase.InRange(aabb))
{
this.m_proxyId = broadPhase.CreateProxy(aabb, this);
}
else
{
this.m_proxyId = b2Pair.b2_nullProxy;
}
if (this.m_proxyId == b2Pair.b2_nullProxy)
{
this.m_body.Freeze();
}
},
Support: function(dX, dY, out)
{
//b2Vec2 dLocal = b2MulT(this.m_R, d);
var dLocalX = (dX*this.m_R.col1.x + dY*this.m_R.col1.y);
var dLocalY = (dX*this.m_R.col2.x + dY*this.m_R.col2.y);
var bestIndex = 0;
//float32 bestValue = b2Dot(this.m_vertices[0], dLocal);
var bestValue = (this.m_coreVertices[0].x * dLocalX + this.m_coreVertices[0].y * dLocalY);
for (var i = 1; i < this.m_vertexCount; ++i)
{
//float32 value = b2Dot(this.m_vertices[i], dLocal);
var value = (this.m_coreVertices[i].x * dLocalX + this.m_coreVertices[i].y * dLocalY);
if (value > bestValue)
{
bestIndex = i;
bestValue = value;
}
}
//return this.m_position + b2Mul(this.m_R, this.m_vertices[bestIndex]);
out.Set( this.m_position.x + (this.m_R.col1.x * this.m_coreVertices[bestIndex].x + this.m_R.col2.x * this.m_coreVertices[bestIndex].y),
this.m_position.y + (this.m_R.col1.y * this.m_coreVertices[bestIndex].x + this.m_R.col2.y * this.m_coreVertices[bestIndex].y));
},
// Local position of the shape centroid in parent body frame.
m_localCentroid: new b2Vec2(),
// Local position oriented bounding box. The OBB center is relative to
// shape centroid.
m_localOBB: new b2OBB(),
m_vertices: null,
m_coreVertices: null,
m_vertexCount: 0,
m_normals: null});
b2PolyShape.tempVec = new b2Vec2();
b2PolyShape.tAbsR = new b2Mat22();