the-world-engine
Version:
three.js based, unity like game engine for browser
444 lines (411 loc) • 14.9 kB
JavaScript
import { b2_epsilon, b2_maxFloat, b2_linearSlop, b2_polygonRadius } from "../common/b2_settings.js";
import { b2Vec2, b2Rot, b2Transform } from "../common/b2_math.js";
import { b2MassData } from "./b2_shape.js";
import { b2Shape, b2ShapeType } from "./b2_shape.js";
export class b2PolygonShape extends b2Shape {
constructor() {
super(b2ShapeType.e_polygonShape, b2_polygonRadius);
this.m_centroid = new b2Vec2(0, 0);
this.m_vertices = [];
this.m_normals = [];
this.m_count = 0;
}
Clone() {
return (new b2PolygonShape).Copy(this);
}
Copy(e) {
super.Copy(e);
this.m_centroid.Copy(e.m_centroid);
this.m_count = e.m_count;
this.m_vertices = b2Vec2.MakeArray(this.m_count);
this.m_normals = b2Vec2.MakeArray(this.m_count);
for (let t = 0; t < this.m_count; ++t) {
this.m_vertices[t].Copy(e.m_vertices[t]);
this.m_normals[t].Copy(e.m_normals[t]);
}
return this;
}
GetChildCount() {
return 1;
}
Set(...e) {
if (typeof e[0][0] === "number") {
const t = e[0];
if (t.length % 2 !== 0) {
throw new Error;
}
return this._Set((e => ({
x: t[e * 2],
y: t[e * 2 + 1]
})), t.length / 2);
} else {
const t = e[0];
const o = e[1] || t.length;
return this._Set((e => t[e]), o);
}
}
_Set(e, t) {
if (t < 3) {
return this.SetAsBox(1, 1);
}
let o = t;
const s = [];
for (let t = 0; t < o; ++t) {
const o = e(t);
let n = true;
for (let e = 0; e < s.length; ++e) {
if (b2Vec2.DistanceSquaredVV(o, s[e]) < .5 * b2_linearSlop * (.5 * b2_linearSlop)) {
n = false;
break;
}
}
if (n) {
s.push(o);
}
}
o = s.length;
if (o < 3) {
return this.SetAsBox(1, 1);
}
let n = 0;
let b = s[0].x;
for (let e = 1; e < o; ++e) {
const t = s[e].x;
if (t > b || t === b && s[e].y < s[n].y) {
n = e;
b = t;
}
}
const h = [];
let c = 0;
let i = n;
for (;;) {
h[c] = i;
let e = 0;
for (let t = 1; t < o; ++t) {
if (e === i) {
e = t;
continue;
}
const o = b2Vec2.SubVV(s[e], s[h[c]], b2PolygonShape.Set_s_r);
const n = b2Vec2.SubVV(s[t], s[h[c]], b2PolygonShape.Set_s_v);
const b = b2Vec2.CrossVV(o, n);
if (b < 0) {
e = t;
}
if (b === 0 && n.LengthSquared() > o.LengthSquared()) {
e = t;
}
}
++c;
i = e;
if (e === n) {
break;
}
}
this.m_count = c;
this.m_vertices = b2Vec2.MakeArray(this.m_count);
this.m_normals = b2Vec2.MakeArray(this.m_count);
for (let e = 0; e < c; ++e) {
this.m_vertices[e].Copy(s[h[e]]);
}
for (let e = 0; e < c; ++e) {
const t = this.m_vertices[e];
const o = this.m_vertices[(e + 1) % c];
const s = b2Vec2.SubVV(o, t, b2Vec2.s_t0);
b2Vec2.CrossVOne(s, this.m_normals[e]).SelfNormalize();
}
b2PolygonShape.ComputeCentroid(this.m_vertices, c, this.m_centroid);
return this;
}
SetAsBox(e, t, o, s = 0) {
this.m_count = 4;
this.m_vertices = b2Vec2.MakeArray(this.m_count);
this.m_normals = b2Vec2.MakeArray(this.m_count);
this.m_vertices[0].Set(-e, -t);
this.m_vertices[1].Set(e, -t);
this.m_vertices[2].Set(e, t);
this.m_vertices[3].Set(-e, t);
this.m_normals[0].Set(0, -1);
this.m_normals[1].Set(1, 0);
this.m_normals[2].Set(0, 1);
this.m_normals[3].Set(-1, 0);
this.m_centroid.SetZero();
if (o) {
this.m_centroid.Copy(o);
const e = new b2Transform;
e.SetPosition(o);
e.SetRotationAngle(s);
for (let t = 0; t < this.m_count; ++t) {
b2Transform.MulXV(e, this.m_vertices[t], this.m_vertices[t]);
b2Rot.MulRV(e.q, this.m_normals[t], this.m_normals[t]);
}
}
return this;
}
TestPoint(e, t) {
const o = b2Transform.MulTXV(e, t, b2PolygonShape.TestPoint_s_pLocal);
for (let e = 0; e < this.m_count; ++e) {
const t = b2Vec2.DotVV(this.m_normals[e], b2Vec2.SubVV(o, this.m_vertices[e], b2Vec2.s_t0));
if (t > 0) {
return false;
}
}
return true;
}
ComputeDistance(e, t, o, s) {
const n = b2Transform.MulTXV(e, t, b2PolygonShape.ComputeDistance_s_pLocal);
let b = -b2_maxFloat;
const h = b2PolygonShape.ComputeDistance_s_normalForMaxDistance.Copy(n);
for (let e = 0; e < this.m_count; ++e) {
const t = b2Vec2.DotVV(this.m_normals[e], b2Vec2.SubVV(n, this.m_vertices[e], b2Vec2.s_t0));
if (t > b) {
b = t;
h.Copy(this.m_normals[e]);
}
}
if (b > 0) {
const t = b2PolygonShape.ComputeDistance_s_minDistance.Copy(h);
let s = b * b;
for (let e = 0; e < this.m_count; ++e) {
const o = b2Vec2.SubVV(n, this.m_vertices[e], b2PolygonShape.ComputeDistance_s_distance);
const b = o.LengthSquared();
if (s > b) {
t.Copy(o);
s = b;
}
}
b2Rot.MulRV(e.q, t, o);
o.Normalize();
return Math.sqrt(s);
} else {
b2Rot.MulRV(e.q, h, o);
return b;
}
}
RayCast(e, t, o, s) {
const n = b2Transform.MulTXV(o, t.p1, b2PolygonShape.RayCast_s_p1);
const b = b2Transform.MulTXV(o, t.p2, b2PolygonShape.RayCast_s_p2);
const h = b2Vec2.SubVV(b, n, b2PolygonShape.RayCast_s_d);
let c = 0, i = t.maxFraction;
let l = -1;
for (let e = 0; e < this.m_count; ++e) {
const t = b2Vec2.DotVV(this.m_normals[e], b2Vec2.SubVV(this.m_vertices[e], n, b2Vec2.s_t0));
const o = b2Vec2.DotVV(this.m_normals[e], h);
if (o === 0) {
if (t < 0) {
return false;
}
} else {
if (o < 0 && t < c * o) {
c = t / o;
l = e;
} else if (o > 0 && t < i * o) {
i = t / o;
}
}
if (i < c) {
return false;
}
}
if (l >= 0) {
e.fraction = c;
b2Rot.MulRV(o.q, this.m_normals[l], e.normal);
return true;
}
return false;
}
ComputeAABB(e, t, o) {
const s = b2Transform.MulXV(t, this.m_vertices[0], e.lowerBound);
const n = e.upperBound.Copy(s);
for (let e = 0; e < this.m_count; ++e) {
const o = b2Transform.MulXV(t, this.m_vertices[e], b2PolygonShape.ComputeAABB_s_v);
b2Vec2.MinV(o, s, s);
b2Vec2.MaxV(o, n, n);
}
const b = this.m_radius;
s.SelfSubXY(b, b);
n.SelfAddXY(b, b);
}
ComputeMass(e, t) {
const o = b2PolygonShape.ComputeMass_s_center.SetZero();
let s = 0;
let n = 0;
const b = b2PolygonShape.ComputeMass_s_s.Copy(this.m_vertices[0]);
const h = 1 / 3;
for (let e = 0; e < this.m_count; ++e) {
const t = b2Vec2.SubVV(this.m_vertices[e], b, b2PolygonShape.ComputeMass_s_e1);
const c = b2Vec2.SubVV(this.m_vertices[(e + 1) % this.m_count], b, b2PolygonShape.ComputeMass_s_e2);
const i = b2Vec2.CrossVV(t, c);
const l = .5 * i;
s += l;
o.SelfAdd(b2Vec2.MulSV(l * h, b2Vec2.AddVV(t, c, b2Vec2.s_t0), b2Vec2.s_t1));
const r = t.x;
const a = t.y;
const p = c.x;
const V = c.y;
const f = r * r + p * r + p * p;
const S = a * a + V * a + V * V;
n += .25 * h * i * (f + S);
}
e.mass = t * s;
o.SelfMul(1 / s);
b2Vec2.AddVV(o, b, e.center);
e.I = t * n;
e.I += e.mass * (b2Vec2.DotVV(e.center, e.center) - b2Vec2.DotVV(o, o));
}
Validate() {
for (let e = 0; e < this.m_count; ++e) {
const t = e;
const o = (e + 1) % this.m_count;
const s = this.m_vertices[t];
const n = b2Vec2.SubVV(this.m_vertices[o], s, b2PolygonShape.Validate_s_e);
for (let e = 0; e < this.m_count; ++e) {
if (e === t || e === o) {
continue;
}
const b = b2Vec2.SubVV(this.m_vertices[e], s, b2PolygonShape.Validate_s_v);
const h = b2Vec2.CrossVV(n, b);
if (h < 0) {
return false;
}
}
}
return true;
}
SetupDistanceProxy(e, t) {
e.m_vertices = this.m_vertices;
e.m_count = this.m_count;
e.m_radius = this.m_radius;
}
ComputeSubmergedArea(e, t, o, s) {
const n = b2Rot.MulTRV(o.q, e, b2PolygonShape.ComputeSubmergedArea_s_normalL);
const b = t - b2Vec2.DotVV(e, o.p);
const h = [];
let c = 0;
let i = -1;
let l = -1;
let r = false;
for (let e = 0; e < this.m_count; ++e) {
h[e] = b2Vec2.DotVV(n, this.m_vertices[e]) - b;
const t = h[e] < -b2_epsilon;
if (e > 0) {
if (t) {
if (!r) {
i = e - 1;
c++;
}
} else {
if (r) {
l = e - 1;
c++;
}
}
}
r = t;
}
switch (c) {
case 0:
if (r) {
const e = b2PolygonShape.ComputeSubmergedArea_s_md;
this.ComputeMass(e, 1);
b2Transform.MulXV(o, e.center, s);
return e.mass;
} else {
return 0;
}
case 1:
if (i === -1) {
i = this.m_count - 1;
} else {
l = this.m_count - 1;
}
break;
}
const a = (i + 1) % this.m_count;
const p = (l + 1) % this.m_count;
const V = (0 - h[i]) / (h[a] - h[i]);
const f = (0 - h[l]) / (h[p] - h[l]);
const S = b2PolygonShape.ComputeSubmergedArea_s_intoVec.Set(this.m_vertices[i].x * (1 - V) + this.m_vertices[a].x * V, this.m_vertices[i].y * (1 - V) + this.m_vertices[a].y * V);
const y = b2PolygonShape.ComputeSubmergedArea_s_outoVec.Set(this.m_vertices[l].x * (1 - f) + this.m_vertices[p].x * f, this.m_vertices[l].y * (1 - f) + this.m_vertices[p].y * f);
let g = 0;
const P = b2PolygonShape.ComputeSubmergedArea_s_center.SetZero();
let u = this.m_vertices[a];
let w;
let m = a;
while (m !== p) {
m = (m + 1) % this.m_count;
if (m === p) {
w = y;
} else {
w = this.m_vertices[m];
}
const e = .5 * ((u.x - S.x) * (w.y - S.y) - (u.y - S.y) * (w.x - S.x));
g += e;
P.x += e * (S.x + u.x + w.x) / 3;
P.y += e * (S.y + u.y + w.y) / 3;
u = w;
}
P.SelfMul(1 / g);
b2Transform.MulXV(o, P, s);
return g;
}
Dump(e) {
e(" const shape: b2PolygonShape = new b2PolygonShape();\n");
e(" const vs: b2Vec2[] = [];\n");
for (let t = 0; t < this.m_count; ++t) {
e(" vs[%d] = new b2Vec2(%.15f, %.15f);\n", t, this.m_vertices[t].x, this.m_vertices[t].y);
}
e(" shape.Set(vs, %d);\n", this.m_count);
}
static ComputeCentroid(e, t, o) {
const s = o;
s.SetZero();
let n = 0;
const b = b2PolygonShape.ComputeCentroid_s_s.Copy(e[0]);
const h = 1 / 3;
for (let o = 0; o < t; ++o) {
const c = b2Vec2.SubVV(e[0], b, b2PolygonShape.ComputeCentroid_s_p1);
const i = b2Vec2.SubVV(e[o], b, b2PolygonShape.ComputeCentroid_s_p2);
const l = b2Vec2.SubVV(e[(o + 1) % t], b, b2PolygonShape.ComputeCentroid_s_p3);
const r = b2Vec2.SubVV(i, c, b2PolygonShape.ComputeCentroid_s_e1);
const a = b2Vec2.SubVV(l, c, b2PolygonShape.ComputeCentroid_s_e2);
const p = b2Vec2.CrossVV(r, a);
const V = .5 * p;
n += V;
s.x += V * h * (c.x + i.x + l.x);
s.y += V * h * (c.y + i.y + l.y);
}
s.x = 1 / n * s.x + b.x;
s.y = 1 / n * s.y + b.y;
return s;
}
}
b2PolygonShape.Set_s_r = new b2Vec2;
b2PolygonShape.Set_s_v = new b2Vec2;
b2PolygonShape.TestPoint_s_pLocal = new b2Vec2;
b2PolygonShape.ComputeDistance_s_pLocal = new b2Vec2;
b2PolygonShape.ComputeDistance_s_normalForMaxDistance = new b2Vec2;
b2PolygonShape.ComputeDistance_s_minDistance = new b2Vec2;
b2PolygonShape.ComputeDistance_s_distance = new b2Vec2;
b2PolygonShape.RayCast_s_p1 = new b2Vec2;
b2PolygonShape.RayCast_s_p2 = new b2Vec2;
b2PolygonShape.RayCast_s_d = new b2Vec2;
b2PolygonShape.ComputeAABB_s_v = new b2Vec2;
b2PolygonShape.ComputeMass_s_center = new b2Vec2;
b2PolygonShape.ComputeMass_s_s = new b2Vec2;
b2PolygonShape.ComputeMass_s_e1 = new b2Vec2;
b2PolygonShape.ComputeMass_s_e2 = new b2Vec2;
b2PolygonShape.Validate_s_e = new b2Vec2;
b2PolygonShape.Validate_s_v = new b2Vec2;
b2PolygonShape.ComputeSubmergedArea_s_normalL = new b2Vec2;
b2PolygonShape.ComputeSubmergedArea_s_md = new b2MassData;
b2PolygonShape.ComputeSubmergedArea_s_intoVec = new b2Vec2;
b2PolygonShape.ComputeSubmergedArea_s_outoVec = new b2Vec2;
b2PolygonShape.ComputeSubmergedArea_s_center = new b2Vec2;
b2PolygonShape.ComputeCentroid_s_s = new b2Vec2;
b2PolygonShape.ComputeCentroid_s_p1 = new b2Vec2;
b2PolygonShape.ComputeCentroid_s_p2 = new b2Vec2;
b2PolygonShape.ComputeCentroid_s_p3 = new b2Vec2;
b2PolygonShape.ComputeCentroid_s_e1 = new b2Vec2;
b2PolygonShape.ComputeCentroid_s_e2 = new b2Vec2;