js-2dmath
Version:
Fast 2d geometry math: Vector2, Rectangle, Circle, Matrix2x3 (2D transformation), Circle, BoundingBox, Line2, Segment2, Intersections, Distances, Transitions (animation/tween), Random numbers, Noise
228 lines (187 loc) • 5.34 kB
JavaScript
/**
* @todo NORMALIZE OUTPUT
*/
var Vec2 = require("../vec2.js"),
Polygon = require("../polygon.js"),
polygon_furthestPoint = Polygon.furthestPoint,
sqrt = Math.sqrt,
abs = Math.abs,
vec2_sub = Vec2.sub,
vec2_add = Vec2.add,
vec2_clone = Vec2.clone,
vec2_dot = Vec2.dot,
vec2_scale = Vec2.scale,
vec2_negate = Vec2.negate,
vec2_normalize = Vec2.normalize,
vec2_perp = Vec2.perp,
vec2_lengthSq = Vec2.lengthSq,
vec2_div = Vec2.div;
var l = [0, 0],
r = [0, 0],
v = [0, 0];
/**
* Find the separating edge for the given direction
* @param {Polygon} polygon
* @param {Vec2} n normal
*/
function _findSeparationEdge(polygon, n) {
// Compute farthest polygon point in particular direction.
var index = polygon_furthestPoint(v, polygon, n);
var index_prev = (index + polygon.length - 1) % polygon.length;
var index_next = (index + 1) % polygon.length;
var v_prev = vec2_clone(polygon[index_prev]);
var v_next = vec2_clone(polygon[index_next]);
vec2_sub(l, v, v_next);
vec2_sub(r, v, v_prev);
var edge = {};
if (vec2_dot(r, n) <= vec2_dot(l, n)) {
return [v_prev, vec2_clone(v)];
}
return [vec2_clone(v), v_next];
}
var delta = [0, 0],
_p = [0, 0];
/**
* @param {Vec2} v1
* @param {Vec2} v2
* @param {Vec2} n
* @param {Number} o
*/
function _clipLineSegment(v1, v2, n, o) {
var d1 = vec2_dot(n, v1) - o;
var d2 = vec2_dot(n, v2) - o;
var cp = [];
if (d1 >= 0) {
cp.push(v1);
}
if (d2 >= 0) {
cp.push(v2);
}
if (d1 * d2 < 0) {
vec2_sub(delta, v2, v1);
var p = vec2_add([0, 0], v1, vec2_scale(_p, delta, d1 / (d1 - d2)));
cp.push(p);
}
return cp;
}
var ccp_nn = [0, 0],
e1d = [0, 0],
e2d = [0, 0],
ref_n = [0, 0],
ref_nn = [0, 0],
ref_perp = [0, 0];
/**
* @source https://github.com/juhl/collision-detection-2d
* @param {Polygon} a_points
* @param {Polygon} b_points
* @param {Vec2} n normal
* @return {Object}
*/
function EdgeClipping(a_points, b_points, n) {
var e1 = _findSeparationEdge(a_points, n);
vec2_negate(ccp_nn, n);
var e2 = _findSeparationEdge(b_points, ccp_nn);
vec2_sub(e1d, e1[1], e1[0]);
vec2_sub(e2d, e2[1], e2[0]);
var ref;
var inc;
var flip;
// The reference edge is the edge most perpendicular to the separation normal.
// So as to separate both polygons as little as possible.
var en1 = abs(vec2_dot(e1d, n));
var en2 = abs(vec2_dot(e2d, n));
if (en1 <= en2) {
ref = e1;
vec2_normalize(ref_n, e1d);
inc = e2;
flip = false;
} else {
ref = e2;
vec2_normalize(ref_n, e2d);
inc = e1;
flip = true;
}
// Clip incident edge vertices using reference edge v1
var o1 = vec2_dot(ref_n, ref[0]);
var v = _clipLineSegment(inc[0], inc[1], ref_n, o1);
if (v.length < 2) {
console.log("v.length 1", v.length);
return null;
}
// Clip incident edge vertices using reference edge v2
var o2 = vec2_dot(ref_n, ref[1]);
vec2_negate(ref_nn, ref_n);
v = _clipLineSegment(v[0], v[1], ref_nn, -o2);
if (v.length < 2) {
console.log("v.length 2", v.length);
return null;
}
vec2_perp(ref_perp, ref_n);
if (flip) {
vec2_negate(ref_perp, ref_perp);
}
var cp = [];
var max = vec2_dot(ref_perp, ref[0]);
var depth0 = vec2_dot(ref_perp, v[0]) - max;
var depth1 = vec2_dot(ref_perp, v[1]) - max;
if (depth0 >= 0) {
cp.push({p: vec2_clone(v[0]), n: vec2_clone(n), d: -depth0});
}
if (depth1 >= 0) {
cp.push({p: vec2_clone(v[1]), n: vec2_clone(n), d: -depth1});
}
return { cp: cp, incidentEdge: inc, referenceEdge: ref };
}
var cc_n = [0, 0];
/**
* @source http://www.randygaul.net/2013/03/28/custom-physics-engine-part-2-manifold-generation/
* @param {Circle} a_circle
* @param {Circle} b_circle
*/
function CircleCircle(a_circle, b_circle) {
// Setup a couple pointers to each object
// Vector from A to B
vec2_sub(cc_n, b_circle[0], a_circle[0]);
var r = b_circle[1] + a_circle[1];
r *= r;
var length_sq = vec2_lengthSq(cc_n);
if (length_sq > r) {
return false;
}
// Circles have collided, now compute manifold
var d = sqrt(length_sq); // perform actual sqrt
// If distance between circles is not zero
if (d !== 0) {
// Distance is difference between radius and distance
var normal = vec2_div([0, 0], cc_n, d);
var position = vec2_scale([0, 0], normal * a_circle[1]);
vec2_add(position, position, a_circle[0]);
return {
penetration: r - d,
position: position,
normal: normal
};
}
// Circles are on same position
// Choose random (but consistent) values
return {
position: a_circle[0],
penetration: a_circle[1],
normal: [1, 0]
};
}
/*
EdgeClipping(
[[8, 4], [14, 4], [8, 10], [14, 10]],
[[12, 5], [4, 5], [12, 0], [4, 0]],
[0, -1]
);
process.exit();
*/
module.exports = EdgeClipping;
module.exports = {
EdgeClipping: EdgeClipping,
CircleCircle: CircleCircle,
// alias
PolygonPolygon: EdgeClipping
};