UNPKG

@awayfl/awayfl-player

Version:

Flash Player emulator for executing SWF files (published for FP versions 6 and up) in javascript

838 lines (735 loc) 25.6 kB
/** * Convex polygon. The vertices must be in CCW order for a right-handed * coordinate system with the z-axis coming out of the screen. * @see b2PolygonDef */ import { ASArray } from '@awayfl/avm2'; import { b2Shape } from './b2Shape'; import { b2Vec2, b2Math, b2Mat22, b2Transform } from '../../Common/Math'; import { b2Settings } from '../../Common/b2Settings'; import { b2RayCastOutput } from '../b2RayCastOutput'; import { b2RayCastInput } from '../b2RayCastInput'; import { b2AABB } from '../b2AABB'; import { b2MassData } from './b2MassData'; import { b2OBB } from '../b2OBB'; export class b2PolygonShape extends b2Shape { __fast__: boolean = true; public Copy(): b2Shape { const s: b2PolygonShape = new b2PolygonShape(); s.Set(this); return s; } public Set(other: b2Shape): void { super.Set(other); if (other instanceof b2PolygonShape) { const other2: b2PolygonShape = other as b2PolygonShape; this.m_centroid.SetV(other2.m_centroid); this.m_vertexCount = other2.m_vertexCount; this.Reserve(this.m_vertexCount); for (let i: number /** int */ = 0; i < this.m_vertexCount; i++) { this.m_vertices[i].SetV(other2.m_vertices[i]); this.m_normals[i].SetV(other2.m_normals[i]); } } } /** * Copy vertices. This assumes the vertices define a convex polygon. * It is assumed that the exterior is the the right of each edge. */ public SetAsArray(vertices: Array<b2Vec2> | ASArray, vertexCount: number = 0): void { let vert = <any>vertices; if (typeof vert.axInitializer === 'function') { vert = (<ASArray>vertices).value; } const v = vert.slice(); this.SetAsVector(v, vertexCount); } public static AsArray(vertices: Array<b2Vec2>, vertexCount: number): b2PolygonShape { const polygonShape: b2PolygonShape = new b2PolygonShape(); polygonShape.SetAsArray(vertices, vertexCount); return polygonShape; } /** * Copy vertices. This assumes the vertices define a convex polygon. * It is assumed that the exterior is the the right of each edge. */ public SetAsVector(vertices: Array<b2Vec2>, vertexCount: number = 0): void { if (vertexCount == 0) vertexCount = vertices.length; b2Settings.b2Assert(2 <= vertexCount); this.m_vertexCount = vertexCount; this.Reserve(vertexCount); let i: number /** int */; // Copy vertices for (i = 0; i < this.m_vertexCount; i++) { this.m_vertices[i].SetV(vertices[i]); } // Compute normals. Ensure the edges have non-zero length. for (i = 0; i < this.m_vertexCount; ++i) { const i1: number /** int */ = i; const i2: number /** int */ = i + 1 < this.m_vertexCount ? i + 1 : 0; const edge: b2Vec2 = b2Math.SubtractVV(this.m_vertices[i2], this.m_vertices[i1]); b2Settings.b2Assert(edge.LengthSquared() > Number.MIN_VALUE /* * Number.MIN_VALUE*/); this.m_normals[i].SetV(b2Math.CrossVF(edge, 1.0)); this.m_normals[i].Normalize(); } //#ifdef _DEBUG // Ensure the polygon is convex and the interior // is to the left of each edge. //for (int32 i = 0; i < m_vertexCount; ++i) //{ //int32 i1 = i; //int32 i2 = i + 1 < m_vertexCount ? i + 1 : 0; //b2Vec2 edge = m_vertices[i2] - m_vertices[i1]; //for (int32 j = 0; j < m_vertexCount; ++j) //{ // Don't check vertices on the current edge. //if (j == i1 || j == i2) //{ //continue; //} // //b2Vec2 r = m_vertices[j] - m_vertices[i1]; // Your polygon is non-convex (it has an indentation) or // has colinear edges. //float32 s = b2Cross(edge, r); //b2Assert(s > 0.0f); //} //} //#endif // Compute the polygon centroid this.m_centroid = b2PolygonShape.ComputeCentroid(this.m_vertices, this.m_vertexCount); } public static AsVector(vertices: Array<b2Vec2>, vertexCount: number): b2PolygonShape { const polygonShape: b2PolygonShape = new b2PolygonShape(); polygonShape.SetAsVector(vertices, vertexCount); return polygonShape; } /** * Build vertices to represent an axis-aligned box. * @param hx the half-width. * @param hy the half-height. */ public SetAsBox(hx: number, hy: number): void { this.m_vertexCount = 4; this.Reserve(4); this.m_vertices[0].Set(-hx, -hy); this.m_vertices[1].Set(hx, -hy); this.m_vertices[2].Set(hx, hy); this.m_vertices[3].Set(-hx, hy); this.m_normals[0].Set(0.0, -1.0); this.m_normals[1].Set(1.0, 0.0); this.m_normals[2].Set(0.0, 1.0); this.m_normals[3].Set(-1.0, 0.0); this.m_centroid.SetZero(); } public static AsBox(hx: number, hy: number): b2PolygonShape { const polygonShape: b2PolygonShape = new b2PolygonShape(); polygonShape.SetAsBox(hx, hy); return polygonShape; } /** * Build vertices to represent an oriented box. * @param hx the half-width. * @param hy the half-height. * @param center the center of the box in local coordinates. * @param angle the rotation of the box in local coordinates. */ private static s_mat: b2Mat22 = new b2Mat22(); public SetAsOrientedBox(hx: number, hy: number, center: b2Vec2 = null, angle: number = 0.0): void { this.m_vertexCount = 4; this.Reserve(4); this.m_vertices[0].Set(-hx, -hy); this.m_vertices[1].Set(hx, -hy); this.m_vertices[2].Set(hx, hy); this.m_vertices[3].Set(-hx, hy); this.m_normals[0].Set(0.0, -1.0); this.m_normals[1].Set(1.0, 0.0); this.m_normals[2].Set(0.0, 1.0); this.m_normals[3].Set(-1.0, 0.0); this.m_centroid = center; const xf: b2Transform = new b2Transform(); xf.position = center; xf.R.Set(angle); // Transform vertices and normals. for (let i: number /** int */ = 0; i < this.m_vertexCount; ++i) { this.m_vertices[i] = b2Math.MulX(xf, this.m_vertices[i]); this.m_normals[i] = b2Math.MulMV(xf.R, this.m_normals[i]); } } public static AsOrientedBox(hx: number, hy: number, center: b2Vec2 = null, angle: number = 0.0): b2PolygonShape { const polygonShape: b2PolygonShape = new b2PolygonShape(); polygonShape.SetAsOrientedBox(hx, hy, center, angle); return polygonShape; } /** * Set this as a single edge. */ public SetAsEdge(v1: b2Vec2, v2: b2Vec2): void { this.m_vertexCount = 2; this.Reserve(2); this.m_vertices[0].SetV(v1); this.m_vertices[1].SetV(v2); this.m_centroid.x = 0.5 * (v1.x + v2.x); this.m_centroid.y = 0.5 * (v1.y + v2.y); this.m_normals[0] = b2Math.CrossVF(b2Math.SubtractVV(v2, v1), 1.0); this.m_normals[0].Normalize(); this.m_normals[1].x = -this.m_normals[0].x; this.m_normals[1].y = -this.m_normals[0].y; } /** * Set this as a single edge. */ public static AsEdge(v1: b2Vec2, v2: b2Vec2): b2PolygonShape { const polygonShape: b2PolygonShape = new b2PolygonShape(); polygonShape.SetAsEdge(v1, v2); return polygonShape; } /** * @inheritDoc */ public TestPoint(xf: b2Transform, p: b2Vec2): boolean { let tVec: b2Vec2; //b2Vec2 pLocal = b2MulT(xf.R, p - xf.position); const tMat: b2Mat22 = xf.R; let tX: number = p.x - xf.position.x; let tY: number = p.y - xf.position.y; const pLocalX: number = (tX * tMat.col1.x + tY * tMat.col1.y); const pLocalY: number = (tX * tMat.col2.x + tY * tMat.col2.y); for (let i: number /** int */ = 0; i < this.m_vertexCount; ++i) { //float32 dot = b2Dot(m_normals[i], pLocal - m_vertices[i]); tVec = this.m_vertices[i]; tX = pLocalX - tVec.x; tY = pLocalY - tVec.y; tVec = this.m_normals[i]; const dot: number = (tVec.x * tX + tVec.y * tY); if (dot > 0.0) { return false; } } return true; } /** * @inheritDoc */ public RayCast(output: b2RayCastOutput, input: b2RayCastInput, transform: b2Transform): boolean { let lower: number = 0.0; let upper: number = input.maxFraction; let tX: number; let tY: number; let tMat: b2Mat22; let tVec: b2Vec2; // Put the ray into the polygon's frame of reference. (AS3 Port Manual inlining follows) //b2Vec2 p1 = b2MulT(transform.R, segment.p1 - transform.position); tX = input.p1.x - transform.position.x; tY = input.p1.y - transform.position.y; tMat = transform.R; const p1X: number = (tX * tMat.col1.x + tY * tMat.col1.y); const p1Y: number = (tX * tMat.col2.x + tY * tMat.col2.y); //b2Vec2 p2 = b2MulT(transform.R, segment.p2 - transform.position); tX = input.p2.x - transform.position.x; tY = input.p2.y - transform.position.y; tMat = transform.R; const p2X: number = (tX * tMat.col1.x + tY * tMat.col1.y); const p2Y: number = (tX * tMat.col2.x + tY * tMat.col2.y); //b2Vec2 d = p2 - p1; const dX: number = p2X - p1X; const dY: number = p2Y - p1Y; let index: number /** int */ = -1; for (let i: number /** int */ = 0; i < this.m_vertexCount; ++i) { // p = p1 + a * d // dot(normal, p - v) = 0 // dot(normal, p1 - v) + a * dot(normal, d) = 0 //float32 numerator = b2Dot(m_normals[i], m_vertices[i] - p1); tVec = this.m_vertices[i]; tX = tVec.x - p1X; tY = tVec.y - p1Y; tVec = this.m_normals[i]; const numerator: number = (tVec.x * tX + tVec.y * tY); //float32 denominator = b2Dot(m_normals[i], d); const denominator: number = (tVec.x * dX + tVec.y * dY); if (denominator == 0.0) { if (numerator < 0.0) { return false; } } else { // Note: we want this predicate without division: // lower < numerator / denominator, where denominator < 0 // Since denominator < 0, we have to flip the inequality: // lower < numerator / denominator <==> denominator * lower > numerator. if (denominator < 0.0 && numerator < lower * denominator) { // Increase lower. // The segment enters this half-space. lower = numerator / denominator; index = i; } else if (denominator > 0.0 && numerator < upper * denominator) { // Decrease upper. // The segment exits this half-space. upper = numerator / denominator; } } if (upper < lower - Number.MIN_VALUE) { return false; } } //b2Settings.b2Assert(0.0 <= lower && lower <= input.maxLambda); if (index >= 0) { output.fraction = lower; //output.normal = b2Mul(transform.R, m_normals[index]); tMat = transform.R; tVec = this.m_normals[index]; output.normal.x = (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); output.normal.y = (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); return true; } return false; } /** * @inheritDoc */ public ComputeAABB(aabb: b2AABB, xf: b2Transform): void { //var lower:b2Vec2 = b2Math.MulX(xf, m_vertices[0]); const tMat: b2Mat22 = xf.R; let tVec: b2Vec2 = this.m_vertices[0]; let lowerX: number = xf.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); let lowerY: number = xf.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); let upperX: number = lowerX; let upperY: number = lowerY; for (let i: number /** int */ = 1; i < this.m_vertexCount; ++i) { tVec = this.m_vertices[i]; const vX: number = xf.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); const vY: number = xf.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); lowerX = lowerX < vX ? lowerX : vX; lowerY = lowerY < vY ? lowerY : vY; upperX = upperX > vX ? upperX : vX; upperY = upperY > vY ? upperY : vY; } aabb.lowerBound.x = lowerX - this.m_radius; aabb.lowerBound.y = lowerY - this.m_radius; aabb.upperBound.x = upperX + this.m_radius; aabb.upperBound.y = upperY + this.m_radius; } /** * @inheritDoc */ public ComputeMass(massData: b2MassData, density: number): void { // Polygon mass, centroid, and inertia. // Let rho be the polygon density in mass per unit area. // Then: // mass = rho * int(dA) // centroid.x = (1/mass) * rho * int(x * dA) // centroid.y = (1/mass) * rho * int(y * dA) // I = rho * int((x*x + y*y) * dA) // // We can compute these integrals by summing all the integrals // for each triangle of the polygon. To evaluate the integral // for a single triangle, we make a change of variables to // the (u,v) coordinates of the triangle: // x = x0 + e1x * u + e2x * v // y = y0 + e1y * u + e2y * v // where 0 <= u && 0 <= v && u + v <= 1. // // We integrate u from [0,1-v] and then v from [0,1]. // We also need to use the Jacobian of the transformation: // D = cross(e1, e2) // // Simplification: triangle centroid = (1/3) * (p1 + p2 + p3) // // The rest of the derivation is handled by computer algebra. //b2Settings.b2Assert(m_vertexCount >= 2); // A line segment has zero mass. if (this.m_vertexCount == 2) { massData.center.x = 0.5 * (this.m_vertices[0].x + this.m_vertices[1].x); massData.center.y = 0.5 * (this.m_vertices[0].y + this.m_vertices[1].y); massData.mass = 0.0; massData.I = 0.0; return; } //b2Vec2 center; center.Set(0.0f, 0.0f); let centerX: number = 0.0; let centerY: number = 0.0; let area: number = 0.0; let I: number = 0.0; // pRef is the reference point for forming triangles. // It's location doesn't change the result (except for rounding error). //b2Vec2 pRef(0.0f, 0.0f); const p1X: number = 0.0; const p1Y: number = 0.0; /*#if 0 // This code would put the reference point inside the polygon. for (int32 i = 0; i < m_vertexCount; ++i) { pRef += m_vertices[i]; } pRef *= 1.0f / count; #endif*/ const k_inv3: number = 1.0 / 3.0; for (let i: number /** int */ = 0; i < this.m_vertexCount; ++i) { // Triangle vertices. //b2Vec2 p1 = pRef; // //b2Vec2 p2 = m_vertices[i]; const p2: b2Vec2 = this.m_vertices[i]; //b2Vec2 p3 = i + 1 < m_vertexCount ? m_vertices[i+1] : m_vertices[0]; const p3: b2Vec2 = i + 1 < this.m_vertexCount ? this.m_vertices[i + 1] : this.m_vertices[0]; //b2Vec2 e1 = p2 - p1; const e1X: number = p2.x - p1X; const e1Y: number = p2.y - p1Y; //b2Vec2 e2 = p3 - p1; const e2X: number = p3.x - p1X; const e2Y: number = p3.y - p1Y; //float32 D = b2Cross(e1, e2); const D: number = e1X * e2Y - e1Y * e2X; //float32 triangleArea = 0.5f * D; const triangleArea: number = 0.5 * D; area += triangleArea; // Area weighted centroid //center += triangleArea * k_inv3 * (p1 + p2 + p3); centerX += triangleArea * k_inv3 * (p1X + p2.x + p3.x); centerY += triangleArea * k_inv3 * (p1Y + p2.y + p3.y); //float32 px = p1.x, py = p1.y; const px: number = p1X; const py: number = p1Y; //float32 ex1 = e1.x, ey1 = e1.y; const ex1: number = e1X; const ey1: number = e1Y; //float32 ex2 = e2.x, ey2 = e2.y; const ex2: number = e2X; const ey2: number = e2Y; //float32 intx2 = k_inv3 * (0.25f * (ex1*ex1 + ex2*ex1 + ex2*ex2) + (px*ex1 + px*ex2)) + 0.5f*px*px; const intx2: number = k_inv3 * (0.25 * (ex1 * ex1 + ex2 * ex1 + ex2 * ex2) + (px * ex1 + px * ex2)) + 0.5 * px * px; //float32 inty2 = k_inv3 * (0.25f * (ey1*ey1 + ey2*ey1 + ey2*ey2) + (py*ey1 + py*ey2)) + 0.5f*py*py; const inty2: number = k_inv3 * (0.25 * (ey1 * ey1 + ey2 * ey1 + ey2 * ey2) + (py * ey1 + py * ey2)) + 0.5 * py * py; I += D * (intx2 + inty2); } // Total mass massData.mass = density * area; // Center of mass //b2Settings.b2Assert(area > Number.MIN_VALUE); //center *= 1.0f / area; centerX *= 1.0 / area; centerY *= 1.0 / area; //massData->center = center; massData.center.Set(centerX, centerY); // Inertia tensor relative to the local origin. massData.I = density * I; } /** * @inheritDoc */ public ComputeSubmergedArea( normal: b2Vec2, offset: number, xf: b2Transform, c: b2Vec2): number { // Transform plane into shape co-ordinates const normalL: b2Vec2 = b2Math.MulTMV(xf.R, normal); const offsetL: number = offset - b2Math.Dot(normal, xf.position); const depths: Array<number> = new Array<number>(); let diveCount: number /** int */ = 0; let intoIndex: number /** int */ = -1; let outoIndex: number /** int */ = -1; let lastSubmerged: boolean = false; let i: number /** int */ ; for (i = 0; i < this.m_vertexCount;++i) { depths[i] = b2Math.Dot(normalL, this.m_vertices[i]) - offsetL; const isSubmerged: boolean = depths[i] < -Number.MIN_VALUE; if (i > 0) { if (isSubmerged) { if (!lastSubmerged) { intoIndex = i - 1; diveCount++; } } else { if (lastSubmerged) { outoIndex = i - 1; diveCount++; } } } lastSubmerged = isSubmerged; } switch (diveCount) { case 0: if (lastSubmerged) { // Completely submerged const md: b2MassData = new b2MassData(); this.ComputeMass(md, 1); c.SetV(b2Math.MulX(xf, md.center)); return md.mass; } else { //Completely dry return 0; } break; case 1: if (intoIndex == -1) { intoIndex = this.m_vertexCount - 1; } else { outoIndex = this.m_vertexCount - 1; } break; } const intoIndex2: number /** int */ = (intoIndex + 1) % this.m_vertexCount; const outoIndex2: number /** int */ = (outoIndex + 1) % this.m_vertexCount; const intoLamdda: number = (0 - depths[intoIndex]) / (depths[intoIndex2] - depths[intoIndex]); const outoLamdda: number = (0 - depths[outoIndex]) / (depths[outoIndex2] - depths[outoIndex]); const intoVec: b2Vec2 = new b2Vec2(this.m_vertices[intoIndex].x * (1 - intoLamdda) + this.m_vertices[intoIndex2].x * intoLamdda, this.m_vertices[intoIndex].y * (1 - intoLamdda) + this.m_vertices[intoIndex2].y * intoLamdda); const outoVec: b2Vec2 = new b2Vec2(this.m_vertices[outoIndex].x * (1 - outoLamdda) + this.m_vertices[outoIndex2].x * outoLamdda, this.m_vertices[outoIndex].y * (1 - outoLamdda) + this.m_vertices[outoIndex2].y * outoLamdda); // Initialize accumulator let area: number = 0; const center: b2Vec2 = new b2Vec2(); let p2: b2Vec2 = this.m_vertices[intoIndex2]; let p3: b2Vec2; // An awkward loop from intoIndex2+1 to outIndex2 i = intoIndex2; while (i != outoIndex2) { i = (i + 1) % this.m_vertexCount; if (i == outoIndex2) p3 = outoVec; else p3 = this.m_vertices[i]; const triangleArea: number = 0.5 * ((p2.x - intoVec.x) * (p3.y - intoVec.y) - (p2.y - intoVec.y) * (p3.x - intoVec.x)); area += triangleArea; // Area weighted centroid center.x += triangleArea * (intoVec.x + p2.x + p3.x) / 3; center.y += triangleArea * (intoVec.y + p2.y + p3.y) / 3; p2 = p3; } //Normalize and transform centroid center.Multiply(1 / area); c.SetV(b2Math.MulX(xf, center)); return area; } /** * Get the vertex count. */ public GetVertexCount(): number /** int */ { return this.m_vertexCount; } /** * Get the vertices in local coordinates. */ public GetVertices(): Array<b2Vec2> { return this.m_vertices; } /** * Get the edge normal vectors. There is one for each vertex. */ public GetNormals(): Array<b2Vec2> { return this.m_normals; } /** * Get the supporting vertex index in the given direction. */ public GetSupport(d: b2Vec2): number /** int */ { let bestIndex: number /** int */ = 0; let bestValue: number = this.m_vertices[0].x * d.x + this.m_vertices[0].y * d.y; for (let i: number /** int */ = 1; i < this.m_vertexCount; ++i) { const value: number = this.m_vertices[i].x * d.x + this.m_vertices[i].y * d.y; if (value > bestValue) { bestIndex = i; bestValue = value; } } return bestIndex; } public GetSupportVertex(d: b2Vec2): b2Vec2 { let bestIndex: number /** int */ = 0; let bestValue: number = this.m_vertices[0].x * d.x + this.m_vertices[0].y * d.y; for (let i: number /** int */ = 1; i < this.m_vertexCount; ++i) { const value: number = this.m_vertices[i].x * d.x + this.m_vertices[i].y * d.y; if (value > bestValue) { bestIndex = i; bestValue = value; } } return this.m_vertices[bestIndex]; } // TODO: Expose this private Validate(): boolean { /* // Ensure the polygon is convex. for (int32 i = 0; i < m_vertexCount; ++i) { for (int32 j = 0; j < m_vertexCount; ++j) { // Don't check vertices on the current edge. if (j == i || j == (i + 1) % m_vertexCount) { continue; } // Your polygon is non-convex (it has an indentation). // Or your polygon is too skinny. float32 s = b2Dot(m_normals[i], this.m_vertices[j] - this.m_vertices[i]); b2Assert(s < -b2_linearSlop); } } // Ensure the polygon is counter-clockwise. for (i = 1; i < m_vertexCount; ++i) { var cross:number = b2Math.b2CrossVV(m_normals[int(i-1)], m_normals[i]); // Keep asinf happy. cross = b2Math.b2Clamp(cross, -1.0, 1.0); // You have consecutive edges that are almost parallel on your polygon. var angle:number = Math.asin(cross); //b2Assert(angle > b2_angularSlop); trace(angle > b2Settings.b2_angularSlop); } */ return false; } //--------------- Internals Below ------------------- /** * @private */ constructor() { super(); //b2Settings.b2Assert(def.type == e_polygonShape); this.m_type = b2Shape.e_polygonShape; this.m_centroid = new b2Vec2(); this.m_vertices = new Array<b2Vec2>(); this.m_normals = new Array<b2Vec2>(); } private Reserve(count: number /** int */): void { for (let i: number /** int */ = this.m_vertices.length; i < count; i++) { this.m_vertices[i] = new b2Vec2(); this.m_normals[i] = new b2Vec2(); } } // Local position of the polygon centroid. public m_centroid: b2Vec2; public m_vertices: Array<b2Vec2>; public m_normals: Array<b2Vec2>; public m_vertexCount: number /** int */ ; /** * Computes the centroid of the given polygon * @param vs vector of b2Vec specifying a polygon * @param count length of vs * @return the polygon centroid */ public static ComputeCentroid(vs: Array<b2Vec2>, count: number /** uint */): b2Vec2 { //b2Settings.b2Assert(count >= 3); //b2Vec2 c; c.Set(0.0f, 0.0f); const c: b2Vec2 = new b2Vec2(); let area: number = 0.0; // pRef is the reference point for forming triangles. // It's location doesn't change the result (except for rounding error). //b2Vec2 pRef(0.0f, 0.0f); const p1X: number = 0.0; const p1Y: number = 0.0; /*#if 0 // This code would put the reference point inside the polygon. for (int32 i = 0; i < count; ++i) { pRef += vs[i]; } pRef *= 1.0f / count; #endif*/ const inv3: number = 1.0 / 3.0; for (let i: number /** int */ = 0; i < count; ++i) { // Triangle vertices. //b2Vec2 p1 = pRef; // 0.0, 0.0 //b2Vec2 p2 = vs[i]; const p2: b2Vec2 = vs[i]; //b2Vec2 p3 = i + 1 < count ? vs[i+1] : vs[0]; const p3: b2Vec2 = i + 1 < count ? vs[i + 1] : vs[0]; //b2Vec2 e1 = p2 - p1; const e1X: number = p2.x - p1X; const e1Y: number = p2.y - p1Y; //b2Vec2 e2 = p3 - p1; const e2X: number = p3.x - p1X; const e2Y: number = p3.y - p1Y; //float32 D = b2Cross(e1, e2); const D: number = (e1X * e2Y - e1Y * e2X); //float32 triangleArea = 0.5f * D; const triangleArea: number = 0.5 * D; area += triangleArea; // Area weighted centroid //c += triangleArea * inv3 * (p1 + p2 + p3); c.x += triangleArea * inv3 * (p1X + p2.x + p3.x); c.y += triangleArea * inv3 * (p1Y + p2.y + p3.y); } // Centroid //beSettings.b2Assert(area > Number.MIN_VALUE); //c *= 1.0 / area; c.x *= 1.0 / area; c.y *= 1.0 / area; return c; } /** * Computes a polygon's OBB * @see http://www.geometrictools.com/Documentation/MinimumAreaRectangle.pdf */ public static ComputeOBB(obb: b2OBB, vs: Array<b2Vec2>, count: number /** int */): void { let i: number /** int */ ; const p: Array<b2Vec2> = new Array<b2Vec2>(count + 1); for (i = 0; i < count; ++i) { p[i] = vs[i]; } p[count] = p[0]; let minArea: number = Number.MAX_VALUE; for (i = 1; i <= count; ++i) { const root: b2Vec2 = p[i - 1]; //b2Vec2 ux = p[i] - root; let uxX: number = p[i].x - root.x; let uxY: number = p[i].y - root.y; //var length:number = ux.Normalize(); const length: number = Math.sqrt(uxX * uxX + uxY * uxY); uxX /= length; uxY /= length; //b2Settings.b2Assert(length > Number.MIN_VALUE); //b2Vec2 uy(-ux.y, ux.x); const uyX: number = -uxY; const uyY: number = uxX; //b2Vec2 lower(FLT_MAX, FLT_MAX); let lowerX: number = Number.MAX_VALUE; let lowerY: number = Number.MAX_VALUE; //b2Vec2 upper(-FLT_MAX, -FLT_MAX); let upperX: number = -Number.MAX_VALUE; let upperY: number = -Number.MAX_VALUE; for (let j: number /** int */ = 0; j < count; ++j) { //b2Vec2 d = p[j] - root; const dX: number = p[j].x - root.x; const dY: number = p[j].y - root.y; //b2Vec2 r; //var rX:number = b2Dot(ux, d); const rX: number = (uxX * dX + uxY * dY); //var rY:number = b2Dot(uy, d); const rY: number = (uyX * dX + uyY * dY); //lower = b2Min(lower, r); if (rX < lowerX) lowerX = rX; if (rY < lowerY) lowerY = rY; //upper = b2Max(upper, r); if (rX > upperX) upperX = rX; if (rY > upperY) upperY = rY; } const area: number = (upperX - lowerX) * (upperY - lowerY); if (area < 0.95 * minArea) { minArea = area; //obb->R.col1 = ux; obb.R.col1.x = uxX; obb.R.col1.y = uxY; //obb->R.col2 = uy; obb.R.col2.x = uyX; obb.R.col2.y = uyY; //b2Vec2 center = 0.5f * (lower + upper); const centerX: number = 0.5 * (lowerX + upperX); const centerY: number = 0.5 * (lowerY + upperY); //obb->center = root + b2Mul(obb->R, center); const tMat: b2Mat22 = obb.R; obb.center.x = root.x + (tMat.col1.x * centerX + tMat.col2.x * centerY); obb.center.y = root.y + (tMat.col1.y * centerX + tMat.col2.y * centerY); //obb->extents = 0.5f * (upper - lower); obb.extents.x = 0.5 * (upperX - lowerX); obb.extents.y = 0.5 * (upperY - lowerY); } } //b2Settings.b2Assert(minArea < Number.MAX_VALUE); } }