UNPKG

quadtree2

Version:

JavaScript implementation of quadtree datastructure for collision detection.

397 lines (335 loc) 9.78 kB
;(function inject(clean, precision, undef) { function Vec2(x, y) { if (!(this instanceof Vec2)) { return new Vec2(x, y); } if('object' === typeof x && x) { this.y = x.y || 0; this.x = x.x || 0; return; } this.x = Vec2.clean(x || 0); this.y = Vec2.clean(y || 0); } Vec2.prototype = { change : function(fn) { if (fn) { if (this.observers) { this.observers.push(fn); } else { this.observers = [fn]; } } else if (this.observers) { for (var i=this.observers.length-1; i>=0; i--) { this.observers[i](this); } } return this; }, ignore : function(fn) { if (this.observers) { var o = this.observers, l = o.length; while(l--) { o[l] === fn && o.splice(l, 1); } } return this; }, // set x and y set: function(x, y, silent) { if('number' != typeof x) { silent = y; y = x.y; x = x.x; } if(this.x === x && this.y === y) return this; this.x = Vec2.clean(x); this.y = Vec2.clean(y); if(silent !== false) return this.change(); }, // reset x and y to zero zero : function() { return this.set(0, 0); }, // return a new vector with the same component values // as this one clone : function() { return new Vec2(this.x, this.y); }, // negate the values of this vector negate : function(returnNew) { if (returnNew) { return new Vec2(-this.x, -this.y); } else { return this.set(-this.x, -this.y); } }, // Add the incoming `vec2` vector to this vector add : function(vec2, returnNew) { if (!returnNew) { this.x += vec2.x; this.y += vec2.y; return this.change(); } else { // Return a new vector if `returnNew` is truthy return new Vec2( this.x + vec2.x, this.y + vec2.y ); } }, // Subtract the incoming `vec2` from this vector subtract : function(vec2, returnNew) { if (!returnNew) { this.x -= vec2.x; this.y -= vec2.y; return this.change(); } else { // Return a new vector if `returnNew` is truthy return new Vec2( this.x - vec2.x, this.y - vec2.y ); } }, // Multiply this vector by the incoming `vec2` multiply : function(vec2, returnNew) { var x,y; if ('number' !== typeof vec2) { //.x !== undef) { x = vec2.x; y = vec2.y; // Handle incoming scalars } else { x = y = vec2; } if (!returnNew) { return this.set(this.x * x, this.y * y); } else { return new Vec2( this.x * x, this.y * y ); } }, // Rotate this vector. Accepts a `Rotation` or angle in radians. // // Passing a truthy `inverse` will cause the rotation to // be reversed. // // If `returnNew` is truthy, a new // `Vec2` will be created with the values resulting from // the rotation. Otherwise the rotation will be applied // to this vector directly, and this vector will be returned. rotate : function(r, inverse, returnNew) { var x = this.x, y = this.y, cos = Math.cos(r), sin = Math.sin(r), rx, ry; inverse = (inverse) ? -1 : 1; rx = cos * x - (inverse * sin) * y; ry = (inverse * sin) * x + cos * y; if (returnNew) { return new Vec2(rx, ry); } else { return this.set(rx, ry); } }, // Calculate the length of this vector 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 `Vec2#length` (if possible). lengthSquared : function() { var x = this.x, y = this.y; return x*x+y*y; }, // Return the distance betwen this `Vec2` and the incoming vec2 vector // and return a scalar distance : function(vec2) { var x = this.x - vec2.x; var y = this.y - vec2.y; return Math.sqrt(x*x + y*y); }, // Convert this vector into a unit vector. // Returns the length. normalize : function(returnNew) { var length = this.length(); // Collect a ratio to shrink the x and y coords var invertedLength = (length < Number.MIN_VALUE) ? 0 : 1/length; if (!returnNew) { // Convert the coords to be greater than zero // but smaller than or equal to 1.0 return this.set(this.x * invertedLength, this.y * invertedLength); } else { return new Vec2(this.x * invertedLength, this.y * invertedLength); } }, // Determine if another `Vec2`'s components match this one's // also accepts 2 scalars equal : function(v, w) { if (w === undef) { w = v.y; v = v.x; } return (Vec2.clean(v) === this.x && Vec2.clean(w) === this.y); }, // Return a new `Vec2` that contains the absolute value of // each of this vector's parts abs : function(returnNew) { var x = Math.abs(this.x), y = Math.abs(this.y); if (returnNew) { return new Vec2(x, y); } else { return this.set(x, y); } }, // Return a new `Vec2` consisting of the smallest values // from this vector and the incoming // // When returnNew is truthy, a new `Vec2` will be returned // otherwise the minimum values in either this or `v` will // be applied to this vector. min : function(v, returnNew) { var tx = this.x, ty = this.y, vx = v.x, vy = v.y, x = tx < vx ? tx : vx, y = ty < vy ? ty : vy; if (returnNew) { return new Vec2(x, y); } else { return this.set(x, y); } }, // Return a new `Vec2` consisting of the largest values // from this vector and the incoming // // When returnNew is truthy, a new `Vec2` will be returned // otherwise the minimum values in either this or `v` will // be applied to this vector. max : function(v, returnNew) { var tx = this.x, ty = this.y, vx = v.x, vy = v.y, x = tx > vx ? tx : vx, y = ty > vy ? ty : vy; if (returnNew) { return new Vec2(x, y); } else { return this.set(x, y); } }, // Clamp values into a range. // If this vector's values are lower than the `low`'s // values, then raise them. If they are higher than // `high`'s then lower them. // // Passing returnNew as true will cause a new Vec2 to be // returned. Otherwise, this vector's values will be clamped clamp : function(low, high, returnNew) { var ret = this.min(high, true).max(low); if (returnNew) { return ret; } else { return this.set(ret.x, ret.y); } }, // Perform linear interpolation between two vectors // amount is a decimal between 0 and 1 lerp : function(vec, amount) { return this.add(vec.subtract(this, true).multiply(amount), true); }, // Get the skew vector such that dot(skew_vec, other) == cross(vec, other) skew : function() { // Returns a new vector. return new Vec2(-this.y, this.x); }, // calculate the dot product between // this vector and the incoming dot : function(b) { return Vec2.clean(this.x * b.x + b.y * this.y); }, // calculate the perpendicular dot product between // this vector and the incoming perpDot : function(b) { return Vec2.clean(this.x * b.y - this.y * b.x); }, // Determine the angle between two vec2s angleTo : function(vec) { return Math.atan2(this.perpDot(vec), this.dot(vec)); }, // Divide this vector's components by a scalar divide : function(vec2, returnNew) { var x,y; if ('number' !== typeof vec2) { x = vec2.x; y = vec2.y; // Handle incoming scalars } else { x = y = vec2; } if (x === 0 || y === 0) { throw new Error('division by zero') } if (isNaN(x) || isNaN(y)) { throw new Error('NaN detected'); } if (returnNew) { return new Vec2(this.x / x, this.y / y); } return this.set(this.x / x, this.y / y); }, isPointOnLine : function(start, end) { return (start.y - this.y) * (start.x - end.x) === (start.y - end.y) * (start.x - this.x); }, toArray: function() { return [this.x, this.y]; }, fromArray: function(array) { return this.set(array[0], array[1]); }, toJSON: function () { return {x: this.x, y: this.y}; }, toString: function() { return '(' + this.x + ', ' + this.y + ')'; } }; Vec2.fromArray = function(array) { return new Vec2(array[0], array[1]); }; // Floating point stability Vec2.precision = precision || 8; var p = Math.pow(10, Vec2.precision); Vec2.clean = clean || function(val) { if (isNaN(val)) { throw new Error('NaN detected'); } if (!isFinite(val)) { throw new Error('Infinity detected'); } if(Math.round(val) === val) { return val; } return Math.round(val * p)/p; }; Vec2.inject = inject; if(!clean) { Vec2.fast = inject(function (k) { return k; }); // Expose, but also allow creating a fresh Vec2 subclass. if (typeof module !== 'undefined' && typeof module.exports == 'object') { module.exports = Vec2; } else { window.Vec2 = window.Vec2 || Vec2; } } return Vec2; })();