- Add pause, reset and step forward playback controls.
- Add camera control.
Last active
April 20, 2023 12:13
-
-
Save foldi/5883853 to your computer and use it in GitHub Desktop.
SimpleSim step 10
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta http-equiv='content-type' content='text/html; charset=UTF-8' /> | |
<meta name='viewport' content='user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0' /> | |
<meta name='apple-mobile-web-app-capable' content='yes' /> | |
<title>Simple Simulator</title> | |
<link rel='stylesheet' href='main.css' type='text/css' charset='utf-8' /> | |
<script src='modernizr.js' type='text/javascript' charset='utf-8'></script> | |
<script src='simplesim.js' type='text/javascript' charset='utf-8'></script> | |
<script src='rocks.js' type='text/javascript' charset='utf-8'></script> | |
</head> | |
<body> | |
<script type='text/javascript' charset='utf-8'> | |
var system = SimpleSim.System; | |
SimpleSim.Classes = Rocks; | |
system.init(function() { | |
for (var i = 1; i < 11; i++) { | |
system.add('Pebble', { | |
index: i, | |
controlCamera: i === 10 | |
}); | |
if (i < 7) { | |
system.add('Boulder', { | |
index: i | |
}); | |
} | |
} | |
}, null, { | |
csstransforms3d: Modernizr.csstransforms3d, | |
csstransforms: Modernizr.csstransforms | |
}); | |
</script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
html { | |
background-color: #fff; | |
} | |
.world { | |
position: relative; | |
margin: 0; | |
padding: 0; | |
overflow: hidden; | |
} | |
.item { | |
position: absolute; | |
top: 0; left: 0; | |
-webkit-box-sizing: border-box; | |
-moz-box-sizing: border-box; | |
-o-box-sizing: border-box; | |
-ms-box-sizing: border-box; | |
box-sizing: border-box; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Modernizr 2.6.2 (Custom Build) | MIT & BSD | |
* Build: http://modernizr.com/download/#-csstransforms-csstransforms3d-teststyles-testprop-testallprops-prefixes-domprefixes | |
*/ | |
;window.Modernizr=function(a,b,c){function y(a){i.cssText=a}function z(a,b){return y(l.join(a+";")+(b||""))}function A(a,b){return typeof a===b}function B(a,b){return!!~(""+a).indexOf(b)}function C(a,b){for(var d in a){var e=a[d];if(!B(e,"-")&&i[e]!==c)return b=="pfx"?e:!0}return!1}function D(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:A(f,"function")?f.bind(d||b):f}return!1}function E(a,b,c){var d=a.charAt(0).toUpperCase()+a.slice(1),e=(a+" "+n.join(d+" ")+d).split(" ");return A(b,"string")||A(b,"undefined")?C(e,b):(e=(a+" "+o.join(d+" ")+d).split(" "),D(e,b,c))}var d="2.6.2",e={},f=b.documentElement,g="modernizr",h=b.createElement(g),i=h.style,j,k={}.toString,l=" -webkit- -moz- -o- -ms- ".split(" "),m="Webkit Moz O ms",n=m.split(" "),o=m.toLowerCase().split(" "),p={},q={},r={},s=[],t=s.slice,u,v=function(a,c,d,e){var h,i,j,k,l=b.createElement("div"),m=b.body,n=m||b.createElement("body");if(parseInt(d,10))while(d--)j=b.createElement("div"),j.id=e?e[d]:g+(d+1),l.appendChild(j);return h=["­",'<style id="s',g,'">',a,"</style>"].join(""),l.id=g,(m?l:n).innerHTML+=h,n.appendChild(l),m||(n.style.background="",n.style.overflow="hidden",k=f.style.overflow,f.style.overflow="hidden",f.appendChild(n)),i=c(l,a),m?l.parentNode.removeChild(l):(n.parentNode.removeChild(n),f.style.overflow=k),!!i},w={}.hasOwnProperty,x;!A(w,"undefined")&&!A(w.call,"undefined")?x=function(a,b){return w.call(a,b)}:x=function(a,b){return b in a&&A(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=t.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(t.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(t.call(arguments)))};return e}),p.csstransforms=function(){return!!E("transform")},p.csstransforms3d=function(){var a=!!E("perspective");return a&&"webkitPerspective"in f.style&&v("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}",function(b,c){a=b.offsetLeft===9&&b.offsetHeight===3}),a};for(var F in p)x(p,F)&&(u=F.toLowerCase(),e[u]=p[F](),s.push((e[u]?"":"no-")+u));return e.addTest=function(a,b){if(typeof a=="object")for(var d in a)x(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,typeof enableClasses!="undefined"&&enableClasses&&(f.className+=" "+(b?"":"no-")+a),e[a]=b}return e},y(""),h=j=null,e._version=d,e._prefixes=l,e._domPrefixes=o,e._cssomPrefixes=n,e.testProp=function(a){return C([a])},e.testAllProps=E,e.testStyles=v,e}(this,this.document); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Rocks = {}; exports = Rocks; | |
(function(exports) { | |
/** | |
* Creates a new Pebble. | |
* | |
* @param {Object} [opt_options=] A map of initial properties. | |
* @constructor | |
*/ | |
function Pebble(opt_options) { | |
var options = opt_options || {}, | |
index = opt_options.index || 5, | |
size = 15 + index; | |
options.width = size; | |
options.height = size; | |
options.color = [SimpleSim.Utils.getRandomNumber(100, 255), SimpleSim.Utils.getRandomNumber(10, 100), 0]; | |
options.maxSpeed = 15; | |
options.borderRadius = SimpleSim.Utils.getRandomNumber(40, 50); | |
SimpleSim.Item.call(this, options); | |
} | |
SimpleSim.Utils.extend(Pebble, SimpleSim.Item); | |
exports.Pebble = Pebble; | |
}(exports)); | |
(function(exports) { | |
/** | |
* Creates a new Boulder. | |
* | |
* @param {Object} [opt_options=] A map of initial properties. | |
* @constructor | |
*/ | |
function Boulder(opt_options) { | |
var options = opt_options || {}, | |
index = opt_options.index || 5, | |
size = 50 + 10 * index; | |
options.width = size; | |
options.height = size; | |
options.color = [SimpleSim.Utils.getRandomNumber(100, 255), SimpleSim.Utils.getRandomNumber(100, 255), 0]; | |
options.maxSpeed = 15; | |
options.borderRadius = SimpleSim.Utils.getRandomNumber(30, 40); | |
SimpleSim.Item.call(this, options); | |
} | |
SimpleSim.Utils.extend(Boulder, SimpleSim.Item); | |
exports.Boulder = Boulder; | |
}(exports)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
SimpleSim = {}; exports = SimpleSim; | |
(function(exports) { | |
/** @namespace */ | |
var System = { | |
name: 'System' | |
}; | |
/** | |
* Stores references to all items in the system. | |
* @private | |
*/ | |
System._records = { | |
lookup: {}, | |
list: [] | |
}; | |
/** | |
* Used to create unique ids. | |
* @private | |
*/ | |
System._idCount = 0; | |
/** | |
* Holds a transform property based on supportedFeatures. | |
* @private | |
*/ | |
System._stylePosition = ''; | |
/** | |
* Increments idCount and returns the value. | |
*/ | |
System.getNewId = function() { | |
this._idCount++; | |
return this._idCount; | |
}; | |
/** | |
* Initializes the system and starts the update loop. | |
* | |
* @param {Function} opt_setup= Creates the initial system conditions. | |
* @param {Object} opt_world= A reference to a DOM element representing the System world. | |
* @param {Function} opt_supportedFeatures= A map of supported browser features. | |
*/ | |
System.init = function(opt_setup, opt_world, opt_supportedFeatures) { | |
var setup = opt_setup || function () {}, | |
world = opt_world || document.body, | |
supportedFeatures = opt_supportedFeatures || null; | |
if (supportedFeatures.csstransforms3d) { | |
this._stylePosition = '-webkit-transform: translate3d(<x>px, <y>px, 0) rotate(<a>deg); -moz-transform: translate3d(<x>px, <y>px, 0) rotate(<a>deg); -o-transform: translate3d(<x>px, <y>px, 0) rotate(<a>deg); -ms-transform: translate3d(<x>px, <y>px, 0) rotate(<a>deg);'; | |
} else if (supportedFeatures.csstransforms) { | |
this._stylePosition = '-webkit-transform: translate(<x>px, <y>px) rotate(<a>deg); -moz-transform: translate(<x>px, <y>px) rotate(<a>deg); -o-transform: translate(<x>px, <y>px) rotate(<a>deg); -ms-transform: translate(<x>px, <y>px) rotate(<a>deg);'; | |
} else { | |
this._stylePosition = 'position: absolute; left: <x>px; top: <y>px;'; | |
} | |
System._records.list.push(new exports.World(world)); | |
exports.Utils._addEvent(window, 'resize', function(e) { | |
System._resize.call(System, e); | |
}); | |
exports.Utils._addEvent(window, 'devicemotion', function(e) { | |
var world = System._records.list[0], | |
x = e.accelerationIncludingGravity.x, | |
y = e.accelerationIncludingGravity.y; | |
if (window.orientation === 0) { | |
world.gravity.x = x; | |
world.gravity.y = y * -1; | |
} else if (window.orientation === -90) { | |
world.gravity.x = y; | |
world.gravity.y = x; | |
} else { | |
world.gravity.x = y * -1; | |
world.gravity.y = x * -1; | |
} | |
}); | |
exports.Utils._addEvent(window, 'keyup', function(e) { | |
System._keyup.call(System, e); | |
}); | |
setup.call(this); | |
this._setup = setup; | |
this._update(); | |
}; | |
/** | |
* Handles keyup events. | |
* | |
* @param {Object} e An event. | |
*/ | |
System._keyup = function(e) { | |
var world = this._records.list[0]; | |
switch(e.keyCode) { | |
case 39: | |
System._stepForward(); // right arrow: step forward | |
break; | |
case 80: // p; pause/play | |
world.pauseStep = !world.pauseStep; | |
break; | |
case 82: // r; reset | |
System._resetSystem(); | |
break; | |
} | |
}; | |
/** | |
* Adds an object to the system. | |
* | |
* @param {Object} opt_options= Object properties. | |
*/ | |
System.add = function(klass, opt_options) { | |
var last, records = this._records.list, | |
recordsLookup = this._records.lookup, | |
options = opt_options || {}; | |
options.world = records[0]; | |
if (exports[klass]) { | |
records[records.length] = new exports[klass](options); | |
} else if (exports.Classes[klass]) { | |
records[records.length] = new exports.Classes[klass](options); | |
} else { | |
throw new Error(klass + ' class does not exist.'); | |
} | |
last = records.length - 1; | |
recordsLookup[records[last].id] = records[last].el.parentNode; | |
records[last].init(options); | |
return records[last]; | |
}; | |
/** | |
* Iterates over objects in the system and calls step() and draw(). | |
* @private | |
*/ | |
System._update = function() { | |
var i, records = System._records.list, record; | |
for (i = records.length - 1; i >= 0; i -= 1) { | |
record = records[i]; | |
if (!record.world.pauseStep) { | |
record.step(); | |
} | |
} | |
for (i = records.length - 1; i >= 0; i -= 1) { | |
records[i].draw(); | |
} | |
window.requestAnimFrame(System._update); | |
}; | |
/** | |
* Pauses the system and processes one step in records. | |
* @private | |
*/ | |
System._stepForward = function() { | |
var i, records = System._records.list, | |
world = this._records.list[0]; | |
world.pauseStep = true; | |
for (i = records.length - 1; i >= 0; i -= 1) { | |
records[i].step(); | |
} | |
for (i = records.length - 1; i >= 0; i -= 1) { | |
records[i].draw(); | |
} | |
}; | |
/** | |
* Updates the corresponding DOM element's style property. | |
*/ | |
System._draw = function(obj) { | |
var cssText = exports.System.getCSSText({ | |
x: obj.location.x - (obj.width / 2), | |
y: obj.location.y - (obj.height / 2), | |
width: obj.width, | |
height: obj.height, | |
color0: obj.color[0], | |
color1: obj.color[1], | |
color2: obj.color[2], | |
visibility: obj.visibility, | |
borderRadius: obj.borderRadius, | |
a: obj.angle | |
}); | |
obj.el.style.cssText = cssText; | |
}; | |
/** | |
* Concatenates a new cssText string. | |
* | |
* @param {Object} props A map of object properties. | |
*/ | |
System.getCSSText = function(props) { | |
return this._stylePosition.replace(/<x>/g, props.x).replace(/<y>/g, props.y).replace(/<a>/g, props.a) + ' width: ' + | |
props.width + 'px; height: ' + props.height + 'px; background-color: ' + | |
'rgb(' + props.color0 + ', ' + props.color1 + ', ' + props.color2 + ');' + | |
'visibility: ' + props.visibility + '; border-radius: ' + props.borderRadius + '%'; | |
}; | |
/** | |
* Repositions all items relative to the viewport size and resets the world bounds. | |
*/ | |
System._resize = function() { | |
var i, max, records = this._records.list, record, | |
viewportSize = exports.Utils.getViewportSize(), | |
world = records[0]; | |
for (i = 1, max = records.length; i < max; i++) { | |
record = records[i]; | |
record.location.x = viewportSize.width * (record.location.x / world.width); | |
record.location.y = viewportSize.height * (record.location.y / world.height); | |
} | |
world.width = viewportSize.width; | |
world.height = viewportSize.height; | |
world.location = new exports.Vector((viewportSize.width / 2), | |
(viewportSize.height / 2)); | |
}; | |
/** | |
* Resets the system. | |
* | |
* @param {boolean} opt_noRestart= Pass true to not restart the system. | |
* @private | |
*/ | |
System._resetSystem = function(opt_noRestart) { | |
var world = this._records.list[0], | |
viewportSize = exports.Utils.getViewportSize(); | |
world.pauseStep = false; | |
while(world.el.firstChild) { | |
world.el.removeChild(world.el.firstChild); | |
} | |
world.location = new exports.Vector((viewportSize.width / 2), | |
(viewportSize.height / 2)); | |
this._records.list = this._records.list.splice(0, 1); | |
System._setup.call(System); | |
}; | |
exports.System = System; | |
}(exports)); | |
(function(exports) { | |
/** | |
* Creates a new World. | |
* | |
* @param {Object} el The DOM element representing the world. | |
* @constructor | |
*/ | |
function World(el) { | |
var viewportSize = exports.Utils.getViewportSize(); | |
if (!el || typeof el !== 'object') { | |
throw new Error('World: A valid DOM object is required for a new World.'); | |
} | |
this.el = el; | |
this.el.className = 'world'; | |
this.width = viewportSize.width; | |
this.height = viewportSize.height; | |
this.location = new exports.Vector(viewportSize.width / 2, viewportSize.height / 2); | |
this.angle = 0; | |
this.gravity = new exports.Vector(0, 0.1); | |
this.wind = new exports.Vector(0.05, 0); | |
this.thermal = new exports.Vector(0, -0.025); | |
this.color = [230, 230, 230]; | |
this.visibility ='visible'; | |
this.cacheVector = new exports.Vector(); | |
this.pauseStep = false; | |
this.camera = new exports.Vector(); | |
} | |
/** | |
* Worlds do not have worlds. However, assigning a | |
* blank object avoid coding extra logic in System._update. | |
*/ | |
World.prototype.world = {}; | |
/** | |
* Updates properties. | |
*/ | |
World.prototype.step = function() {}; | |
/** | |
* Updates the corresponding DOM element's style property. | |
*/ | |
World.prototype.draw = function() { | |
exports.System._draw(this); | |
}; | |
exports.World = World; | |
}(exports)); | |
(function(exports) { | |
/** | |
* Creates a new Item. | |
* | |
* @param {Object} options A map of initial properties. | |
* @constructor | |
*/ | |
function Item(options) { | |
if (!options || !options.world || typeof options.world !== 'object') { | |
throw new Error('Item: A valid DOM object is required for a new Item.'); | |
} | |
this.world = options.world; | |
this.name = options.name || 'Item'; | |
this.id = this.name + exports.System.getNewId(); | |
this.el = document.createElement('div'); | |
this.el.id = this.id; | |
this.el.className = 'item ' + this.name.toLowerCase(); | |
this.el.style.visibility = 'hidden'; | |
this.world.el.appendChild(this.el); | |
} | |
/** | |
* Initializes the object. | |
* @param {Object} options= A map of initial properties. | |
*/ | |
Item.prototype.init = function(opt_options) { | |
var options = opt_options || {}; | |
this.acceleration = options.acceleration || new exports.Vector(); | |
this.velocity = options.velocity || new exports.Vector(); | |
this.location = options.location || new exports.Vector(this.world.width / 2, this.world.height / 2); | |
this.width = options.width || 20; | |
this.height = options.height || 20; | |
this.mass = (this.width * this.height) * 0.025; | |
this.color = options.color || [0, 0, 0]; | |
this.visibility = options.visibility || 'visible'; | |
this.maxSpeed = options.maxSpeed || 5; | |
this.bounciness = options.bounciness || 0.75; | |
this.borderRadius = options.borderRadius || 0; | |
this.angle = options.angle || 0; | |
this.checkWorldEdges = options.checkWorldEdges === undefined ? true : options.checkWorldEdges; | |
this.controlCamera = options.controlCamera === undefined ? false : options.controlCamera; | |
}; | |
/** | |
* Updates properties. | |
*/ | |
Item.prototype.step = function() { | |
this.applyForce(this.world.wind); | |
this.applyForce(this.world.thermal); | |
this.applyForce(this.world.gravity); | |
this.velocity.add(this.acceleration); | |
this.velocity.limit(this.maxSpeed); | |
if (this.checkWorldEdges) { | |
this._checkWorldEdges(); | |
} | |
if (this.controlCamera) { | |
this._checkCameraEdges(); | |
} | |
this.location.add(this.velocity); | |
this.angle = this.location.x; | |
this.acceleration.mult(0); | |
}; | |
/** | |
* Adds a force to this object's acceleration. | |
* | |
* @param {Object} force A Vector representing a force to apply. | |
*/ | |
Item.prototype.applyForce = function(force) { | |
var vector = this.world.cacheVector; | |
vector.x = force.x; | |
vector.y = force.y; | |
vector.div(this.mass); | |
this.acceleration.add(vector); | |
}; | |
/** | |
* Determines if this object is outside the world bounds. | |
* @private | |
*/ | |
Item.prototype._checkWorldEdges = function() { | |
var world = this.world, | |
location = this.location, | |
velocity = this.velocity, | |
width = this.width, | |
height = this.height, | |
bounciness = this.bounciness; | |
if (location.x + width / 2 > world.width) { | |
location.x = world.width - width / 2; | |
velocity.x *= -1 * bounciness; | |
} else if (location.x < width / 2) { | |
location.x = width / 2; | |
velocity.x *= -1 * bounciness; | |
} | |
if (location.y + height / 2 > world.height) { | |
location.y = world.height - height / 2; | |
velocity.y *= -1 * bounciness; | |
} else if (location.y < height / 2) { | |
location.y = height / 2; | |
velocity.y *= -1 * bounciness; | |
} | |
}; | |
/** | |
* Moves the world in the opposite direction of the item. | |
*/ | |
Item.prototype._checkCameraEdges = function() { | |
this.world.camera.x = this.velocity.x; | |
this.world.camera.y = this.velocity.y; | |
this.world.location.add(this.world.camera.mult(-1)); | |
}; | |
/** | |
* Updates the corresponding DOM element's style property. | |
*/ | |
Item.prototype.draw = function() { | |
exports.System._draw(this); | |
}; | |
exports.Item = Item; | |
}(exports)); | |
(function(exports) { | |
var Utils = {}; | |
/** | |
* Determines the size of the browser viewport. | |
* | |
* @returns {Object} The current browser viewport width and height. | |
* @private | |
*/ | |
Utils.getViewportSize = function() { | |
var d = {}; | |
if (typeof(window.innerWidth) !== 'undefined') { | |
d.width = window.innerWidth; | |
d.height = window.innerHeight; | |
} else if (typeof(document.documentElement) !== 'undefined' && | |
typeof(document.documentElement.clientWidth) !== 'undefined') { | |
d.width = document.documentElement.clientWidth; | |
d.height = document.documentElement.clientHeight; | |
} else if (typeof(document.body) !== 'undefined') { | |
d.width = document.body.clientWidth; | |
d.height = document.body.clientHeight; | |
} else { | |
d.width = undefined; | |
d.height = undefined; | |
} | |
return d; | |
}; | |
/** | |
* Adds an event listener. | |
* | |
* @param {Object} target The element to receive the event listener. | |
* @param {string} eventType The event type. | |
* @param {function} The function to run when the event is triggered. | |
* @private | |
*/ | |
Utils._addEvent = function(target, eventType, handler) { | |
if (target.addEventListener) { // W3C | |
target.addEventListener(eventType, handler, false); | |
} else if (target.attachEvent) { // IE | |
target.attachEvent("on" + eventType, handler); | |
} | |
}; | |
/** | |
* Extends the properties and methods of a superClass onto a subClass. | |
* | |
* @param {Object} subClass The subClass. | |
* @param {Object} superClass The superClass. | |
*/ | |
Utils.extend = function(subClass, superClass) { | |
function F() {} | |
F.prototype = superClass.prototype; | |
subClass.prototype = new F; | |
subClass.prototype.constructor = subClass; | |
}; | |
/** | |
* Generates a psuedo-random number within a range. | |
* | |
* @param {number} low The low end of the range. | |
* @param {number} high The high end of the range. | |
* @param {boolean} [flt] Set to true to return a float. | |
* @returns {number} A number. | |
*/ | |
Utils.getRandomNumber = function(low, high, flt) { | |
if (flt) { | |
return Math.random()*(high-(low-1)) + low; | |
} | |
return Math.floor(Math.random()*(high-(low-1))) + low; | |
}; | |
exports.Utils = Utils; | |
}(exports)); | |
(function(exports) { | |
var Classes = {}; | |
exports.Classes = Classes; | |
}(exports)); | |
(function(exports) { | |
/** | |
* Creates a new Vector. | |
* | |
* @param {number} [opt_x = 0] The x location. | |
* @param {number} [opt_y = 0] The y location. | |
* @constructor | |
*/ | |
function Vector(opt_x, opt_y) { | |
var x = opt_x || 0, | |
y = opt_y || 0; | |
this.x = x; | |
this.y = y; | |
} | |
/** | |
* Adds a vector to this vector. | |
* | |
* @param {Object} vector The vector to add. | |
* @returns {Object} This vector. | |
*/ | |
Vector.prototype.add = function(vector) { | |
this.x += vector.x; | |
this.y += vector.y; | |
return this; | |
}; | |
/** | |
* Subtracts a vector from this vector. | |
* | |
* @param {Object} vector The vector to subtract. | |
* @returns {Object} This vector. | |
*/ | |
Vector.prototype.sub = function(vector) { | |
this.x -= vector.x; | |
this.y -= vector.y; | |
return this; | |
}; | |
/** | |
* Multiplies this vector by a passed value. | |
* | |
* @param {number} n Vector will be multiplied by this number. | |
* @returns {Object} This vector. | |
*/ | |
Vector.prototype.mult = function(n) { | |
this.x *= n; | |
this.y *= n; | |
return this; | |
}; | |
/** | |
* Divides this vector by a passed value. | |
* | |
* @param {number} n Vector will be divided by this number. | |
* @returns {Object} This vector. | |
*/ | |
Vector.prototype.div = function(n) { | |
this.x = this.x / n; | |
this.y = this.y / n; | |
return this; | |
}; | |
/** | |
* Calculates the magnitude of this vector. | |
* | |
* @returns {number} The vector's magnitude. | |
*/ | |
Vector.prototype.mag = function() { | |
return Math.sqrt((this.x * this.x) + (this.y * this.y)); | |
}; | |
/** | |
* Limits the vector's magnitude. | |
* | |
* @param {number} opt_high The upper bound of the vector's magnitude | |
* @param {number} opt_low The lower bound of the vector's magnitude. | |
* @returns {Object} This vector. | |
*/ | |
Vector.prototype.limit = function(opt_high, opt_low) { | |
var high = opt_high || null, | |
low = opt_low || null; | |
if (high && this.mag() > high) { | |
this.normalize(); | |
this.mult(high); | |
} | |
if (low && this.mag() < low) { | |
this.normalize(); | |
this.mult(low); | |
} | |
return this; | |
}; | |
/** | |
* Divides a vector by its magnitude to reduce its magnitude to 1. | |
* Typically used to retrieve the direction of the vector for later manipulation. | |
* | |
* @returns {Object} This vector. | |
*/ | |
Vector.prototype.normalize = function() { | |
var m = this.mag(); | |
if (m !== 0) { | |
return this.div(m); | |
} | |
}; | |
/** | |
* Rotates a vector using a passed angle in radians. | |
* | |
* @param {number} radians The angle to rotate in radians. | |
* @returns {Object} This vector. | |
*/ | |
Vector.prototype.rotate = function(radians) { | |
var cos = Math.cos(radians), | |
sin = Math.sin(radians), | |
x = this.x, | |
y = this.y; | |
this.x = x * cos - y * sin; | |
this.y = x * sin + y * cos; | |
return this; | |
}; | |
exports.Vector = Vector; | |
}(exports)); | |
/** | |
* RequestAnimationFrame shim layer with setTimeout fallback | |
* @param {function} callback The function to call. | |
* @returns {function|Object} An animation frame or a timeout object. | |
*/ | |
window.requestAnimFrame = (function(callback){ | |
return window.requestAnimationFrame || | |
window.webkitRequestAnimationFrame || | |
window.mozRequestAnimationFrame || | |
window.oRequestAnimationFrame || | |
window.msRequestAnimationFrame || | |
function(callback) { | |
window.setTimeout(callback, 1000 / 60); | |
}; | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment