UNPKG

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

422 lines (386 loc) 9.16 kB
/** * Stability: 1 (Only additions & fixes) * * BoundingBox2 is represented as a 5 coordinates array * [left: Number, bottom: Number, right: Number, top: Number, normalized: Boolean] */ var min = Math.min, max = Math.max, TOPLEFT = 1, TOPMIDDLE = 2, TOPRIGHT = 3, CENTERLEFT = 4, CENTER = 5, CENTERRIGHT = 6, BOTTOMLEFT = 7, BOTTOM = 8, BOTTOMRIGHT = 9, r = 0, x = 0, y = 0, min_x = 0, max_x = 0, min_y = 0, max_y = 0; /** * @param {Number} l * @param {Number} b * @param {Number} r * @param {Number} t * @return {AABB2} */ function create(l, b, r, t) { var out = [l, b, r, t, false]; normalize(out, out); return out; } /** * @param {AABB2} aabb2 * @param {Number} x * @param {Number} y * @return {Array<AABB2>} */ function fromAABB2Division(aabb2, x, y) { var out = [], i, j, l = aabb2[0], b = aabb2[1], r = aabb2[2], t = aabb2[3], w = (r - l) / x, h = (t - b) / y; for (i = 0; i < x; ++i) { for (j = 0; j < y; ++j) { out.push([l + i * w, b + j * h, l + (i + 1) * w, b + (j + 1) * h]); } } return out; } /** * @param {Segment2} seg2 * @return {AABB2} */ function fromSegment2(seg2) { var out = [seg2[0], seg2[1], seg2[2], seg2[3], false]; normalize(out, out); return out; } /** * @param {Circle} circle * @return {AABB2} */ function fromCircle(circle) { r = circle[1]; x = circle[0][0]; y = circle[0][1]; return create( x - r, y - r, x + r, y + r ); } /** * @param {Rectangle} rect * @return {AABB2} */ function fromRectangle(rect) { var out = [rect[0][0], rect[0][1], rect[1][0], rect[1][1], false]; normalize(out, out); return out; } /** * @todo implement a more robust / fast algorithm http://stackoverflow.com/questions/2587751/an-algorithm-to-find-bounding-box-of-closed-bezier-curves (Timo answer) * * @reference http://jsfiddle.net/4VCVX/3/ * * @param {Beizer} beizer * @param {Number} npoints * @return {AABB2} */ function fromBeizer(beizer, npoints) { npoints = npoints || 40; var vec2_list = Beizer.get(beizer, npoints), i, l = Infinity, b = Infinity, r = -Infinity, t = -Infinity, v, x, y; // loop min, max for (i = 0; i < npoints; ++i) { v = vec2_list[i]; x = v[0]; y = v[1]; if (x > r) { r = x; } else if (x < l) { l = x; } if (y < b) { b = y; } else if (y > t) { t = y; } } return [l, b, r, t, true]; } /** * @return {AABB2} */ function zero() { return [0, 0, 0, 0, true]; } /** * @param {AABB2} aabb2 * @return {AABB2} */ function clone(aabb2) { return [aabb2[0], aabb2[1], aabb2[2], aabb2[3], aabb2[4]]; } /** * @param {AABB2} out * @param {AABB2} aabb2 * @return {AABB2} */ function copy(out, aabb2) { out[0] = aabb2[0]; out[1] = aabb2[1]; out[2] = aabb2[2]; out[3] = aabb2[3]; out[4] = aabb2[4]; return out; } /** * @param {AABB2} out * @param {AABB2} aabb2 * @param {Number} margin * @return {AABB2} */ function expand(out, aabb2, margin) { out[0] = aabb2[0] - margin; out[1] = aabb2[1] - margin; out[2] = aabb2[2] + margin; out[3] = aabb2[3] + margin; return out; } /** * @param {AABB2} out * @param {AABB2} aabb2_1 * @param {AABB2} aabb2_2 * @return {AABB2} */ function merge(out, aabb2_1, aabb2_2) { out[0] = min(aabb2_1[0], aabb2_2[0]); out[1] = min(aabb2_1[1], aabb2_2[1]); out[2] = max(aabb2_1[2], aabb2_2[2]); out[3] = max(aabb2_1[3], aabb2_2[3]); return out; } /** * @param {AABB2} out * @param {AABB2} aabb2_1 * @param {AABB2} aabb2_2 * @param {Vec2} vec2_offset * @return {AABB2} */ function offsetMerge(out, aabb2_1, aabb2_2, vec2_offset) { out[0] = min(aabb2_1[0], aabb2_2[0] + vec2_offset[0]); out[1] = min(aabb2_1[1], aabb2_2[1] + vec2_offset[1]); out[2] = max(aabb2_1[2], aabb2_2[2] + vec2_offset[0]); out[3] = max(aabb2_1[3], aabb2_2[3] + vec2_offset[1]); return out; } /** * offset & scale merge * @param {AABB2} out * @param {AABB2} aabb2_1 * @param {AABB2} aabb2_2 * @param {Vec2} vec2_offset * @param {Vec2} vec2_scale * @return {AABB2} */ function osMerge(out, aabb2_1, aabb2_2, vec2_offset, vec2_scale) { out[0] = min(aabb2_1[0], (aabb2_2[0] * vec2_scale[0]) + vec2_offset[0]); out[1] = min(aabb2_1[1], (aabb2_2[1] * vec2_scale[1]) + vec2_offset[1]); out[2] = max(aabb2_1[2], (aabb2_2[2] * vec2_scale[0]) + vec2_offset[0]); out[3] = max(aabb2_1[3], (aabb2_2[3] * vec2_scale[1]) + vec2_offset[1]); return out; } /** * offset & scale merge * @param {AABB2} aabb2 * @return {Number} */ function area(aabb2) { return (aabb2[2] - aabb2[0]) * (aabb2[3] - aabb2[1]); } /** * @param {AABB2} out * @param {AABB2} aabb2 * @return {AABB2} */ function normalize(out, aabb2) { min_x = aabb2[0] > aabb2[2] ? aabb2[2] : aabb2[0]; max_x = aabb2[0] > aabb2[2] ? aabb2[0] : aabb2[2]; min_y = aabb2[1] > aabb2[3] ? aabb2[3] : aabb2[1]; max_y = aabb2[1] > aabb2[3] ? aabb2[1] : aabb2[3]; out[0] = min_x; out[1] = min_y; out[2] = max_x; out[3] = max_y; out[4] = true; } /** * @param {AABB2} out * @param {AABB2} aabb2 * @param {Vec2} vec2 * @return {AABB2} */ function translate(out, aabb2, vec2) { x = vec2[0]; y = vec2[1]; out[0] = aabb2[0] + x; out[1] = aabb2[1] + y; out[2] = aabb2[2] + x; out[3] = aabb2[3] + y; return out; } /** * @param {Vec2} out_vec2 * @param {AABB2} aabb2 * @param {Vec2} vec2 * @return {Vec2} */ function clampVec(out_vec2, aabb2, vec2) { out_vec2[0] = min(max(aabb2[0], vec2[0]), aabb2[2]); out_vec2[1] = min(max(aabb2[1], vec2[1]), aabb2[3]); return out_vec2; } /** * @param {Vec2} out_vec2 * @param {AABB2} aabb2 */ function center(out_vec2, aabb2) { out_vec2[0] = (aabb2[0] + aabb2[1]) * 0.5; out_vec2[1] = (aabb2[3] + aabb2[2]) * 0.5; return out_vec2; } /** * alignment values: AABB2.TOPLEFT, AABB2.TOPMIDDLE, AABB2.TOPRIGHT, AABB2.CENTERLEFT, AABB2.CENTER, AABB2.CENTERRIGHT, AABB2.BOTTOMLEFT, AABB2.BOTTOM, AABB2.BOTTOMRIGH * * @param {Vec2} out_vec2 * @param {AABB2} aabb2 * @param {Number} alignment * @return {Vec2} */ function align(out_vec2, aabb2, alignment) { switch (alignment) { case TOPLEFT: // do nothing! out_vec2[0] = aabb2[0]; out_vec2[1] = aabb2[1]; break; case TOPMIDDLE: out_vec2[0] = (aabb2[2] - aabb2[0]) * 0.5 + aabb2[0]; out_vec2[1] = aabb2[1]; break; case TOPRIGHT: out_vec2[0] = aabb2[2]; out_vec2[1] = aabb2[1]; break; case CENTERLEFT: out_vec2[0] = aabb2[0]; out_vec2[1] = (aabb2[3] - aabb2[1]) * 0.5 + aabb2[1]; break; case CENTER: out_vec2[0] = (aabb2[2] - aabb2[0]) * 0.5 + aabb2[0]; out_vec2[1] = (aabb2[3] - aabb2[1]) * 0.5 + aabb2[1]; break; case CENTERRIGHT: out_vec2[0] = aabb2[2]; out_vec2[1] = (aabb2[3] - aabb2[1]) * 0.5 + aabb2[1]; break; case BOTTOMLEFT: out_vec2[0] = aabb2[0]; out_vec2[1] = aabb2[3]; break; case BOTTOM: out_vec2[0] = (aabb2[2] - aabb2[0]) * 0.5 + aabb2[0]; out_vec2[1] = aabb2[3]; break; case BOTTOMRIGHT: out_vec2[0] = aabb2[2]; out_vec2[1] = aabb2[3]; break; } return out_vec2; } /** * @param {AABB2} aabb2 * @param {Vec2} vec2 * @return {Boolean} */ function isVec2Inside(aabb2, vec2) { return aabb2[0] < vec2[0] && aabb2[2] > vec2[0] && aabb2[1] < vec2[1] && aabb2[3] > vec2[1]; } /** * @param {AABB2} aabb2 * @param {AABB2} aabb2_2 * @return {Boolean} */ function isAABB2Inside(aabb2, aabb2_2) { return aabb2[0] <= aabb2_2[0] && aabb2[1] <= aabb2_2[1] && aabb2_2[2] <= aabb2[2] && aabb2_2[3] <= aabb2[3]; } /** * @param {AABB2} aabb2 * @return {Number} */ function perimeter(aabb2) { return (aabb2[2] - aabb2[0]) * 2 + (aabb2[3] - aabb2[1]) * 2; } /** * @class AABB2 */ var AABB2 = { // defines TOPLEFT: TOPLEFT, TOPMIDDLE: TOPMIDDLE, TOPRIGHT: TOPRIGHT, CENTERLEFT: CENTERLEFT, CENTER: CENTER, CENTERRIGHT: CENTERRIGHT, BOTTOMLEFT: BOTTOMLEFT, BOTTOM: BOTTOM, BOTTOMRIGHT: BOTTOMRIGHT, create: create, fromAABB2Division: fromAABB2Division, fromSegment2: fromSegment2, fromCircle: fromCircle, fromRectangle: fromRectangle, zero: zero, clone: clone, copy: copy, expand: expand, merge: merge, offsetMerge: offsetMerge, osMerge: osMerge, area: area, normalize: normalize, translate: translate, clampVec: clampVec, center: center, align: align, isVec2Inside: isVec2Inside, isAABB2Inside: isAABB2Inside, perimeter: perimeter, // alias contains: isAABB2Inside }; module.exports = AABB2;