@awayfl/awayfl-player
Version:
Flash Player emulator for executing SWF files (published for FP versions 6 and up) in javascript
385 lines (384 loc) • 19 kB
JavaScript
/*
* Copyright (c) 2006-2007 Erin Catto http://www.gphysics.com
*
* 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 as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
import { __extends } from "tslib";
import { b2Collision } from "../../Collision/b2Collision";
import { b2Manifold } from "../../Collision/b2Manifold";
import { b2ManifoldPoint } from "../../Collision/b2ManifoldPoint";
import { b2Math, b2Vec2 } from "../../Common/Math";
import { b2PolygonContact } from "./b2PolygonContact";
var b2PolyAndConcaveArcContact = /** @class */ (function (_super) {
__extends(b2PolyAndConcaveArcContact, _super);
function b2PolyAndConcaveArcContact(shape1, shape2) {
var _this = _super.call(this, shape1, shape2) || this;
//Manifolds to return if collision with arc
_this.m_arcManifolds = [];
return _this;
}
b2PolyAndConcaveArcContact.Create = function (shape1, shape2, allocator) {
//void* mem = allocator->Allocate(sizeof(b2PolyContact));
return new b2PolyAndConcaveArcContact(shape1, shape2);
};
b2PolyAndConcaveArcContact.Destroy = function (contact, allocator) {
//((b2PolyContact*)contact)->~b2PolyContact();
//allocator->Free(contact, sizeof(b2PolyContact));
};
//~b2PolyContact() {}
//Flash only: Allow variable sized manifold field
b2PolyAndConcaveArcContact.prototype.enlargeManifolds = function (l) {
while (this.m_arcManifolds.length < l) {
var tMani = new b2Manifold();
tMani.pointCount = 0;
tMani.points[0].normalImpulse = 0;
tMani.points[0].tangentImpulse = 0;
this.m_arcManifolds.push(tMani);
}
};
b2PolyAndConcaveArcContact.prototype.Evaluate = function (listener) {
var b1 = this.m_shape1.m_body;
var b2 = this.m_shape2.m_body;
var b2_nullFeature /** uint */ = b2Collision.b2_nullFeature;
//var cornerMatchTol=b2Settings.b2_linearSlop;
var i /** int */, j /** int */;
var tMani;
//Store previous impulses
//Flash only: Using dictionary for contact matching
//TODO: Avoid creating or destroying objects here, as in super()
var impulses = [];
var cp, cp0;
var singleKey;
if (!this.m_arcColl) {
for (i = 0; i < this.m_manifoldCount; i++) {
for (j = 0; j < this.m_manifolds[i].pointCount; j++) {
cp0 = this.m_manifolds[i].points[j];
impulses[cp0.id.key] = cp = new b2ManifoldPoint();
cp.normalImpulse = cp0.normalImpulse;
cp.tangentImpulse = cp0.tangentImpulse;
}
}
if (this.m_manifoldCount == 1 && this.m_manifolds[0].pointCount == 1) {
singleKey = this.m_manifolds[0].points[0].id.key;
}
else {
singleKey = -1;
}
}
else {
for (i = 0; i < this.m_manifoldCount; i++) {
for (j = 0; j < this.m_arcManifolds[i].pointCount; j++) {
cp0 = this.m_arcManifolds[i].points[j];
impulses[cp0.id.key] = cp = new b2ManifoldPoint();
cp.normalImpulse = cp0.normalImpulse;
cp.tangentImpulse = cp0.tangentImpulse;
}
}
if (this.m_manifoldCount == 1 && this.m_arcManifolds[0].pointCount == 1) {
singleKey = this.m_arcManifolds[0].points[0].id.key;
}
else {
singleKey = -1;
}
}
//Do ordinary poly collision
_super.prototype.Evaluate.call(this, listener);
var polyManiCount = this.m_manifoldCount;
//The poly bounds the arc anyway
if (this.m_manifoldCount == 0)
return;
cp = this.m_manifolds[0].points[0];
var features = cp.id.features;
var edge = features.flip ? features.referenceEdge : features.incidentEdge;
var polySep = -Number.MAX_VALUE;
if (this.m_manifoldCount == 1 && edge != 0 && cp.separation < 0)
polySep = cp.separation;
//Do arc vs poly collision
{
//We have hit the arc edge, so we now test against the arc
var poly = this.m_shape1;
var arc = this.m_shape2;
var arcSep = Number.MAX_VALUE;
// Compute circle position in the frame of the polygon.
//TODO: Inline the calculations
//From local in arc to local in poly v= poly.R^T*(arc.R * v+arc.pos-poly.pos)
var localCenter = b2Math.b2MulXT(b1.m_xf, b2Math.b2MulX(b2.m_xf, arc.m_arcCenter));
var local0 = b2Math.b2MulX(b2.m_xf, arc.m_vertices[0]);
var world0 = local0.Copy();
local0 = b2Math.b2MulXT(b1.m_xf, local0);
var local1 = b2Math.b2MulX(b2.m_xf, arc.m_vertices[1]);
var world1 = local1.Copy();
local1 = b2Math.b2MulXT(b1.m_xf, local1);
var localNorm = b2Math.b2MulTMV(b1.m_xf.R, b2Math.b2MulMV(b2.m_xf.R, arc.m_normals[0].Copy()));
var worlds = [world0, world1];
var locals = [local0, local1];
var strictMode = false;
this.m_manifoldCount = 0;
//Used for when we are investigating near ends of the arc, but find a vx collision that is not actually on the end. Stores the vx
var autoApprove = [-1, -1];
//If we have found a collision near the ends, we can use that information later to restrict collisions not on the end. Stores the edge
var arcLimitations = [-1, -1];
//Find a collision between the ends of the arc and the poly
//We do this by treating each end as if it were a wedge extending to infinity
//Where one side of the wedge is colinear with the flat side of the end
//And the other side is the tangent to the arc.
//We then do a simplified version of PolyContact
if (edge == 0)
for (i = 0; i < 2; i++) {
var maxS = -Number.MAX_VALUE;
var maxEdge = -1;
var local = locals[i];
var s;
var arcNormal1, arcNormal2;
if (i == 0) {
arcNormal1 = b2Math.b2MulTMV(b1.m_xf.R, b2Math.b2MulMV(b2.m_xf.R, arc.m_normals[arc.m_vertexCount - 1]));
arcNormal2 = b2Math.SubtractVV(localCenter, local);
arcNormal2.Normalize();
}
else {
arcNormal2 = b2Math.b2MulTMV(b1.m_xf.R, b2Math.b2MulMV(b2.m_xf.R, arc.m_normals[1]));
arcNormal1 = b2Math.SubtractVV(localCenter, local);
arcNormal1.Normalize();
}
//Check for wedge stabbing the poly
//FindMaxSeparation
for (j = 0; j < poly.m_vertexCount; j++) {
//Check that the edge is angled correctly
var polyVx = poly.m_vertices[j];
var polyNormal = poly.m_normals[j];
if (polyNormal.x * arcNormal1.y - polyNormal.y * arcNormal1.x < 0)
continue;
if (polyNormal.x * arcNormal2.y - polyNormal.y * arcNormal2.x > 0)
continue;
var v2x = local.x - polyVx.x;
var v2y = local.y - polyVx.y;
//Get separation
s = v2x * polyNormal.x + v2y * polyNormal.y;
if (s > 0) {
maxEdge = -1;
break;
}
if (s > maxS) {
maxS = s;
maxEdge = j;
}
}
if (maxEdge != -1) {
polyNormal = poly.m_normals[maxEdge];
polyVx = poly.m_vertices[maxEdge];
var polyVx2 = poly.m_vertices[maxEdge + 1 < poly.m_vertexCount ? maxEdge + 1 : 0];
//FindIncidentEdge
var dot1 = polyNormal.x * arcNormal1.x + polyNormal.y * arcNormal1.y;
var dot2 = polyNormal.x * arcNormal2.x + polyNormal.y * arcNormal2.y;
var tangent;
if (dot1 < dot2) {
tangent = b2Math.b2CrossVF(arcNormal1, 1);
}
else {
tangent = b2Math.b2CrossVF(arcNormal2, -1);
}
//Clip
var t1 = (local.x - polyVx.x) * polyNormal.x + (local.y - polyVx.y) * polyNormal.y;
var t2 = tangent.x * polyNormal.x + tangent.y * polyNormal.y;
var t3 = -t1 / t2;
v2x = polyVx2.x - polyVx.x;
v2y = polyVx2.y - polyVx.y;
var t4 = (local.x + t3 * tangent.x - polyVx.x) * v2x + (local.y + t3 * tangent.y - polyVx.y) * v2y;
if (t4 < 0 || t4 > v2x * v2x + v2y * v2y) {
maxEdge = -1;
}
}
//Technically, we should check for the poly stabbing the wedge case as well
//But this has problems about treating the wedge as infinite
//Instead we do a cheaper test. It's not so much wrong as just different from PolyContact
if (maxEdge != -1) {
arcLimitations[i] = maxEdge;
if (i == 0) {
s = (polyVx2.x - local.x) * arcNormal2.x + (polyVx2.y - local.y) * arcNormal2.y;
if (maxS < s && s < 0) {
autoApprove[i] = maxEdge + 1 < poly.m_vertexCount ? maxEdge + 1 : 0;
maxEdge = -1;
}
}
else {
s = (polyVx.x - local.x) * arcNormal1.x + (polyVx.y - local.y) * arcNormal1.y;
if (maxS < s && s < 0) {
autoApprove[i] = maxEdge;
maxEdge = -1;
}
}
}
if (maxEdge != -1) {
//Generate a collision
this.m_manifoldCount++;
this.enlargeManifolds(this.m_manifoldCount);
tMani = this.m_arcManifolds[this.m_manifoldCount - 1];
tMani.pointCount = 1;
var tPoint = tMani.points[0];
tPoint.localPoint1.SetV(locals[i]);
tPoint.localPoint2.SetV(arc.m_vertices[i]);
tPoint.id.features.incidentEdge = b2_nullFeature;
tPoint.id.features.incidentVertex = i;
tPoint.id.features.referenceEdge = maxEdge;
tPoint.id.features.flip = 0;
tPoint.separation = maxS;
tPoint.normalImpulse = 0;
tPoint.tangentImpulse = 0;
tMani.normal = b2Math.b2MulMV(b1.m_xf.R, poly.m_normals[maxEdge]);
arcSep = Math.min(arcSep, tPoint.separation);
}
}
//Find collision. between poly and the main part of arc
//Note due to concavity, we can have a seperate manifold for each point
//On the other hand, we will have no two point manifolds, which makes life easier
var bool1;
var tVec;
var topVx;
var d2, d, s1, s2;
var l;
var direction;
var currVx, startVx;
if (arcLimitations[1] == -1) {
if (arcLimitations[0] == -1) {
direction = 1;
startVx = 0;
}
else {
direction = poly.m_vertexCount - 1; //I.e. -1 mod poly.m_vertexCount
startVx = arcLimitations[0] + 1 == poly.m_vertexCount ? 0 : arcLimitations[0] + 1;
}
}
else {
direction = 1;
startVx = arcLimitations[1];
}
//We restrict our matches to one contiguous block
var foundBlock = false;
//direction = 1;
//startVx = 0;
currVx = startVx;
do {
//Innocent until proven guilty
bool1 = true;
//Test vs arcLimitations
tVec = poly.m_vertices[currVx];
if (arcLimitations[0] != -1) {
l = arcLimitations[0];
topVx = poly.m_vertices[l + 1 == poly.m_vertexCount ? 0 : l + 1];
bool1 = bool1 && ((tVec.x - topVx.x) * poly.m_normals[l].y - (tVec.y - topVx.y) * poly.m_normals[l].x > 0);
}
if (arcLimitations[1] != -1) {
l = arcLimitations[1];
topVx = poly.m_vertices[l];
bool1 = bool1 && ((tVec.x - topVx.x) * poly.m_normals[l].y - (tVec.y - topVx.y) * poly.m_normals[l].x < 0);
}
if (foundBlock && !bool1) {
//Make this loop the last, just incase it's autoapprove
startVx = (currVx + direction) % poly.m_vertexCount;
}
if (bool1)
foundBlock = true;
//Test vs __^__
tVec = new b2Vec2(poly.m_vertices[currVx].x - localCenter.x, poly.m_vertices[currVx].y - localCenter.y);
d2 = tVec.x * tVec.x + tVec.y * tVec.y;
d = Math.sqrt(d2);
s1 = arc.m_radius - d;
s2 = (poly.m_vertices[currVx].x - local0.x) * localNorm.x + (poly.m_vertices[currVx].y - local0.y) * localNorm.y;
s = Math.max(s1, s2);
arcSep = Math.min(arcSep, s);
bool1 = bool1 && (s < 0);
if (bool1 || currVx == autoApprove[0] || currVx == autoApprove[1]) {
this.m_manifoldCount++;
this.enlargeManifolds(this.m_manifoldCount);
tMani = this.m_arcManifolds[this.m_manifoldCount - 1];
tMani.pointCount = 1;
tPoint = tMani.points[0];
tPoint.id.features.incidentEdge = b2_nullFeature;
tPoint.id.features.incidentVertex = currVx;
tPoint.id.features.referenceEdge = 0;
tPoint.id.features.flip = 0;
tPoint.normalImpulse = 0;
tPoint.tangentImpulse = 0;
tVec.x /= d;
tVec.y /= d;
var tMat = b1.m_xf.R;
tMani.normal.x = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y;
tMani.normal.y = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y;
tVec = poly.m_vertices[currVx];
//tVec=new b2Vec2(localCenter.x,localCenter.y);//Use this instead?
tPoint.localPoint1.SetV(tVec);
tPoint.localPoint2.SetV(b2Math.b2MulXT(b2.m_xf, b2Math.b2MulX(b1.m_xf, tVec)));
tPoint.separation = arc.m_radius - d;
}
currVx = (currVx + direction) % poly.m_vertexCount;
} while (currVx != startVx);
}
var arcManiCount /** int */ = this.m_manifoldCount;
if (polySep > arcSep) {
this.m_arcColl = false;
this.m_manifoldCount = polyManiCount;
//Match contact points
for (i = 0; i < this.m_manifoldCount; i++) {
for (j = 0; j < this.m_manifolds[i].pointCount; j++) {
cp = this.m_manifolds[i].points[j];
cp0 = impulses[cp.id.key];
if (cp0) {
cp.normalImpulse = cp0.normalImpulse;
cp.tangentImpulse = cp0.tangentImpulse;
}
}
}
if (singleKey != -1 && this.m_manifoldCount == 1 && this.m_manifolds[0].pointCount == 1) {
cp = this.m_manifolds[0].points[0];
cp0 = impulses[singleKey];
if (cp0) {
cp.normalImpulse = cp0.normalImpulse;
cp.tangentImpulse = cp0.tangentImpulse;
}
}
}
else {
this.m_arcColl = true;
this.m_manifoldCount = arcManiCount;
//Match contact points
for (i = 0; i < this.m_manifoldCount; i++) {
for (j = 0; j < this.m_arcManifolds[i].pointCount; j++) {
cp = this.m_arcManifolds[i].points[j];
cp0 = impulses[cp.id.key];
if (cp0) {
cp.normalImpulse = cp0.normalImpulse;
cp.tangentImpulse = cp0.tangentImpulse;
}
}
}
if (singleKey != -1 && this.m_manifoldCount == 1 && this.m_arcManifolds[0].pointCount == 1) {
cp = this.m_arcManifolds[0].points[0];
cp0 = impulses[singleKey];
if (cp0) {
cp.normalImpulse = cp0.normalImpulse;
cp.tangentImpulse = cp0.tangentImpulse;
}
}
}
};
b2PolyAndConcaveArcContact.prototype.GetManifolds = function () {
if (this.m_arcColl)
return this.m_arcManifolds;
else
return this.m_manifolds;
};
return b2PolyAndConcaveArcContact;
}(b2PolygonContact));
export { b2PolyAndConcaveArcContact };