planck-js
Version:
2D physics engine for JavaScript/HTML5 game development
223 lines (185 loc) • 6.2 kB
JavaScript
/*
* Copyright (c) 2016 Ali Shakiba http://shakiba.me/planck.js
* Copyright (c) 2006-2011 Erin Catto http://www.box2d.org
*
* 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.
*/
var Settings = require('../Settings');
var Math = require('../common/Math');
var Vec2 = require('../common/Vec2');
module.exports = AABB;
function AABB(lower, upper) {
if (!(this instanceof AABB)) {
return new AABB(lower, upper);
}
this.lowerBound = Vec2();
this.upperBound = Vec2();
if (typeof lower === 'object') {
this.lowerBound.Set(lower);
}
if (typeof upper === 'object') {
this.upperBound.Set(upper);
}
};
/**
* Verify that the bounds are sorted.
*/
AABB.prototype.IsValid = function() {
return AABB.IsValid(this);
}
AABB.IsValid = function(aabb) {
var d = Vec2.Sub(aabb.upperBound, aabb.lowerBound);
var valid = d.x >= 0.0 && d.y >= 0.0 && Vec2.IsValid(aabb.lowerBound)
&& Vec2.IsValid(aabb.upperBound);
return valid;
}
/**
* Get the center of the AABB.
*/
AABB.prototype.GetCenter = function() {
return Vec2((this.lowerBound.x + this.upperBound.x) * 0.5,
(this.lowerBound.y + this.upperBound.y) * 0.5);
}
/**
* Get the extents of the AABB (half-widths).
*/
AABB.prototype.GetExtents = function() {
return Vec2((this.upperBound.x - this.lowerBound.x) * 0.5,
(this.upperBound.y - this.lowerBound.y) * 0.5);
}
/**
* Get the perimeter length.
*/
AABB.prototype.GetPerimeter = function() {
return 2.0 * (this.upperBound.x - this.lowerBound.x + this.upperBound.y - this.lowerBound.y);
}
/**
* Combine one or two AABB into this one.
*/
AABB.prototype.Combine = function(a, b) {
b = b || this;
this.lowerBound.Set(Math.min(a.lowerBound.x, b.lowerBound.x), Math.min(
a.lowerBound.y, b.lowerBound.y));
this.upperBound.Set(Math.max(a.upperBound.x, b.upperBound.x), Math.max(
a.upperBound.y, b.upperBound.y));
}
AABB.prototype.CombinePoints = function(a, b) {
this.lowerBound.Set(Math.min(a.x, b.x), Math.min(a.y, b.y));
this.upperBound.Set(Math.max(a.x, b.x), Math.max(a.y, b.y));
}
AABB.prototype.Set = function(aabb) {
this.lowerBound.Set(aabb.lowerBound.x, aabb.lowerBound.y);
this.upperBound.Set(aabb.upperBound.x, aabb.upperBound.y);
}
AABB.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;
}
AABB.prototype.Extend = function(value) {
AABB.Extend(this, value);
}
AABB.Extend = function(aabb, value) {
aabb.lowerBound.x -= value;
aabb.lowerBound.y -= value;
aabb.upperBound.x += value;
aabb.upperBound.y += value;
}
AABB.TestOverlap = function(a, b) {
var d1x = b.lowerBound.x - a.upperBound.x;
var d2x = a.lowerBound.x - b.upperBound.x;
var d1y = b.lowerBound.y - a.upperBound.y;
var d2y = a.lowerBound.y - b.upperBound.y;
if (d1x > 0 || d1y > 0 || d2x > 0 || d2y > 0) {
return false;
}
return true;
}
AABB.Equals = function(a, b) {
return Vec2.Equals(a.lowerBound, b.lowerBound)
&& Vec2.Equals(a.upperBound, b.upperBound);
}
AABB.Diff = function(a, b) {
var wD = Math.max(0, Math.min(a.upperBound.x, b.upperBound.x)
- Math.max(b.lowerBound.x, a.lowerBound.x))
var hD = Math.max(0, Math.min(a.upperBound.y, b.upperBound.y)
- Math.max(b.lowerBound.y, a.lowerBound.y));
var wA = a.upperBound.x - a.lowerBound.x;
var hA = a.upperBound.y - a.lowerBound.y;
var hB = b.upperBound.y - b.lowerBound.y;
var hB = b.upperBound.y - b.lowerBound.y;
return wA * hA + wB * hB - wD * hD;
}
var XY = [ 'x', 'y' ];
/**
* @param {RayCastOutput} output
* @param {RayCastInput} input
* @returns {boolean}
*/
AABB.prototype.RayCast = function(output, input) {
// From Real-time Collision Detection, p179.
var tmin = -Infinity;
var tmax = Infinity;
var p = input.p1;
var d = Sub(input.p2, input.p1);
var absD = Abs(d);
var normal = Vec2();
for (var i = 0; i < 2; ++i) {
var field = XY[i];
if (absD.x < Math.EPSILON) {
// Parallel.
if (p[field] < lowerBound[field] || upperBound[field] < p[field]) {
return false;
}
} else {
var inv_d = 1.0 / d[field];
var t1 = (lowerBound[field] - p[field]) * inv_d;
var t2 = (upperBound[field] - p[field]) * inv_d;
// Sign of the normal vector.
var s = -1.0;
if (t1 > t2) {
var temp = t1;
t1 = t2, t2 = temp;
s = 1.0;
}
// Push the min up
if (t1 > tmin) {
normal.SetZero();
normal[field] = s;
tmin = t1;
}
// Pull the max down
tmax = Min(tmax, t2);
if (tmin > tmax) {
return false;
}
}
}
// Does the ray start inside the box?
// Does the ray intersect beyond the max fraction?
if (tmin < 0.0 || input.maxFraction < tmin) {
return false;
}
// Intersection.
output.fraction = tmin;
output.normal = normal;
return true;
}
AABB.prototype.toString = function() {
return JSON.stringify(this);
}