pixelscanjs
Version:
Simple pixel based physics engine only supporting static environments and ropes!
2,166 lines (1,800 loc) • 850 kB
JavaScript
const PixelScan = (function() {
class Vec2 {
x;
y;
constructor(x, y) {
this.x = x || 0;
this.y = y || 0;
}
static copy(vec) {
return new Vec2(vec.x, vec.y);
}
static fromAngle(angle) {
return new Vec2(Math.cos(angle), Math.sin(angle));
}
static set(x, y) {
return new Vec2(x, y);
}
copy(vec) {
this.x = vec.x;
this.y = vec.y;
return this;
}
set(x, y) {
this.x = x;
this.y = y;
return this;
}
add(vec) {
this.x += vec.x;
this.y += vec.y;
return this;
}
subtract(vec) {
this.x -= vec.x;
this.y -= vec.y;
return this;
}
sub = this.subtract;
round() {
this.x = Math.round(this.x);
this.y = Math.round(this.y);
return this;
}
multiply(mat) {
if (Number.isNaN(mat)) {
const x = this.x * mat.v0 + this.y * mat.v3 + mat.v6;
const y = this.x * mat.v1 + this.y * mat.v4 + mat.v7;
this.x = x;
this.y = y;
} else {
this.x *= mat;
this.y *= mat;
}
return this;
}
mul = this.multiply;
magnitude() {
return Math.sqrt(this.x * this.x + this.y * this.y);
}
length = this.magnitude;
magnitudeSquared() {
return this.x * this.x + this.y * this.y;
}
square() {
this.x = this.x * this.x;
this.y = this.y * this.y;
return this;
}
squareRoot() {
this.x = Math.sqrt(this.x);
this.y = Math.sqrt(this.y);
return this;
}
sqrt = this.squareRoot;
rotate(radians) {
const x = this.x;
const y = this.y;
this.x = x * Math.cos(radians) - y * Math.sin(radians);
this.y = y * Math.cos(radians) + x * Math.sin(radians);
return this;
}
orthogonal() {
const x = this.x;
this.x = -this.y;
this.y = x;
return this;
}
ortho = this.orthogonal;
normalize() {
const length = this.magnitude();
if (length === 0) {
return this;
}
this.x /= length;
this.y /= length;
return this;
}
norm = this.normalize;
distance(vec) {
const dx = vec.x - this.x;
const dy = vec.y - this.y;
return Math.sqrt(dx * dx + dy * dy);
}
dist = this.distance;
distanceSquared(vec) {
const dx = vec.x - this.x;
const dy = vec.y - this.y;
return dx * dx + dy * dy;
}
distSquared = this.distanceSquared;
negate() {
this.x = -this.x;
this.y = -this.y;
return this;
}
atan2() {
return Math.atan2(this.y, this.x);
}
dot(vec) {
return this.x * vec.x + this.y * vec.y;
}
cross(vec) {
return this.x * vec.y - vec.x * this.y;
}
// returns a number between -1 and 1,
// where 0 represents the two vectors are the same direction,
// 0.5 represents the perpendicular normal,
// and -0.5 is the inverted normal
// valid for all vectors where the positive angle between them is < 180, not equal
crossDot(vec) {
const sign = Math.sign(this.cross(vec)) || 1;
return (0.5 - this.dot(vec) / 2.0) * sign;
}
projectOnto(vec) {
tempVec.copy(vec);
tempVec.normalize();
const top = this.dot(tempVec);
const bottom = tempVec.dot(tempVec);
this.copy(tempVec);
this.multiply(top / bottom);
return this;
}
}
tempVec = new Vec2();
class Mat3 {
v0;
v1;
v2;
v3;
v4;
v5;
v6;
v7;
v8;
// NOTE: libgdx's indices are transposed
constructor() {
this.v0 = 1;
this.v1 = 0;
this.v2 = 0;
this.v3 = 0;
this.v4 = 1;
this.v5 = 0;
this.v6 = 0;
this.v7 = 0;
this.v8 = 1;
}
copy(mat) {
this.v0 = mat.v0;
this.v1 = mat.v1;
this.v2 = mat.v2;
this.v3 = mat.v3;
this.v4 = mat.v4;
this.v5 = mat.v5;
this.v6 = mat.v6;
this.v7 = mat.v7;
this.v8 = mat.v8;
return this;
}
determinant() {
return this.v0 * this.v4 * this.v8 + this.v1 * this.v5 * this.v6 + this.v2 * this.v3 * this.v7 - this.v0
* this.v5 * this.v7 - this.v1 * this.v3 * this.v8 - this.v2 * this.v4 * this.v6;
}
invert() {
const det = this.determinant();
if (det === 0) {
return null;
}
const inv = 1.0 / det;
tempMat.v0 = this.v4 * this.v8 - this.v7 * this.v5;
tempMat.v3 = this.v6 * this.v5 - this.v3 * this.v8;
tempMat.v6 = this.v3 * this.v7 - this.v6 * this.v4;
tempMat.v1 = this.v7 * this.v2 - this.v1 * this.v8;
tempMat.v4 = this.v0 * this.v8 - this.v6 * this.v2;
tempMat.v7 = this.v6 * this.v1 - this.v0 * this.v7;
tempMat.v2 = this.v1 * this.v5 - this.v4 * this.v2;
tempMat.v5 = this.v3 * this.v2 - this.v0 * this.v5;
tempMat.v8 = this.v0 * this.v4 - this.v3 * this.v1;
this.v0 = inv * tempMat.v0;
this.v3 = inv * tempMat.v3;
this.v6 = inv * tempMat.v6;
this.v1 = inv * tempMat.v1;
this.v4 = inv * tempMat.v4;
this.v7 = inv * tempMat.v7;
this.v2 = inv * tempMat.v2;
this.v5 = inv * tempMat.v5;
this.v8 = inv * tempMat.v8;
return this;
}
multiply(mat) {
const v00 = this.v0 * mat.v0 + this.v3 * mat.v1 + this.v6 * mat.v2;
const v01 = this.v0 * mat.v3 + this.v3 * mat.v4 + this.v6 * mat.v5;
const v02 = this.v0 * mat.v6 + this.v3 * mat.v7 + this.v6 * mat.v8;
const v10 = this.v1 * mat.v0 + this.v4 * mat.v1 + this.v7 * mat.v2;
const v11 = this.v1 * mat.v3 + this.v4 * mat.v4 + this.v7 * mat.v5;
const v12 = this.v1 * mat.v6 + this.v4 * mat.v7 + this.v7 * mat.v8;
const v20 = this.v2 * mat.v0 + this.v5 * mat.v1 + this.v8 * mat.v2;
const v21 = this.v2 * mat.v3 + this.v5 * mat.v4 + this.v8 * mat.v5;
const v22 = this.v2 * mat.v6 + this.v5 * mat.v7 + this.v8 * mat.v8;
this.v0 = v00;
this.v1 = v10;
this.v2 = v20;
this.v3 = v01;
this.v4 = v11;
this.v5 = v21;
this.v6 = v02;
this.v7 = v12;
this.v8 = v22;
return this;
}
leftMultiply(mat) {
const v00 = mat.v0 * this.v0 + mat.v3 * this.v1 + mat.v6 * this.v2;
const v01 = mat.v0 * this.v3 + mat.v3 * this.v4 + mat.v6 * this.v5;
const v02 = mat.v0 * this.v6 + mat.v3 * this.v7 + mat.v6 * this.v8;
const v10 = mat.v1 * this.v0 + mat.v4 * this.v1 + mat.v7 * this.v2;
const v11 = mat.v1 * this.v3 + mat.v4 * this.v4 + mat.v7 * this.v5;
const v12 = mat.v1 * this.v6 + mat.v4 * this.v7 + mat.v7 * this.v8;
const v20 = mat.v2 * this.v0 + mat.v5 * this.v1 + mat.v8 * this.v2;
const v21 = mat.v2 * this.v3 + mat.v5 * this.v4 + mat.v8 * this.v5;
const v22 = mat.v2 * this.v6 + mat.v5 * this.v7 + mat.v8 * this.v8;
this.v0 = v00;
this.v1 = v10;
this.v2 = v20;
this.v3 = v01;
this.v4 = v11;
this.v5 = v21;
this.v6 = v02;
this.v7 = v12;
this.v8 = v22;
return this;
}
setToTranslation(vec) {
this.v0 = 1;
this.v1 = 0;
this.v2 = 0;
this.v3 = 0;
this.v4 = 1;
this.v5 = 0;
this.v6 = vec.x;
this.v7 = vec.y;
this.v8 = 1;
return this;
}
getTranslation(out) {
out.x = this.v6;
out.y = this.v7;
return out;
}
setTranslation(vec) {
const inverseVec = this.getTranslation(tempVec).negate();
const inverse = tempMat.setToTranslation(inverseVec);
// translation * (inverse * self)
this.leftMultiply(inverse);
const correct = tempMat.setToTranslation(vec);
return this.leftMultiply(correct)
}
translate(vec) {
tempMat.setToTranslation(vec);
return this.multiply(tempMat);
}
setToRotation(radians) {
const cos = Math.cos(radians);
const sin = Math.sin(radians);
this.v0 = cos;
this.v1 = sin;
this.v2 = 0;
this.v3 = -sin;
this.v4 = cos;
this.v5 = 0;
this.v6 = 0;
this.v7 = 0;
this.v8 = 1;
return this;
}
getRotation() {
return Math.atan2(this.v1, this.v0);
}
setRotation(radians) {
const inverse = tempMat.setToRotation(-this.getRotation());
this.multiply(inverse);
const correct = tempMat.setToRotation(radians);
return this.multiply(correct);
}
rotate(radians) {
tempMat.setToRotation(radians);
return this.multiply(tempMat);
}
}
tempMat = new Mat3();
class Hash {
static buffer = new ArrayBuffer(4);
static byteBuffer = new Uint8Array(Hash.buffer);
static intBuffer = new Int32Array(Hash.buffer);
static integerHash(string) {
let value = 0;
let index = 0;
while (index < string.length) {
for (let i = 0; i < 4; i++) {
if (index < string.length) {
Hash.byteBuffer[i] = string.charCodeAt(index);
index++;
} else {
Hash.byteBuffer[i] = 0;
}
}
}
value ^= Hash.intBuffer[0];
return value;
}
}
class AABB {
x;
y;
width;
height;
constructor(x, y, width, height) {
this.x = x || 0;
this.y = y || 0;
this.width = width || 0;
this.height = height || 0;
}
static copy(aabb) {
return new AABB(aabb.x, aabb.y, aabb.width, aabb.height);
}
copy(aabb) {
this.x = aabb.x;
this.y = aabb.y;
this.width = aabb.width;
this.height = aabb.height;
return this;
}
round() {
this.x = Math.round(this.x);
this.y = Math.round(this.y);
return this;
}
contains(x, y) {
return (x >= this.x) && (y >= this.y) && (x - this.x < this.width) && (y - this.y < this.height);
}
}
class Camera {
static position = new Vec2();
static aabb = new AABB();
static scale = new Vec2(1, 1);
static nextPosition = new Vec2();
static nextScale = new Vec2(1, 1);
static containers = [];
static positionSpeedStrength = 0.5;
static scaleSpeedStrength = 0.05;
static shakeDuration = 15;
static shakeIntensity = 10;
static shakeFalloff = 0.75;
static remainingShakeDuration = 0;
static shakeSeedHorizontal = 0;
static shakeSeedVertical = 0;
static cameraHeight = 1080;
static setPosition(position) {
Camera.nextPosition.copy(position).round();
}
static setPositionImmediate(position) {
Camera.nextPosition.copy(position).round();
Camera.position.copy(position).round();
}
static setScale(scale) {
Camera.nextScale.copy(scale);
}
static setScaleImmediate(scale) {
Camera.nextScale.copy(scale);
Camera.scale.copy(scale);
}
static shake(intensity) {
Camera.shakeIntensity = intensity || 15;
Camera.remainingShakeDuration = Camera.shakeDuration;
Camera.shakeSeedHorizontal = Math.random();
Camera.shakeSeedVertical = Math.random();
}
static setSpeedProperties(strength) {
this.positionSpeedStrength = strength;
}
static setScaleProperties(strength) {
this.scaleAccel = accel;
this.minimumScaleSpeed = minumumSpeed;
this.maximumScaleSpeed = maximumSpeed;
this.maximumScaleDistance = maximumScale;
}
static setShakeProperties(duration, falloff) {
Camera.shakeDuration = duration;
Camera.shakeFalloff = falloff;
}
static setCameraHeight(height) {
Camera.cameraHeight = height;
}
static update() {
let shakeX = 0;
let shakeY = 0;
if (Camera.remainingShakeDuration > 0) {
Camera.remainingShakeDuration--;
// its okay if progress goes past 1 because it wraps around
const progress = (Camera.shakeDuration - Camera.remainingShakeDuration) / 30;
const shake = Camera.shakeIntensity - Camera.shakeIntensity * progress * Camera.shakeFalloff;
shakeX = PerlinNoise.getNoise(Camera.shakeSeedHorizontal, progress) * shake - shake / 2;
shakeY = PerlinNoise.getNoise(Camera.shakeSeedVertical, progress) * shake - shake / 2;
}
const positionDeltaX = Camera.nextPosition.x - Camera.position.x;
const positionDeltaY = Camera.nextPosition.y - Camera.position.y;
const positionDeltaLength = Math.sqrt(positionDeltaX * positionDeltaX + positionDeltaY * positionDeltaY);
if (positionDeltaLength <= 0.5) {
Camera.position.copy(Camera.nextPosition);
} else {
Camera.position.x += positionDeltaX * Camera.positionSpeedStrength;
Camera.position.y += positionDeltaY * Camera.positionSpeedStrength;
}
const scaleDelta = Camera.nextScale.x - Camera.scale.x;
if (Math.abs(scaleDelta) <= 0.01) {
Camera.scale.copy(Camera.nextScale);
} else {
Camera.scale.x += scaleDelta * Camera.scaleSpeedStrength;
Camera.scale.y = Camera.scale.x;
}
const width = window.innerWidth;
const height = window.innerHeight;
const heightScale = height / Camera.cameraHeight;
const scaleX = Camera.scale.x * heightScale;
const scaleY = Camera.scale.y * heightScale;
Camera.aabb.x = Camera.position.x - width / 2 / scaleX + shakeX;
Camera.aabb.y = Camera.position.y - height / 2 / scaleY + shakeY;
Camera.aabb.width = width / scaleX;
Camera.aabb.height = height / scaleY;
const x = Camera.aabb.width / 2 * scaleX - Camera.position.x * scaleX;
const y = Camera.aabb.height / 2 * scaleY - Camera.position.y * scaleY;
for (let i = 0; i < Camera.containers.length; i++) {
Camera.containers[i].position.x = x + shakeX;
Camera.containers[i].position.y = y + shakeY;
Camera.containers[i].scale.x = scaleX;
Camera.containers[i].scale.y = scaleY;
}
}
static addContainer(container) {
Camera.containers.push(container);
}
}
class CPUTracker extends PIXI.Text {
history;
nextIndex;
startTime;
endTime;
constructor(color) {
super('CPU: 0.0%', {fill: color === undefined ? 0xffffff : color, fontSize: 16});
this.history = new Array(60).fill(0);
this.nextIndex = 0;
this.startTime = 0;
this.endTime = 0;
}
beginFrame(time) {
if (this.startTime > 0 && this.endTime > 0) {
const totalTime = time - this.startTime;
const frameTime = this.endTime - this.startTime;
this.history[this.nextIndex] = Math.max(frameTime / totalTime, Number.MIN_VALUE);
this.nextIndex = (this.nextIndex + 1) % this.history.length;
let total = 0;
let count = 0;
for (let i = 0; i < this.history.length; i++) {
if (this.history[i] === 0) {
continue;
}
total += this.history[i];
count++;
}
const cpu = total / count * 100;
const integer = Math.floor(cpu);
const remainder = Math.floor((cpu - integer) * 100);
this.text = 'CPU: ' + integer + '.' + remainder + '%';
}
this.startTime = time;
}
endFrame(time) {
this.endTime = time;
}
}
class DebugCanvas extends PIXI.Graphics {
constructor() {
super();
}
drawRect(x, y, width, height, color, alpha) {
color = color || 0;
alpha = alpha === undefined ? 1 : alpha;
this.lineStyle(0);
this.beginFill(color, alpha);
super.drawRect(x, y, width, height);
this.endFill();
}
drawLine(x1, y1, x2, y2, color, alpha) {
color = color || 0;
alpha = alpha === undefined ? 1 : alpha;
this.lineStyle(1, color, alpha);
this.moveTo(x1, y1);
this.lineTo(x2, y2);
this.closePath();
}
render(renderer) {
super.render(renderer);
this.clear();
}
}
class FPSTracker extends PIXI.Text {
history;
nextIndex;
constructor(color) {
super('FPS: 0.0', {fill: color === undefined ? 0xffffff : color, fontSize: 16});
this.history = new Array(60).fill(0);
this.nextIndex = 0;
}
getFPS() {
let startIndex = this.nextIndex;
if (this.history[startIndex] === 0) {
startIndex = 0;
}
const firstTime = this.history[startIndex];
if (startIndex === 0 && firstTime === 0) {
return 0.0;
}
const lastIndex = (this.nextIndex + this.history.length - 1) % this.history.length;
const lastTime = this.history[lastIndex];
const deltaTime = lastTime - firstTime;
const deltaFrames = (lastIndex - startIndex + this.history.length) % this.history.length;
if (deltaTime === 0) {
return 0;
}
return deltaFrames / deltaTime * 1000;
}
tick(time) {
this.history[this.nextIndex] = time;
this.nextIndex = (this.nextIndex + 1) % this.history.length;
const fps = this.getFPS();
const integer = Math.floor(fps);
const remainder = Math.floor((fps - integer) * 100);
this.text = 'FPS: ' + integer + '.' + remainder;
}
}
class FramedSprite extends PIXI.Sprite {
textures;
animations;
currentName;
currentFrame;
// left to right top to bottom sprite sheet
constructor(texture, width, height, columns, count) {
super(null);
this.textures = [];
this.textures.length = count;
for (let i = 0; i < count; i++) {
const row = Math.floor(i / columns);
const column = i % columns;
const x = column * width;
const y = row * height;
this.textures[i] = new PIXI.Texture(texture, new PIXI.Rectangle(x, y, width, height));
}
this.texture = this.textures[0];
this.animations = {};
this.currentName = undefined;
this.currentFrame = 0;
}
addAnimation(name, start, count) {
const animation = {
start: start,
count: count,
linked: {},
};
this.animations[name] = animation;
}
gotoAnimation(name, frame) {
if (frame !== undefined) {
this.currentFrame = frame;
} else if (this.currentName !== name) {
// if were on a different animation check if we need to reset the frame
if (!this.animations[this.currentName] || !this.animations[this.currentName].linked[name]) {
this.currentFrame = 0;
}
}
this.currentName = name;
if (this.animations[this.currentName]) {
this.currentFrame = this.currentFrame % this.animations[this.currentName].count;
} else {
this.currentFrame = this.currentFrame
}
this.updateFrame();
}
stepAnimation(name, frames, loop) {
if (Number.isNaN(name)) {
loop = frames;
frames = name;
name = undefined;
}
frames = frames || 1;
if (loop === undefined) {
loop = true;
}
if (this.currentName !== name) {
if (!this.animations[this.currentName] || !this.animations[this.currentName].linked[name]) {
this.currentFrame = 0;
}
}
this.currentName = name;
if (this.animations[this.currentName]) {
if (loop) {
this.currentFrame = (this.currentFrame + frames) % this.animations[this.currentName].count;
} else {
this.currentFrame = Math.min(this.currentFrame + frames, this.animations[this.currentName].count - 1);
}
} else {
if (loop) {
this.currentFrame = (this.currentFrame + frames) % this.textures.length;
} else {
this.currentFrame = Math.min(this.currentFrame + frames, this.textures.length - 1);
}
}
this.updateFrame();
}
// adds a linked animation so the animation will pick up from where it left off
linkAnimations(name, linkedName) {
this.animations[name].linked[linkedName] = true;
this.animations[linkedName].linked[name] = true;
}
getFrame() {
return this.currentFrame;
}
updateFrame() {
if (this.animations[this.currentName]) {
this.texture = this.textures[Math.floor(this.currentFrame + this.animations[this.currentName].start)];
} else {
this.texture = this.textures[Math.floor(this.currentFrame)];
}
}
}
class ParallaxSprite extends PIXI.Sprite {
aabb;
constructor(texture, aabb) {
super(texture);
this.aabb = aabb;
}
update(cameraAABB) {
const cameraCenterX = cameraAABB.x + cameraAABB.width / 2;
const cameraCenterY = cameraAABB.y + cameraAABB.height / 2;
const minX = this.aabb.x + cameraAABB.width / 2;
const minY = this.aabb.y + cameraAABB.height / 2;
const maxX = this.aabb.x + this.aabb.width - cameraAABB.width / 2;
const maxY = this.aabb.y + this.aabb.height - cameraAABB.height / 2;
const progressX = Math.min(Math.max((cameraCenterX - minX) / (maxX - minX), 0), 1);
const progressY = Math.min(Math.max((cameraCenterY - minY) / (maxY - minY), 0), 1);
const spriteMaxDeltaX = this.width - cameraAABB.width;
const spriteMaxDeltaY = this.height - cameraAABB.height;
this.position.x = -spriteMaxDeltaX * progressX;
this.position.y = -spriteMaxDeltaY * progressY;
}
}
class Input {
static KEY_0 = '0';
static KEY_1 = '1';
static KEY_2 = '2';
static KEY_3 = '3';
static KEY_4 = '4';
static KEY_5 = '5';
static KEY_6 = '6';
static KEY_7 = '7';
static KEY_8 = '8';
static KEY_9 = '9';
static KEY_A = 'a';
static KEY_B = 'b';
static KEY_C = 'c';
static KEY_D = 'd';
static KEY_E = 'e';
static KEY_F = 'f';
static KEY_G = 'g';
static KEY_H = 'h';
static KEY_I = 'i';
static KEY_J = 'j';
static KEY_K = 'k';
static KEY_L = 'l';
static KEY_M = 'm';
static KEY_N = 'n';
static KEY_O = 'o';
static KEY_P = 'p';
static KEY_Q = 'q';
static KEY_R = 'r';
static KEY_S = 's';
static KEY_T = 't';
static KEY_U = 'u';
static KEY_V = 'v';
static KEY_W = 'w';
static KEY_X = 'x';
static KEY_Y = 'y';
static KEY_Z = 'z';
static KEY_ESCAPE = 'escape';
static KEY_SHIFT = 'shift';
static KEY_SPACE = ' ';
static keys = {};
static mouseDownLeft = false;
static mouseDownRight = false;
static mousePosition = null;
}
window.addEventListener('load', () => {
Input.mousePosition = new Vec2();
window.addEventListener('keydown', event => {
if (!event.key) {
return true;
}
Input.keys[event.key.toLowerCase()] = true;
return true;
}, true);
window.addEventListener('keyup', event => {
if (!event.key) {
return true;
}
delete Input.keys[event.key.toLowerCase()];
return true;
}, true);
window.addEventListener('mousedown', event => {
if (event.button === 0) {
Input.mouseDownLeft = true;
}
if (event.button === 2) {
Input.mouseDownRight = true;
}
return true;
}, true);
window.addEventListener('mouseup', event => {
if (event.button === 0) {
Input.mouseDownLeft = false;
}
if (event.button === 2) {
Input.mouseDownRight = false;
}
return true;
}, true);
window.addEventListener('mousemove', event => {
Input.mousePosition[0] = event.clientX;
Input.mousePosition[1] = event.clientY;
return true;
}, true);
window.addEventListener('contextmenu', event => {
event.preventDefault();
return false;
}, true);
});
const box2d = {
b2_aabbExtension: 0.1,
};
box2d.DEBUG = false;
box2d.ENABLE_ASSERTS = box2d.DEBUG;
/**
* @export
* @const
* @type {number}
*/
box2d.b2_maxFloat = 1E+37; // FLT_MAX instead of Number.MAX_VALUE;
/**
* @export
* @const
* @type {number}
*/
box2d.b2_epsilon = 1E-5; // FLT_EPSILON instead of Number.MIN_VALUE;
/**
* This is used to fatten AABBs in the dynamic tree. This is
* used to predict the future position based on the current
* displacement.
* This is a dimensionless multiplier.
* @export
* @const
* @type {number}
*/
box2d.b2_aabbMultiplier = 2;
box2d.b2Assert = function(condition, opt_message, var_args) {
if (box2d.DEBUG) {
if (!condition) {
throw new Error();
}
//goog.asserts.assert(condition, opt_message, var_args);
}
}
/**
* @export
* @return {number}
* @param {number} n
*/
box2d.b2Abs = Math.abs;
/**
* @export
* @return {number}
* @param {number} a
* @param {number} b
*/
box2d.b2Min = Math.min;
/**
* @export
* @return {number}
* @param {number} a
* @param {number} b
*/
box2d.b2Max = Math.max;
/**
* @export
* @return {number}
* @param {number} a
* @param {number} lo
* @param {number} hi
*/
box2d.b2Clamp = function(a, lo, hi) {
return Math.min(Math.max(a, lo), hi);
}
/**
* @export
* @return {Array.<*>}
* @param {number=} length
* @param {function(number): *=} init
*/
box2d.b2MakeArray = function(length, init) {
length = (typeof(length) === 'number') ? (length) : (0);
var a = [];
if (typeof(init) === 'function') {
for (var i = 0; i < length; ++i) {
a.push(init(i));
}
} else {
for (var i = 0; i < length; ++i) {
a.push(null);
}
}
return a;
}
/**
* @export
* @return {Array.<number>}
* @param {number=} length
*/
box2d.b2MakeNumberArray = function(length) {
return box2d.b2MakeArray(length, function(i) {
return 0;
});
}
/**
* This is a growable LIFO stack with an initial capacity of N.
* If the stack size exceeds the initial capacity, the heap is
* used to increase the size of the stack.
* @export
* @constructor
* @param {number} N
*/
box2d.b2GrowableStack = function(N) {
this.m_stack = new Array(N);
}
/**
* @export
* @type {Array.<*>}
*/
box2d.b2GrowableStack.prototype.m_stack = null;
/**
* @export
* @type {number}
*/
box2d.b2GrowableStack.prototype.m_count = 0;
/**
* @export
* @return {box2d.b2GrowableStack}
*/
box2d.b2GrowableStack.prototype.Reset = function() {
this.m_count = 0;
return this;
}
/**
* @export
* @return {void}
* @param {*} element
*/
box2d.b2GrowableStack.prototype.Push = function(element) {
this.m_stack[this.m_count] = element;
++this.m_count;
}
/**
* @export
* @return {*}
*/
box2d.b2GrowableStack.prototype.Pop = function() {
if (box2d.ENABLE_ASSERTS) {
box2d.b2Assert(this.m_count > 0);
}
--this.m_count;
var element = this.m_stack[this.m_count];
this.m_stack[this.m_count] = null;
return element;
}
/**
* @export
* @return {number}
*/
box2d.b2GrowableStack.prototype.GetCount = function() {
return this.m_count;
}
/**
* A 2D column vector.
* @export
* @constructor
* @param {number=} x
* @param {number=} y
*/
box2d.b2Vec2 = function(x, y) {
this.x = x || 0.0;
this.y = y || 0.0;
//this.a = new Float32Array(2);
//this.a[0] = x || 0;
//this.a[1] = y || 0;
}
/**
* @export
* @type {number}
*/
box2d.b2Vec2.prototype.x = 0.0;
/**
* @export
* @type {number}
*/
box2d.b2Vec2.prototype.y = 0.0;
// /**
// * @type {Float32Array}
// */
// box2d.b2Vec2.prototype.a;
//
// box2d.b2Vec2.prototype.__defineGetter__('x', function () { return this.a[0]; });
// box2d.b2Vec2.prototype.__defineGetter__('y', function () { return this.a[1]; });
// box2d.b2Vec2.prototype.__defineSetter__('x', function (n) { this.a[0] = n; });
// box2d.b2Vec2.prototype.__defineSetter__('y', function (n) { this.a[1] = n; });
/**
* @export
* @const
* @type {box2d.b2Vec2}
*/
box2d.b2Vec2_zero = new box2d.b2Vec2();
/**
* @export
* @const
* @type {box2d.b2Vec2}
*/
box2d.b2Vec2.ZERO = new box2d.b2Vec2();
/**
* @export
* @const
* @type {box2d.b2Vec2}
*/
box2d.b2Vec2.UNITX = new box2d.b2Vec2(1.0, 0.0);
/**
* @export
* @const
* @type {box2d.b2Vec2}
*/
box2d.b2Vec2.UNITY = new box2d.b2Vec2(0.0, 1.0);
/**
* @export
* @type {box2d.b2Vec2}
*/
box2d.b2Vec2.s_t0 = new box2d.b2Vec2();
/**
* @export
* @type {box2d.b2Vec2}
*/
box2d.b2Vec2.s_t1 = new box2d.b2Vec2();
/**
* @export
* @type {box2d.b2Vec2}
*/
box2d.b2Vec2.s_t2 = new box2d.b2Vec2();
/**
* @export
* @type {box2d.b2Vec2}
*/
box2d.b2Vec2.s_t3 = new box2d.b2Vec2();
/**
* @export
* @return {Array.<box2d.b2Vec2>}
* @param {number=} length
*/
box2d.b2Vec2.MakeArray = function(length) {
return box2d.b2MakeArray(length, function(i) {
return new box2d.b2Vec2();
});
}
/**
* @export
* @return {box2d.b2Vec2}
*/
box2d.b2Vec2.prototype.Clone = function() {
return new box2d.b2Vec2(this.x, this.y);
}
/**
* Set this vector to all zeros.
* @export
* @return {box2d.b2Vec2}
*/
box2d.b2Vec2.prototype.SetZero = function() {
this.x = 0.0;
this.y = 0.0;
return this;
}
/**
* Set this vector to some specified coordinates.
* @export
* @return {box2d.b2Vec2}
* @param {number} x
* @param {number} y
*/
box2d.b2Vec2.prototype.Set = function(x, y) {
this.x = x;
this.y = y;
return this;
}
/**
* @export
* @return {box2d.b2Vec2}
* @param {box2d.b2Vec2} other
*/
box2d.b2Vec2.prototype.Copy = function(other) {
//if (box2d.ENABLE_ASSERTS) { box2d.b2Assert(this !== other); }
this.x = other.x;
this.y = other.y;
return this;
}
/**
* Add a vector to this vector.
* @export
* @return {box2d.b2Vec2}
* @param {box2d.b2Vec2} v
*/
box2d.b2Vec2.prototype.SelfAdd = function(v) {
this.x += v.x;
this.y += v.y;
return this;
}
/**
* @export
* @return {box2d.b2Vec2}
* @param {number} x
* @param {number} y
*/
box2d.b2Vec2.prototype.SelfAddXY = function(x, y) {
this.x += x;
this.y += y;
return this;
}
/**
* Subtract a vector from this vector.
* @export
* @return {box2d.b2Vec2}
* @param {box2d.b2Vec2} v
*/
box2d.b2Vec2.prototype.SelfSub = function(v) {
this.x -= v.x;
this.y -= v.y;
return this;
}
/**
* @export
* @return {box2d.b2Vec2}
* @param {number} x
* @param {number} y
*/
box2d.b2Vec2.prototype.SelfSubXY = function(x, y) {
this.x -= x;
this.y -= y;
return this;
}
/**
* Multiply this vector by a scalar.
* @export
* @return {box2d.b2Vec2}
* @param {number} s
*/
box2d.b2Vec2.prototype.SelfMul = function(s) {
this.x *= s;
this.y *= s;
return this;
}
/**
* this += s * v
* @export
* @return {box2d.b2Vec2}
* @param {number} s
* @param {box2d.b2Vec2} v
*/
box2d.b2Vec2.prototype.SelfMulAdd = function(s, v) {
this.x += s * v.x;
this.y += s * v.y;
return this;
}
/**
* this -= s * v
* @export
* @return {box2d.b2Vec2}
* @param {number} s
* @param {box2d.b2Vec2} v
*/
box2d.b2Vec2.prototype.SelfMulSub = function(s, v) {
this.x -= s * v.x;
this.y -= s * v.y;
return this;
}
/**
* @export
* @return {number}
* @param {box2d.b2Vec2} v
*/
box2d.b2Vec2.prototype.Dot = function(v) {
return this.x * v.x + this.y * v.y;
}
/**
* @export
* @return {number}
* @param {box2d.b2Vec2} v
*/
box2d.b2Vec2.prototype.Cross = function(v) {
return this.x * v.y - this.y * v.x;
}
/**
* Get the length of this vector (the norm).
* @export
* @return {number}
*/
box2d.b2Vec2.prototype.Length = function() {
var x = this.x,
y = this.y;
return Math.sqrt(x * x + y * y);
}
/**
* Get the length squared. For performance, use this instead of
* b2Vec2::Length (if possible).
* @export
* @return {number}
*/
box2d.b2Vec2.prototype.LengthSquared = function() {
var x = this.x,
y = this.y;
return (x * x + y * y);
}
/**
* Convert this vector into a unit vector. Returns the length.
* @export
* @return {number}
*/
box2d.b2Vec2.prototype.Normalize = function() {
var length = this.Length();
if (length >= box2d.b2_epsilon) {
var inv_length = 1.0 / length;
this.x *= inv_length;
this.y *= inv_length;
}
return length;
}
/**
* @export
* @return {box2d.b2Vec2}
*/
box2d.b2Vec2.prototype.SelfNormalize = function() {
this.Normalize();
return this;
}
/**
* @export
* @return {box2d.b2Vec2}
* @param {number} c
* @param {number} s
*/
box2d.b2Vec2.prototype.SelfRotate = function(c, s) {
var x = this.x,
y = this.y;
this.x = c * x - s * y;
this.y = s * x + c * y;
return this;
}
/**
* @export
* @return {box2d.b2Vec2}
* @param {number} radians
*/
box2d.b2Vec2.prototype.SelfRotateAngle = function(radians) {
return this.SelfRotate(Math.cos(radians), Math.sin(radians));
}
/**
* Does this vector contain finite coordinates?
* @export
* @return {boolean}
*/
box2d.b2Vec2.prototype.IsValid = function() {
return isFinite(this.x) && isFinite(this.y);
}
/**
* @export
* @return {box2d.b2Vec2}
* @param {box2d.b2Vec2} v
*/
box2d.b2Vec2.prototype.SelfMin = function(v) {
this.x = Math.min(this.x, v.x);
this.y = Math.min(this.y, v.y);
return this;
}
/**
* @export
* @return {box2d.b2Vec2}
* @param {box2d.b2Vec2} v
*/
box2d.b2Vec2.prototype.SelfMax = function(v) {
this.x = Math.max(this.x, v.x);
this.y = Math.max(this.y, v.y);
return this;
}
/**
* @export
* @return {box2d.b2Vec2}
*/
box2d.b2Vec2.prototype.SelfAbs = function() {
this.x = Math.abs(this.x);
this.y = Math.abs(this.y);
return this;
}
/**
* @export
* @return {box2d.b2Vec2}
*/
box2d.b2Vec2.prototype.SelfNeg = function() {
this.x = (-this.x);
this.y = (-this.y);
return this;
}
/**
* Get the skew vector such that dot(skew_vec, other) ===
* cross(vec, other)
* @export
* @return {box2d.b2Vec2}
*/
box2d.b2Vec2.prototype.SelfSkew = function() {
var x = this.x;
this.x = -this.y;
this.y = x;
return this;
}
/**
* @export
* @return {box2d.b2Vec2}
* @param {box2d.b2Vec2} v
* @param {box2d.b2Vec2} out
*/
box2d.b2Abs_V2 = function(v, out) {
out.x = Math.abs(v.x);
out.y = Math.abs(v.y);
return out;
}
/**
* @export
* @return {box2d.b2Vec2}
* @param {box2d.b2Vec2} a
* @param {box2d.b2Vec2} b
* @param {box2d.b2Vec2} out
*/
box2d.b2Min_V2_V2 = function(a, b, out) {
out.x = Math.min(a.x, b.x);
out.y = Math.min(a.y, b.y);
return out;
}
/**
* @export
* @return {box2d.b2Vec2}
* @param {box2d.b2Vec2} a
* @param {box2d.b2Vec2} b
* @param {box2d.b2Vec2} out
*/
box2d.b2Max_V2_V2 = function(a, b, out) {
out.x = Math.max(a.x, b.x);
out.y = Math.max(a.y, b.y);
return out;
}
/**
* @export
* @return {box2d.b2Vec2}
* @param {box2d.b2Vec2} v
* @param {box2d.b2Vec2} lo
* @param {box2d.b2Vec2} hi
* @param {box2d.b2Vec2} out
*/
box2d.b2Clamp_V2_V2_V2 = function(v, lo, hi, out) {
out.x = Math.min(Math.max(v.x, lo.x), hi.x);
out.y = Math.min(Math.max(v.y, lo.y), hi.y);
return out;
}
/**
* Perform the dot product on two vectors.
* a.x * b.x + a.y * b.y
* @export
* @return {number}
* @param {box2d.b2Vec2} a
* @param {box2d.b2Vec2} b
*/
box2d.b2Dot_V2_V2 = function(a, b) {
return a.x * b.x + a.y * b.y;
}
/**
* Perform the cross product on two vectors. In 2D this produces a scalar.
* a.x * b.y - a.y * b.x
* @export
* @return {number}
* @param {box2d.b2Vec2} a
* @param {box2d.b2Vec2} b
*/
box2d.b2Cross_V2_V2 = function(a, b) {
return a.x * b.y - a.y * b.x;
}
/**
* Perform the cross product on a vector and a scalar. In 2D
* this produces a vector.
* @export
* @return {box2d.b2Vec2}
* @param {box2d.b2Vec2} v
* @param {number} s
* @param {box2d.b2Vec2} out
*/
box2d.b2Cross_V2_S = function(v, s, out) {
var v_x = v.x;
out.x = s * v.y;
out.y = -s * v_x;
return out;
}
/**
* Perform the cross product on a scalar and a vector. In 2D
* this produces a vector.
* @export
* @return {box2d.b2Vec2}
* @param {number} s
* @param {box2d.b2Vec2} v
* @param {box2d.b2Vec2} out
*/
box2d.b2Cross_S_V2 = function(s, v, out) {
var v_x = v.x;
out.x = -s * v.y;
out.y = s * v_x;
return out;
}
/**
* Add two vectors component-wise.
* @export
* @return {box2d.b2Vec2}
* @param {box2d.b2Vec2} a
* @param {box2d.b2Vec2} b
* @param {box2d.b2Vec2} out
*/
box2d.b2Add_V2_V2 = function(a, b, out) {
out.x = a.x + b.x;
out.y = a.y + b.y;
return out;
}
/**
* Subtract two vectors component-wise.
* @export
* @return {box2d.b2Vec2}
* @param {box2d.b2Vec2} a
* @param {box2d.b2Vec2} b
* @param {box2d.b2Vec2} out
*/
box2d.b2Sub_V2_V2 = function(a, b, out) {
out.x = a.x - b.x;
out.y = a.y - b.y;
return out;
}
/**
* @export
* @return {box2d.b2Vec2}
* @param {box2d.b2Vec2} v
* @param {number} s
* @param {box2d.b2Vec2} out
*/
box2d.b2Add_V2_S = function(v, s, out) {
out.x = v.x + s;
out.y = v.y + s;
return out;
}
/**
* @export
* @return {box2d.b2Vec2}
* @param {box2d.b2Vec2} v
* @param {number} s
* @param {box2d.b2Vec2} out
*/
box2d.b2Sub_V2_S = function(v, s, out) {
out.x = v.x - s;
out.y = v.y - s;
return out;
}
/**
* @export
* @return {box2d.b2Vec2}
* @param {number} s
* @param {box2d.b2Vec2} v
* @param {box2d.b2Vec2} out
*/
box2d.b2Mul_S_V2 = function(s, v, out) {
out.x = v.x * s;
out.y = v.y * s;
return out;
}
/**
* @export
* @return {box2d.b2Vec2}
* @param {box2d.b2Vec2} v
* @param {number} s
* @param {box2d.b2Vec2} out
*/
box2d.b2Mul_V2_S = function(v, s, out) {
out.x = v.x * s;
out.y = v.y * s;
return out;
}
/**
* @export
* @return {box2d.b2Vec2}
* @param {box2d.b2Vec2} v
* @param {number} s
* @param {box2d.b2Vec2} out
*/
box2d.b2Div_V2_S = function(v, s, out) {
out.x = v.x / s;
out.y = v.y / s;
return out;
}
/**
* out = a + (s * b)
* @export
* @return {box2d.b2Vec2}
* @param {box2d.b2Vec2} a
* @param {number} s
* @param {box2d.b2Vec2} b
* @param {box2d.b2Vec2} out
*/
box2d.b2AddMul_V2_S_V2 = function(a, s, b, out) {
out.x = a.x + (s * b.x);
out.y = a.y + (s * b.y);
return out;
}
/**
* out = a - (s * b)
* @export
* @return {box2d.b2Vec2}
* @param {box2d.b2Vec2} a
* @param {number} s
* @param {box2d.b2Vec2} b
* @param {box2d.b2Vec2} out
*/
box2d.b2SubMul_V2_S_V2 = function(a, s, b, out) {
out.x = a.x - (s * b.x);
out.y = a.y - (s * b.y);
return out;
}
/**
* out = a + b2Cross(s, v)
* @export
* @return {box2d.b2Vec2}
* @param {box2d.b2Vec2} a
* @param {number} s
* @param {box2d.b2Vec2} v
* @param {box2d.b2Vec2} out
*/
box2d.b2AddCross_V2_S_V2 = function(a, s, v, out) {
var v_x = v.x;
out.x = a.x - (s * v.y);
out.y = a.y + (s * v_x);
return out;
}
/**
* Get the center of two vectors.
* @export
* @return {box2d.b2Vec2}
* @param {box2d.b2Vec2} a
* @param {box2d.b2Vec2} b
* @param {box2d.b2Vec2} out
*/
box2d.b2Mid_V2_V2 = function(a, b, out) {
out.x = (a.x + b.x) * 0.5;
out.y = (a.y + b.y) * 0.5;
return out;
}
/**
* Get the extent of two vectors (half-widths).
* @export
* @return {box2d.b2Vec2}
* @param {box2d.b2Vec2} a
* @param {box2d.b2Vec2} b
* @param {box2d.b2Vec2} out
*/
box2d.b2Ext_V2_V2 = function(a, b, out) {
out.x = (b.x - a.x) * 0.5;
out.y = (b.y - a.y) * 0.5;
return out;
}
/**
* @export
* @return {number}
* @param {box2d.b2Vec2} a
* @param {box2d.b2Vec2} b
*/
box2d.b2Distance = function(a, b) {
var c_x = a.x - b.x;
var c_y = a.y - b.y;
return Math.sqrt(c_x * c_x + c_y * c_y);
}
/**
* @export
* @return {number}
* @param {box2d.b2Vec2} a
* @param {box2d.b2Vec2} b
*/
box2d.b2DistanceSquared = function(a, b) {
var c_x = a.x - b.x;
var c_y = a.y - b.y;
return (c_x * c_x + c_y * c_y);
}
/**
* Ray-cast input data. The ray extends from p1 to p1 +
* maxFraction * (p2 - p1).
* @export
* @constructor
*/
box2d.b2RayCastInput = function() {
this.p1 = new box2d.b2Vec2();
this.p2 = new box2d.b2Vec2();
this.maxFraction = 1;
}
/**
* @export
* @type {box2d.b2Vec2}
*/
box2d.b2RayCastInput.prototype.p1 = null;
/**
* @export
* @type {box2d.b2Vec2}
*/
box2d.b2RayCastInput.prototype.p2 = null;
/**
* @export
* @type {number}
*/
box2d.b2RayCastInput.prototype.maxFraction = 1;
/**
* @export
* @return {box2d.b2RayCastInput}
* @param {box2d.b2RayCastInput} o
*/
box2d.b2RayCastInput.prototype.Copy = function(o) {
this.p1.Copy(o.p1);
this.p2.Copy(o.p2);
this.maxFraction = o.maxFraction;
return this;
}
/**
* Ray-cast output data. The ray hits at p1 + fraction * (p2 -
* p1), where p1 and p2 come from box2d.b2RayCastInput.
* @export
* @constructor
*/
box2d.b2RayCastOutput = function() {
this.normal = new box2d.b2Vec2();
this.fraction = 0;
};
/**
* @export
* @type {box2d.b2Vec2}
*/
box2d.b2RayCastOutput.prototype.normal = null;
/**
* @export
* @type {number}
*/
box2d.b2RayCastOutput.prototype.fraction = 0;
/**
* @export
* @return {box2d.b2RayCastOutput}
* @param {box2d.b2RayCastOutput} o
*/
box2d.b2RayCastOutput.prototype.Copy = function(o) {
this.normal.Copy(o.normal);
this.fraction = o.fraction;
return this;
}
/**
* An axis aligned bounding box.
* @export
* @constructor
*/
box2d.b2AABB = function() {
this.lowerBound = new box2d.b2Vec2();
this.upperBound = new box2d.b2Vec2();
this.m_out_center = new box2d.b2Vec2();
this.m_out_extent = new box2d.b2Vec2();
};
/**
* @export
* @type {box2d.b2Vec2}
*/
box2d.b2AABB.prototype.lowerBound = null; ///< the lower vertex
/**
* @export
* @type {box2d.b2Vec2}
*/
box2d.b2AABB.prototype.upperBound = null; ///< the upper vertex
/**
* @export
* @type {box2d.b2Vec2}
*/
box2d.b2AABB.prototype.m_out_center = null; // access using GetCenter()
/**
* @export
* @type {box2d.b2Vec2}
*/
box2d.b2AABB.prototype.m_out_extent = null; // access using GetExtents()
/**
* @export
* @return {box2d.b2AABB}
*/
box2d.b2AABB.prototype.Clone = function() {
return new box2d.b2AABB().Copy(this);
}
/**
* @export
* @return {box2d.b2AABB}
* @param {box2d.b2AABB} o
*/
box2d.b2AABB.prototype.Copy = function(o) {
this.lowerBound.Copy(o.lowerBound);
this.upperBound.Copy(o.upperBound);
return this;
}
/**
* Verify that the bounds are sorted.
* @export
* @return {boolean}
*/
box2d.b2AABB.prototype.IsValid = function() {
var d_x = this.upperBound.x - this.lowerBound.x;
var d_y = this.upperBound.y - this.lowerBound.y;
var valid = d_x >= 0 && d_y >= 0;
valid = valid && this.lowerBound.IsValid() && this.upperBound.IsValid();
return valid;
}
/**
* Get the center of the AABB.
* @export
* @return {box2d.b2Vec2}
*/
box2d.b2AABB.prototype.GetCenter = function() {
return box2d.b2Mid_V2_V2(this.lowerBound, this.upperBound, this.m_out_center);
}
/**
* Get the extents of the AABB (half-widths).
* @export
* @return {box2d.b2Vec2}
*/
box2d.b2AABB.prototype.GetExtents = function() {
return box2d.b2Ext_V2_V2(this.lowerBound, this.upperBound, this.m_out_extent);
}
/**
* Get the perimeter length
* @export
* @return {number}
*/
box2d.b2AABB.prototype.GetPerimeter = function() {
var wx = this.upperBound.x - this.lowerBound.x;
var wy = this.upperBound.y - this.lowerBound.y;
return 2 * (wx + wy);
}
/**
* @return {box2d.b2AABB}
* @param {box2d.b2AABB} a0
* @param {box2d.b2AABB=} a1
*/
box2d.b2AABB.prototype.Combine = function(a0, a1) {
switch (arguments.length) {
case 1:
return this.Combine1(a0);
case 2:
return this.Combine2(a0, a1 || new box2d.b2AABB());
default:
throw new Error();
}
}
/**
* Combine an AABB into this one.
* @export
* @return {box2d.b2AABB}
* @param {box2d.b2AABB} aabb
*/
box2d.b2AABB.prototype.Combine1 = function(aabb) {
this.lowerBound.x = box2d.b2Min(this.lowerBound.x, aabb.lowerBound.x);
this.lowerBound.y = box2d.b2Min(this.lowerBound.y, aabb.lowerBound.y);
this.upperBound.x = box2d.b2Max(this.upperBound.x, aabb.upperBound.x);
this.upperBound.y = box2d.b2Max(this.upperBound.y, aabb.upperBound.y);
return this;
}
/**
* Combine two AABBs into this one.
* @export
* @return {box2d.b2AABB}
* @param {box2d.b2AABB} aabb1
* @param {box2d.b2AABB} aabb2
*/
box2d.b2AABB.prototype.Combine2 = function(aabb1, aabb2) {
this.lowerBound.x = box2d.b2Min(aabb1.lowerBound.x, aabb2.lowerBound.x);
this.lowerBound.y = box2d.b2Min(aabb1.lowerBound.y, aabb2.lowerBound.y);
this.upperBound.x = box2d.b2Max(aabb1.upperBound.x, aabb2.upperBound.x);
this.upperBound.y = box2d.b2Max(aabb1.upperBound.y, aabb2.upperBound.y);
return this;
}
/**
* @export
* @return {box2d.b2AABB}
* @param {box2d.b2AABB} aabb1
* @param {box2d.b2AABB} aabb2
* @param {box2d.b2AABB} out
*/
box2d.b2AABB.Combine = function(aabb1, aabb2, out) {
out.Combine2(aabb1, aabb2);
return out;
}
/**
* Does this aabb contain the provided AABB.
* @export
* @return {boolean}
* @param {box2d.b2AABB} aabb
*/
box2d.b2AABB.prototype.Contains = function(aabb) {
var result = true;
result = result && this.lowerBound.x <= aabb.lowerBound.x;
result = result && this.lowerBound.y <= aabb.lowerBound.y;
result = result && aabb.upperBound.x <= this.upperBound.x;
result = result && aabb.upperBound.y <= this.upperBound.y;
return result;
}
/**
* From Real-time Collision Detection, p179.
* @export
* @return {boolean}
* @param {box2d.b2RayCastOutput} output
* @param {box2d.b2RayCastInput} input
*/
box2d.b2AABB.prototype.RayCast = function(output, input) {
var tmin = (-box2d.b2_maxFloat);
var tmax = box2d.b2_maxFloat;
var p_x = input.p1.x;
var p_y = input.p1.y;
var d_x = input.p2.x - input.p1.x;
var d_y = input.p2.y - input.p1.y;
var absD_x = box2d.b2Abs(d_x);
var absD_y = box2d.b2Abs(d_y);
var normal = output.normal;
if (absD_x < box2d.b2_epsilon) {
// Parallel.
if (p_x < this.lowerBound.x || this.upperBound.x < p_x) {
return false;
}
} else {
var inv_d = 1 / d_x;
var t1 = (this.lowerBound.x - p_x) * inv_d;
var t2 = (this.upperBound.x - p_x) * inv_d;
// Sign of the normal vector.
var s = (-1);
if (t1 > t2) {
var t3 = t1;
t1 = t2;
t2 = t3;
s = 1;
}
// Push the min up
if (t1 > tmin) {
normal.x = s;
normal.y = 0;
tmin = t1;
}
// Pull the max down
tmax = box2d.b2Min(tmax, t2);
if (tmin > tmax) {
return false;