|
// Generated by CoffeeScript 1.7.1 |
|
(function() { |
|
var $z, |
|
__slice = [].slice, |
|
__hasProp = {}.hasOwnProperty, |
|
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, |
|
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; |
|
|
|
$z = {}; |
|
|
|
$z.Factory = (function() { |
|
function Factory() {} |
|
|
|
Factory.inactive = {}; |
|
|
|
Factory.spawn = function(klass, config, callback) { |
|
var old; |
|
if (this.inactive[klass] === void 0) { |
|
this.inactive[klass] = []; |
|
} |
|
if (this.inactive[klass].length === 0) { |
|
old = new klass(config); |
|
} else { |
|
old = this.inactive[klass].pop(); |
|
if (old.is_sleeping === false || old.is_removed === false) { |
|
console.log('$z.Factory.spawn: not sleeping & unremoved instance found in inactive list!', old); |
|
$z.Factory.spawn(klass, config); |
|
return; |
|
} |
|
if (old.wake != null) { |
|
old.wake(config); |
|
} else { |
|
$z.Utils.set(old, config); |
|
} |
|
} |
|
if (typeof callback === "function") { |
|
callback(old); |
|
} |
|
return old; |
|
}; |
|
|
|
Factory.sleep = function(instance) { |
|
var _ref; |
|
if (instance === void 0) { |
|
console.log('$z.Factory.sleep(): undefined input', instance); |
|
return; |
|
} |
|
if (instance.is_sleeping === true) { |
|
console.log('$z.Factory.sleep(): sleeping instance', instance, this.inactive[instance.constructor].indexOf(instance)); |
|
return; |
|
} |
|
if ((_ref = this.inactive[instance.constructor]) != null) { |
|
_ref.push(instance); |
|
} |
|
}; |
|
|
|
return Factory; |
|
|
|
})(); |
|
|
|
$z.Gamescore = (function() { |
|
function Gamescore() {} |
|
|
|
Gamescore.value = 0; |
|
|
|
Gamescore.increment = 100; |
|
|
|
Gamescore.initialLives = 2; |
|
|
|
Gamescore.lives = Gamescore.initialLives; |
|
|
|
Gamescore.increment_value = function() { |
|
return this.value += this.increment; |
|
}; |
|
|
|
Gamescore.decrement_value = function() { |
|
return this.value -= this.increment; |
|
}; |
|
|
|
return Gamescore; |
|
|
|
})(); |
|
|
|
$z.ImageLoader = (function() { |
|
function ImageLoader() {} |
|
|
|
ImageLoader.loading = false; |
|
|
|
ImageLoader.cache = {}; |
|
|
|
ImageLoader.loadingStats = { |
|
total: null, |
|
count: null, |
|
finalCallback: null |
|
}; |
|
|
|
ImageLoader.load = function(url) { |
|
var img; |
|
if (this.cache[url] != null) { |
|
this.callbackHandler(); |
|
} else { |
|
img = new Image(); |
|
img.onload = $z.ImageLoader.callbackHandler; |
|
img.src = url; |
|
this.cache[url] = img; |
|
} |
|
return img; |
|
}; |
|
|
|
ImageLoader.callbackHandler = function() { |
|
$z.ImageLoader.loadingStats.count++; |
|
if ($z.ImageLoader.loadingStats.count === $z.ImageLoader.loadingStats.total) { |
|
$z.ImageLoader.loadingStats.finalCallback(); |
|
$z.ImageLoader.loading = false; |
|
} |
|
}; |
|
|
|
ImageLoader.preload = function(imageList, callback) { |
|
var url, _i, _len; |
|
if (this.loading) { |
|
return; |
|
} |
|
this.loading = true; |
|
this.loadingStats.total = imageList.length; |
|
this.loadingStats.count = 0; |
|
this.loadingStats.finalCallback = callback; |
|
for (_i = 0, _len = imageList.length; _i < _len; _i++) { |
|
url = imageList[_i]; |
|
this.load(url); |
|
} |
|
}; |
|
|
|
return ImageLoader; |
|
|
|
})(); |
|
|
|
$z.Utils = (function() { |
|
function Utils() {} |
|
|
|
Utils.fullscreen = function() { |
|
var elem; |
|
elem = document.body.parentNode; |
|
if (elem.requestFullscreen) { |
|
return elem.requestFullscreen(); |
|
} else if (elem.msRequestFullscreen) { |
|
return elem.msRequestFullscreen(); |
|
} else if (elem.mozRequestFullScreen) { |
|
return elem.mozRequestFullScreen(); |
|
} else if (elem.webkitRequestFullscreen) { |
|
return elem.webkitRequestFullscreen(); |
|
} |
|
}; |
|
|
|
Utils.defaultscreen = function() { |
|
var elem; |
|
elem = document; |
|
if (elem.exitFullscreen) { |
|
return elem.exitFullscreen(); |
|
} else if (elem.msExitFullscreen) { |
|
return elem.msExitFullscreen(); |
|
} else if (elem.mozExitFullScreen) { |
|
return elem.mozExitFullScreen(); |
|
} else if (elem.webkitExitFullscreen) { |
|
return elem.webkitExitFullscreen(); |
|
} |
|
}; |
|
|
|
Utils.index_pop = function(array, index) { |
|
var length, swap; |
|
length = array.length; |
|
if (index < array.length - 1) { |
|
swap = array[index]; |
|
array[index] = array[length - 1]; |
|
array[length - 1] = swap; |
|
} |
|
return array.pop(); |
|
}; |
|
|
|
Utils.delayedLoop = function(dur, Niter, callback, finalCallback) { |
|
var i, runLoop; |
|
if (dur == null) { |
|
dur = 1000; |
|
} |
|
if (finalCallback == null) { |
|
finalCallback = void 0; |
|
} |
|
i = 0; |
|
runLoop = function() { |
|
callback(i); |
|
if (++i === Niter) { |
|
if (typeof finalCallback === "function") { |
|
finalCallback(); |
|
} |
|
return; |
|
} |
|
return setTimeout(runLoop, dur); |
|
}; |
|
return runLoop(); |
|
}; |
|
|
|
Utils.set = function(obj, config) { |
|
var x, _results; |
|
_results = []; |
|
for (x in config) { |
|
_results.push(obj[x] = config[x]); |
|
} |
|
return _results; |
|
}; |
|
|
|
Utils.clone = function(obj) { |
|
var key, temp; |
|
if (obj === null || typeof obj !== "object") { |
|
return obj; |
|
} |
|
temp = new obj.constructor(); |
|
for (key in obj) { |
|
temp[key] = $z.Utils.clone(obj[key]); |
|
} |
|
return temp; |
|
}; |
|
|
|
Utils.addChainedAttributeAccessor = function(obj, attr) { |
|
return obj[attr] = function() { |
|
var newValues; |
|
newValues = 1 <= arguments.length ? __slice.call(arguments, 0) : []; |
|
if (newValues.length === 0) { |
|
return obj['_' + attr]; |
|
} else { |
|
obj['_' + attr] = newValues[0]; |
|
obj.image.attr(attr, obj['_' + attr]); |
|
return obj; |
|
} |
|
}; |
|
}; |
|
|
|
Utils.timestamp = function() { |
|
return Date.now(); |
|
}; |
|
|
|
Utils.angle = function(a) { |
|
return 2 * Math.PI * a / 360; |
|
}; |
|
|
|
Utils.path_seg = function(p) { |
|
var a; |
|
a = p.pathSegTypeAsLetter; |
|
switch (a) { |
|
case 'M': |
|
case 'm': |
|
return [a, p.x, p.y].join(" "); |
|
case 'L': |
|
case 'l': |
|
return [a, p.x, p.y].join(" "); |
|
case 'A': |
|
case 'a': |
|
return [a, p.r, p.r, p.rot, p.c, p.d, p.x, p.y].join(" "); |
|
case 'C': |
|
case 'c': |
|
return [a, p.x1, p.y1, p.x2, p.y2, p.x, p.y].join(" "); |
|
case 'S': |
|
case 's': |
|
return [a, p.x2, p.y2, p.x, p.y].join(" "); |
|
case 'Q': |
|
case 'q': |
|
return [a, p.x1, p.y1, p.x, p.y].join(" "); |
|
case 'T': |
|
case 't': |
|
return [a, p.x, p.y].join(" "); |
|
case 'Z': |
|
case 'z': |
|
return a; |
|
} |
|
}; |
|
|
|
Utils.d = function(path) { |
|
var p; |
|
return ((function() { |
|
var _i, _len, _results; |
|
_results = []; |
|
for (_i = 0, _len = path.length; _i < _len; _i++) { |
|
p = path[_i]; |
|
_results.push(this.path_seg(p)); |
|
} |
|
return _results; |
|
}).call(this)).join(" "); |
|
}; |
|
|
|
Utils.pathTween = function(d, i, a) { |
|
var interp, prec; |
|
prec = 4; |
|
interp = function(d, path) { |
|
var distances, dt, n0, n1, p, points; |
|
n0 = path.getTotalLength(); |
|
p = path.cloneNode(); |
|
p.setAttribute("d", d); |
|
n1 = p.getTotalLength(); |
|
distances = [0]; |
|
i = 0; |
|
dt = prec / Math.max(n0, n1); |
|
while ((i += dt) < 1) { |
|
distances.push(i); |
|
} |
|
distances.push(1); |
|
points = distances.map(function(t) { |
|
var p0, p1; |
|
p0 = path.getPointAtLength(t * n0); |
|
p1 = p.getPointAtLength(t * n1); |
|
return d3.interpolate([p0.x, p0.y], [p1.x, p1.y]); |
|
}); |
|
return function(t) { |
|
if (t < 1) { |
|
return "M" + points.map(function(p) { |
|
return p(t); |
|
}).join("L"); |
|
} else { |
|
return d; |
|
} |
|
}; |
|
}; |
|
return interp(d, this); |
|
}; |
|
|
|
Utils.bilinear_interp = function(matrix, x, y) { |
|
var dxc, dxf, dyc, dyf, interp, tol, xc, xf, yc, yf; |
|
tol = 1e-100; |
|
xf = Math.floor(x); |
|
xc = Math.ceil(x + tol); |
|
yf = Math.floor(y); |
|
yc = Math.ceil(y + tol); |
|
dxf = x - xf; |
|
dxc = xc - x; |
|
dyf = y - yf; |
|
dyc = yc - y; |
|
interp = matrix[yf][xf] * dxc * dyc + matrix[yf][xc] * dxf * dyc + matrix[yc][xf] * dxc * dyf + matrix[yc][xc] * dxf * dyf; |
|
return interp; |
|
}; |
|
|
|
return Utils; |
|
|
|
})(); |
|
|
|
$z.Element = (function() { |
|
function Element(config) { |
|
this.config = config != null ? config : {}; |
|
this.d = $z.Factory.spawn($z.Vec); |
|
this.ri = $z.Factory.spawn($z.Vec); |
|
this.rj = $z.Factory.spawn($z.Vec); |
|
this.r_temp = $z.Factory.spawn($z.Vec); |
|
this.dr_temp = $z.Factory.spawn($z.Vec); |
|
this.line = $z.Factory.spawn($z.Vec); |
|
this.normal = $z.Factory.spawn($z.Vec); |
|
this.lshift = $z.Factory.spawn($z.Vec); |
|
this.vPar = $z.Factory.spawn($z.Vec); |
|
this.vPerp = $z.Factory.spawn($z.Vec); |
|
this.uPar = $z.Factory.spawn($z.Vec); |
|
this.uPerp = $z.Factory.spawn($z.Vec); |
|
this.dt = this.config.dt || 0.25; |
|
this.r = this.config.r || $z.Factory.spawn($z.Vec); |
|
this.dr = this.config.dr || $z.Factory.spawn($z.Vec); |
|
this.v = this.config.v || $z.Factory.spawn($z.Vec); |
|
this.f = this.config.f || $z.Factory.spawn($z.Vec); |
|
this.fcopy = $z.Utils.clone(this.config.f) || $z.Factory.spawn($z.Vec); |
|
this.force_param = this.config.force_param || []; |
|
this.size = this.config.size || 0; |
|
this.bb_width = this.config.bb_width || 0; |
|
this.bb_height = this.config.bb_height || 0; |
|
this.left = this.config.bb_width || 0; |
|
this.right = this.config.bb_height || 0; |
|
this.top = this.config.top || 0; |
|
this.bottom = this.config.bottom || 0; |
|
this.collision = this.config.collision || true; |
|
this.tol = this.config.tol || 0.25; |
|
this._stroke = this.config.stroke || "none"; |
|
this._fill = this.config.fill || "black"; |
|
this.angle = this.config.angle || 0; |
|
this.is_root = this.config.is_root || false; |
|
this.is_bullet = this.config.is_bullet || false; |
|
this.type = this.config.type || null; |
|
this.image = this.config.image || null; |
|
this.overlay = this.config.overlay || null; |
|
this.g = d3.select("#game_g").append("g").attr("transform", "translate(" + this.r.x + "," + this.r.y + ")").style('opacity', 0).datum(this); |
|
this.g = this.config.g || this.g; |
|
this.svg = this.config.svg || $z.Game.instance.svg; |
|
this.game_g = this.config.game_g || $z.Game.instance.g; |
|
this.quadtree = this.config.quadtree || null; |
|
this.tick = this.config.tick || $z.Physics.euler; |
|
this.is_removed = false; |
|
this.is_sleeping = false; |
|
this.is_flashing = false; |
|
this._cleanup = true; |
|
$z.Utils.addChainedAttributeAccessor(this, 'fill'); |
|
$z.Utils.addChainedAttributeAccessor(this, 'stroke'); |
|
} |
|
|
|
Element.prototype.reaction = function(element) { |
|
return element != null ? element.reaction() : void 0; |
|
}; |
|
|
|
Element.prototype.BB = function() { |
|
this.left = this.r.x - 0.5 * this.bb_width; |
|
this.right = this.r.x + 0.5 * this.bb_width; |
|
this.top = this.r.y - 0.5 * this.bb_height; |
|
return this.bottom = this.r.y + 0.5 * this.bb_height; |
|
}; |
|
|
|
Element.prototype.draw = function() { |
|
this.g.attr("transform", "translate(" + this.r.x + "," + this.r.y + ") rotate(" + (360 * 0.5 * this.angle / Math.PI) + ")"); |
|
}; |
|
|
|
Element.prototype.remove_check = function(n) { |
|
if (this.is_root || this.is_bullet) { |
|
this.reaction(n); |
|
return true; |
|
} |
|
return false; |
|
}; |
|
|
|
Element.prototype.offscreen = function() { |
|
return this.r.x < -this.size || this.r.y < -this.size || this.r.x > $z.Game.width + this.size || this.r.y > $z.Game.height + this.size; |
|
}; |
|
|
|
Element.prototype.fadeIn = function(dur, callback) { |
|
if (dur == null) { |
|
dur = 30; |
|
} |
|
if (dur != null) { |
|
return this.g.transition().duration(dur).ease('linear').style("opacity", 1).each('end', function(d) { |
|
return typeof callback === "function" ? callback(d) : void 0; |
|
}); |
|
} else { |
|
this.g.style("opacity", 1); |
|
return typeof callback === "function" ? callback(this) : void 0; |
|
} |
|
}; |
|
|
|
Element.prototype.fadeOut = function(dur, callback) { |
|
if (dur == null) { |
|
dur = 30; |
|
} |
|
return this.g.transition().duration(dur).ease('linear').style("opacity", 0).each('end', function(d) { |
|
return typeof callback === "function" ? callback(d) : void 0; |
|
}); |
|
}; |
|
|
|
Element.prototype.flash = function(dur, color, scaleFactor, initialOpacity) { |
|
if (dur == null) { |
|
dur = 1000; |
|
} |
|
if (color == null) { |
|
color = '#FFF'; |
|
} |
|
if (scaleFactor == null) { |
|
scaleFactor = 3; |
|
} |
|
if (initialOpacity == null) { |
|
initialOpacity = 0.4; |
|
} |
|
if (this.is_flashing) { |
|
return; |
|
} |
|
this.is_flashing = true; |
|
return this.overlay.style('fill', color).style('opacity', initialOpacity).transition().duration(dur).attr('transform', 'scale(' + scaleFactor + ')').style('opacity', 0).ease('linear').each('end', (function(_this) { |
|
return function() { |
|
_this.overlay.attr('transform', 'scale(1)'); |
|
return _this.is_flashing = false; |
|
}; |
|
})(this)); |
|
}; |
|
|
|
Element.prototype.start = function(duration, callback) { |
|
var index; |
|
if (duration == null) { |
|
duration = void 0; |
|
} |
|
if (callback == null) { |
|
callback = (function(_this) { |
|
return function() { |
|
return _this.collision = true; |
|
}; |
|
})(this); |
|
} |
|
if (this.is_sleeping) { |
|
console.log('element.start: is_sleeping... bug?'); |
|
return; |
|
} |
|
index = $z.Collision.list.indexOf(this); |
|
if (index === -1) { |
|
$z.Collision.list.push(this); |
|
} else { |
|
console.log('element.start: this element is already on the physics list! bug?'); |
|
} |
|
this.is_removed = false; |
|
this.draw(); |
|
this.fadeIn(duration, callback); |
|
}; |
|
|
|
Element.prototype.cleanup = function(_cleanup) { |
|
this._cleanup = _cleanup != null ? _cleanup : this._cleanup; |
|
if (this._cleanup && this.offscreen()) { |
|
return this.remove(); |
|
} |
|
}; |
|
|
|
Element.prototype.sleep = function() { |
|
$z.Factory.sleep(this); |
|
this.is_sleeping = true; |
|
}; |
|
|
|
Element.prototype.remove = function(dur) { |
|
if (dur == null) { |
|
dur = 30; |
|
} |
|
if (this.is_removed || !this.collision) { |
|
return; |
|
} |
|
this.collision = false; |
|
if (dur > 0) { |
|
this.fadeOut(dur, (function(d) { |
|
return d.is_removed = true; |
|
})); |
|
} else { |
|
this.is_removed = true; |
|
} |
|
}; |
|
|
|
Element.prototype.spawn = function() { |
|
this.wake(); |
|
this.start(); |
|
return this; |
|
}; |
|
|
|
Element.prototype.init = function() { |
|
this.r.x = 0; |
|
this.r.y = 0; |
|
this.dr.x = 0; |
|
this.dr.y = 0; |
|
this.v.x = 0; |
|
this.v.y = 0; |
|
this.f.x = 0; |
|
this.f.y = 0; |
|
return this; |
|
}; |
|
|
|
Element.prototype.wake = function(config) { |
|
this.is_sleeping = false; |
|
this.init(); |
|
if (config != null) { |
|
$z.Utils.set(this, config); |
|
} |
|
return this; |
|
}; |
|
|
|
Element.prototype.scale = function(scalingFactor, dur, callback) { |
|
if (scalingFactor == null) { |
|
scalingFactor = 10; |
|
} |
|
if (dur == null) { |
|
dur = void 0; |
|
} |
|
if (callback == null) { |
|
callback = function() {}; |
|
} |
|
if (dur != null) { |
|
return this.image.transition().duration(dur).ease('linear').attr('transform', 'scale(' + scalingFactor + ')').each('end', callback); |
|
} else { |
|
this.image.attr('transform', 'scale(' + scalingFactor + ')'); |
|
return callback(); |
|
} |
|
}; |
|
|
|
return Element; |
|
|
|
})(); |
|
|
|
$z.Game = (function() { |
|
var current_height, current_width, get_scale, image_preload_callback; |
|
|
|
Game.width = null; |
|
|
|
Game.height = null; |
|
|
|
Game.scale = 1; |
|
|
|
Game.audioSwitch = true; |
|
|
|
Game.musicSwitch = true; |
|
|
|
Game.instance = null; |
|
|
|
Game.message_color = "#FFF"; |
|
|
|
Game.width = 800; |
|
|
|
Game.height = 600; |
|
|
|
Game.maxdim = Math.max(Game.width, Game.height); |
|
|
|
function Game(config) { |
|
this.config = config != null ? config : {}; |
|
this.images_loaded = false; |
|
this.element = []; |
|
this.div = d3.select("#game_div"); |
|
this.svg = d3.select("#game_svg"); |
|
if (this.svg.empty()) { |
|
this.svg = this.div.append('svg').attr('id', 'game_svg'); |
|
} |
|
this.svg.attr("viewBox", '0 0 ' + $z.Game.width + ' ' + $z.Game.height).attr("preserveAspectRatio", "xMidYMin meet").attr('width', '100%').style('max-height', '100%'); |
|
this.scale = 1; |
|
this.g = d3.select("#game_g"); |
|
if (this.g.empty()) { |
|
this.g = this.svg.append('g'); |
|
} |
|
this.g.attr('id', 'game_g'); |
|
$z.Game.instance = this; |
|
$z.Game.instance.div.style('opacity', 0); |
|
this.preload_images(); |
|
} |
|
|
|
image_preload_callback = function() { |
|
var dur; |
|
$z.Game.instance.images_loaded = true; |
|
$z.Game.instance.start(); |
|
dur = 1000; |
|
return $z.Game.instance.div.transition().duration(dur).style('opacity', 1); |
|
}; |
|
|
|
Game.prototype.preload_images = function(image_list, preload_callback) { |
|
var dur; |
|
if (image_list == null) { |
|
image_list = $z.Game.instance.image_list; |
|
} |
|
if ((image_list != null) && (image_list.length != null) && image_list.length > 0) { |
|
return $z.ImageLoader.preload(image_list, image_preload_callback); |
|
} else { |
|
dur = 1000; |
|
return $z.Game.instance.div.transition().duration(dur).style('opacity', 1); |
|
} |
|
}; |
|
|
|
current_width = function(padding) { |
|
var element, x; |
|
if (padding == null) { |
|
padding = 8; |
|
} |
|
element = window.top.document.body; |
|
x = $(element).width(); |
|
x = Math.min(x, $(window).width()); |
|
x = Math.min(x, $(window.top).width()); |
|
if (x > padding && padding > 0) { |
|
return x = x - padding; |
|
} |
|
}; |
|
|
|
current_height = function(padding) { |
|
var element, y; |
|
if (padding == null) { |
|
padding = 124; |
|
} |
|
element = window.top; |
|
y = $(element).height(); |
|
if (y > padding && padding > 0) { |
|
y = y - padding; |
|
} |
|
return y; |
|
}; |
|
|
|
get_scale = function() { |
|
var max_scale, min_scale, r1, r2, scale; |
|
r1 = current_width() / $z.Game.width; |
|
r2 = current_height() / $z.Game.height; |
|
scale = r1 <= r2 ? r1 : r2; |
|
max_scale = 1.0; |
|
min_scale = 0.39; |
|
return scale = Math.max(min_scale, Math.min(max_scale, scale)); |
|
}; |
|
|
|
Game.prototype.start = function() { |
|
$z.Physics.start(); |
|
if (typeof Gameprez !== "undefined" && Gameprez !== null) { |
|
Gameprez.start(); |
|
} |
|
}; |
|
|
|
Game.prototype.stop = function(callback) { |
|
if (callback == null) { |
|
callback = function() {}; |
|
} |
|
this.cleanup(); |
|
$z.Physics.stop(); |
|
if (typeof Gameprez !== "undefined" && Gameprez !== null) { |
|
Gameprez.end($z.Gamescore.value, callback); |
|
} else { |
|
callback(); |
|
} |
|
}; |
|
|
|
Game.prototype.cleanup = function() { |
|
this.g.selectAll('g').each(function(d) { |
|
return d != null ? d.remove() : void 0; |
|
}); |
|
}; |
|
|
|
Game.prototype.message = function(txt, callback, dur) { |
|
var ready, spacing; |
|
if (dur == null) { |
|
dur = 1000; |
|
} |
|
callback || (callback = function() {}); |
|
this.g.selectAll('.game_message').remove(); |
|
spacing = 10; |
|
ready = this.g.append("text").attr('class', 'game_message').text(txt).attr("stroke", "none").attr("fill", $z.Game.message_color).attr("font-size", "36").attr("x", $z.Game.width / 2 - txt.length * spacing).attr("y", $z.Game.height / 2 + 20).attr('font-family', 'arial').attr('font-weight', 'bold').attr('opacity', 0).transition().duration(dur).style("opacity", 1).transition().duration(dur).style('opacity', 0).remove().each('end', callback); |
|
}; |
|
|
|
return Game; |
|
|
|
})(); |
|
|
|
$z.Circle = (function(_super) { |
|
__extends(Circle, _super); |
|
|
|
function Circle(config) { |
|
var _base; |
|
this.config = config != null ? config : {}; |
|
(_base = this.config).size || (_base.size = 15); |
|
Circle.__super__.constructor.call(this, this.config); |
|
this.type = 'Circle'; |
|
this.BB(); |
|
this.image = this.g.append("circle").attr("r", this.size).attr("x", 0).attr("y", 0); |
|
this.overlay = this.g.append("circle").style('opacity', 0).attr("r", this.size).attr("x", 0).attr("y", 0); |
|
this.stroke(this._stroke); |
|
this.fill(this._fill); |
|
} |
|
|
|
Circle.prototype.draw = function() { |
|
Circle.__super__.draw.apply(this, arguments); |
|
return this.image.attr("r", this.size); |
|
}; |
|
|
|
Circle.prototype.BB = function(size) { |
|
this.size = size != null ? size : this.size; |
|
this.bb_width = 2 * this.size; |
|
this.bb_height = 2 * this.size; |
|
return Circle.__super__.BB.apply(this, arguments); |
|
}; |
|
|
|
return Circle; |
|
|
|
})($z.Element); |
|
|
|
$z.Polygon = (function(_super) { |
|
__extends(Polygon, _super); |
|
|
|
function Polygon(config) { |
|
this.config = config != null ? config : {}; |
|
Polygon.__super__.constructor.call(this, this.config); |
|
this.type = 'Polygon'; |
|
this.path = this.config.path || this.default_path(); |
|
this.image = this.g.append("path"); |
|
this.overlay = this.g.append("path").style('opacity', 0).attr("x", 0).attr("y", 0); |
|
this.fill(this._fill); |
|
this.stroke(this._stroke); |
|
this.set_path(); |
|
} |
|
|
|
Polygon.prototype.default_path = function() { |
|
var invsqrt3; |
|
invsqrt3 = 1 / Math.sqrt(3); |
|
return [ |
|
{ |
|
pathSegTypeAsLetter: 'M', |
|
x: -this.size, |
|
y: this.size * invsqrt3, |
|
react: true |
|
}, { |
|
pathSegTypeAsLetter: 'L', |
|
x: 0, |
|
y: -2 * this.size * invsqrt3, |
|
react: true |
|
}, { |
|
pathSegTypeAsLetter: 'L', |
|
x: this.size, |
|
y: this.size * invsqrt3, |
|
react: true |
|
}, { |
|
pathSegTypeAsLetter: 'Z' |
|
} |
|
]; |
|
}; |
|
|
|
Polygon.prototype.d_attr = function() { |
|
return $z.Utils.d(this.path); |
|
}; |
|
|
|
Polygon.prototype.polygon_path = function() { |
|
var i, _i, _ref; |
|
for (i = _i = 0, _ref = this.path.length - 2; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) { |
|
this.path[i].r = $z.Factory.spawn($z.Vec, this.path[i]).subtract(this.path[(i + 1) % (this.path.length - 1)]); |
|
this.path[i].n = $z.Factory.spawn($z.Vec, { |
|
x: -this.path[i].r.y, |
|
y: this.path[i].r.x |
|
}).normalize(); |
|
} |
|
this.BB(); |
|
}; |
|
|
|
Polygon.prototype.set_path = function(path) { |
|
var i, maxd, maxnode, node, _i, _ref; |
|
this.path = path != null ? path : this.path; |
|
this.pathref = this.path.map(function(d) { |
|
return $z.Utils.clone(d); |
|
}); |
|
this.polygon_path(); |
|
maxnode = this.path[0]; |
|
this.path[0].d = maxnode.x * maxnode.x + maxnode.y * maxnode.y; |
|
maxd = this.path[0].d; |
|
for (i = _i = 1, _ref = this.path.length - 2; 1 <= _ref ? _i <= _ref : _i >= _ref; i = 1 <= _ref ? ++_i : --_i) { |
|
node = this.path[i]; |
|
node.d = node.x * node.x + node.y * node.y; |
|
if (node.d > maxd) { |
|
maxnode = this.path[i]; |
|
} |
|
} |
|
this.maxnode = $z.Factory.spawn($z.Vec, maxnode); |
|
this.size = this.maxnode.length(); |
|
this.image.attr("d", this.d_attr()); |
|
return this.overlay.attr("d", this.d_attr()); |
|
}; |
|
|
|
Polygon.prototype.BB = function() { |
|
var i, xmax, xmin, ymax, ymin, _i, _ref; |
|
xmax = this.path[0].x; |
|
ymax = this.path[0].y; |
|
xmin = xmax; |
|
ymin = ymax; |
|
for (i = _i = 1, _ref = this.path.length - 1; 1 <= _ref ? _i < _ref : _i > _ref; i = 1 <= _ref ? ++_i : --_i) { |
|
if (this.path[i].x > xmax) { |
|
xmax = this.path[i].x; |
|
} |
|
if (this.path[i].x < xmin) { |
|
xmin = this.path[i].x; |
|
} |
|
if (this.path[i].y > ymax) { |
|
ymax = this.path[i].y; |
|
} |
|
if (this.path[i].y < ymin) { |
|
ymin = this.path[i].y; |
|
} |
|
} |
|
this.bb_width = xmax - xmin; |
|
this.bb_height = ymax - ymin; |
|
return Polygon.__super__.BB.apply(this, arguments); |
|
}; |
|
|
|
Polygon.prototype.rotate_path = function() { |
|
var c, i, s, seg, _i, _ref; |
|
for (i = _i = 0, _ref = this.path.length - 1; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) { |
|
seg = this.path[i]; |
|
if (seg.x == null) { |
|
continue; |
|
} |
|
c = Math.cos(this.angle); |
|
s = Math.sin(this.angle); |
|
seg.x = c * this.pathref[i].x - s * this.pathref[i].y; |
|
seg.y = s * this.pathref[i].x + c * this.pathref[i].y; |
|
} |
|
this.polygon_path(); |
|
}; |
|
|
|
return Polygon; |
|
|
|
})($z.Element); |
|
|
|
$z.Collision = (function() { |
|
var circle_circle_dist, circle_lineseg_dist, lineseg_intersect, name, nearest_node, sort, z_check; |
|
|
|
function Collision() {} |
|
|
|
Collision.use_bb = false; |
|
|
|
Collision.list = []; |
|
|
|
Collision.update_quadtree = function(force_update) { |
|
var data; |
|
if (force_update == null) { |
|
force_update = false; |
|
} |
|
if (!(this.list.length > 0)) { |
|
return; |
|
} |
|
data = this.list.filter(function(d) { |
|
return d.collision; |
|
}).map(function(d) { |
|
return { |
|
x: d.r.x, |
|
y: d.r.y, |
|
d: d |
|
}; |
|
}); |
|
return this.quadtree = d3.geom.quadtree(data); |
|
}; |
|
|
|
Collision.quadtree = Collision.update_quadtree(); |
|
|
|
Collision.resolve = function(m, n) { |
|
var iter, maxiter, reaction, _results; |
|
maxiter = 32; |
|
iter = 1; |
|
reaction = false; |
|
_results = []; |
|
while ($z.Collision.check(m, n, reaction) && iter <= maxiter) { |
|
m.tick(); |
|
n.tick(); |
|
_results.push(iter++); |
|
} |
|
return _results; |
|
}; |
|
|
|
Collision.detect = function() { |
|
var d, i, length, size, x0, x3, y0, y3, _results; |
|
if (!(this.list.length > 0)) { |
|
return; |
|
} |
|
this.update_quadtree(); |
|
length = this.list.length; |
|
i = 0; |
|
_results = []; |
|
while (i < length) { |
|
d = this.list[i]; |
|
if (d.collision) { |
|
size = 2 * (d.size + d.tol); |
|
x0 = d.r.x - size; |
|
x3 = d.r.x + size; |
|
y0 = d.r.y - size; |
|
y3 = d.r.y + size; |
|
this.quadtree.visit(function(node, x1, y1, x2, y2) { |
|
var p; |
|
p = node.point; |
|
if (p !== null) { |
|
if (p.is_removed) { |
|
return false; |
|
} |
|
if (!(d !== p.d && p.d.collision)) { |
|
return false; |
|
} |
|
if ((p.x >= x0) && (p.x < x3) && (p.y >= y0) && (p.y < y3)) { |
|
$z.Collision.check(d, p.d); |
|
} |
|
} |
|
return x1 >= x3 || y1 >= y3 || x2 < x0 || y2 < y0; |
|
}); |
|
} |
|
length = this.list.length; |
|
_results.push(i++); |
|
} |
|
return _results; |
|
}; |
|
|
|
name = [null, null]; |
|
|
|
sort = [null, null]; |
|
|
|
Collision.check = function(ei, ej, reaction) { |
|
var collision, d, m, n, reaction_type; |
|
if (reaction == null) { |
|
reaction = true; |
|
} |
|
name[0] = ei.type; |
|
name[1] = ej.type; |
|
sort[0] = ei.type; |
|
sort[1] = ej.type; |
|
sort.sort(); |
|
if (name[0] === sort[0] && name[1] === sort[1]) { |
|
m = ei; |
|
n = ej; |
|
} else { |
|
m = ej; |
|
n = ei; |
|
} |
|
m.d.collision = false; |
|
n.d.collision = false; |
|
switch (m.type) { |
|
case 'Circle': |
|
switch (n.type) { |
|
case 'Circle': |
|
d = this.circle_circle(m, n); |
|
reaction_type = 'circle_circle'; |
|
break; |
|
case 'Polygon': |
|
d = this.circle_polygon(m, n); |
|
reaction_type = 'circle_polygon'; |
|
} |
|
break; |
|
case 'Polygon': |
|
switch (n.type) { |
|
case 'Polygon': |
|
d = this.polygon_polygon(m, n); |
|
reaction_type = 'polygon_polygon'; |
|
} |
|
} |
|
if (d.collision && reaction) { |
|
$z.Reaction[reaction_type](m, n, d); |
|
} |
|
collision = d.collision; |
|
return collision; |
|
}; |
|
|
|
Collision.rectangle_rectangle = function(m, n) { |
|
var not_intersect; |
|
m.BB(); |
|
n.BB(); |
|
not_intersect = n.left > m.right || n.right < m.left || n.top > m.bottom || n.bottom < m.top; |
|
return !not_intersect; |
|
}; |
|
|
|
Collision.circle_circle = function(m, n) { |
|
var d; |
|
if (this.use_bb) { |
|
if (this.rectangle_rectangle(m, n)) { |
|
d = circle_circle_dist(m, n); |
|
d.collision = true; |
|
} else { |
|
d = { |
|
collision: false |
|
}; |
|
} |
|
} else { |
|
d = circle_circle_dist(m, n); |
|
d.collision = d.dist <= d.dmin ? true : false; |
|
} |
|
return d; |
|
}; |
|
|
|
Collision.circle_polygon = function(circle, polygon) { |
|
var d, i, _i, _ref; |
|
if (this.use_bb) { |
|
if (this.rectangle_rectangle(circle, polygon)) { |
|
i = nearest_node(polygon, circle); |
|
d = circle_lineseg_dist(circle, polygon, i); |
|
d.i = i; |
|
d.collision = true; |
|
} else { |
|
d = { |
|
collision: false |
|
}; |
|
} |
|
} else { |
|
for (i = _i = 0, _ref = polygon.path.length - 2; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) { |
|
if (!polygon.path[i].react) { |
|
continue; |
|
} |
|
d = circle_lineseg_dist(circle, polygon, i); |
|
if (d.dist > circle.size) { |
|
continue; |
|
} |
|
d.i = i; |
|
d.collision = true; |
|
break; |
|
} |
|
} |
|
return d; |
|
}; |
|
|
|
Collision.polygon_polygon = function(m, n) { |
|
var d, i, j, _i, _j, _ref, _ref1; |
|
if (this.use_bb) { |
|
if (this.rectangle_rectangle(m, n)) { |
|
d = circle_circle_dist(m, n); |
|
d.i = nearest_node(m, n); |
|
d.j = nearest_node(n, m); |
|
d.collision = true; |
|
} else { |
|
d = { |
|
collision: false |
|
}; |
|
} |
|
} else { |
|
d = circle_circle_dist(m, n); |
|
d.collision = false; |
|
if (d.dist <= d.dmin) { |
|
for (i = _i = 0, _ref = m.path.length - 2; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) { |
|
for (j = _j = 0, _ref1 = n.path.length - 2; 0 <= _ref1 ? _j <= _ref1 : _j >= _ref1; j = 0 <= _ref1 ? ++_j : --_j) { |
|
if (!lineseg_intersect(m, n, i, j)) { |
|
continue; |
|
} |
|
d.i = i; |
|
d.j = j; |
|
d.collision = true; |
|
break; |
|
} |
|
if (d.collision) { |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
return d; |
|
}; |
|
|
|
nearest_node = function(m, n) { |
|
var d, i, nn, nnd, node, _i, _ref; |
|
nn = m.path[0]; |
|
nnd = (nn.x + m.r.x - n.r.x) * (nn.x + m.r.x - n.r.x) + (nn.y + m.r.y - n.r.y) * (nn.y + m.r.y - n.r.y); |
|
for (i = _i = 1, _ref = this.path.length - 2; 1 <= _ref ? _i <= _ref : _i >= _ref; i = 1 <= _ref ? ++_i : --_i) { |
|
node = m.path[i]; |
|
d = (node.x + m.r.x - n.r.x) * (node.x + m.r.x - n.r.x) + (node.y + m.r.y - n.r.y) * (node.y + m.r.y - n.r.y); |
|
if (d < nnd) { |
|
nn = m.path[i]; |
|
} |
|
} |
|
return m.path.indexOf(nn); |
|
}; |
|
|
|
circle_circle_dist = function(m, n) { |
|
var d; |
|
d = m.d.init(m.r).subtract(n.r); |
|
d.dist = Math.sqrt(d.x * d.x + d.y * d.y); |
|
d.dmin = m.size + n.size; |
|
return d; |
|
}; |
|
|
|
circle_lineseg_dist = function(circle, polygon, i) { |
|
var d, dr, r, ri, rj, rr, t; |
|
ri = polygon.path[i]; |
|
rj = circle.rj.init(z_check(polygon.path, i)); |
|
r = circle.r_temp.init(circle.rj).subtract(ri); |
|
rr = r.x * r.x + r.y * r.y; |
|
dr = circle.dr_temp.init(circle.r).subtract(ri).subtract(polygon.r); |
|
t = (r.x * dr.x + r.y * dr.y) / rr; |
|
if (t < 0) { |
|
|
|
} else if (t > 1) { |
|
dr.x = circle.r.x - rj.x - polygon.r.x; |
|
dr.y = circle.r.y - rj.y - polygon.r.y; |
|
} else { |
|
dr.x = r.x * t + ri.x + polygon.r.x; |
|
dr.y = r.y * t + ri.y + polygon.r.y; |
|
dr.x *= -1; |
|
dr.y *= -1; |
|
dr.x += circle.r.x; |
|
dr.y += circle.r.y; |
|
} |
|
d = circle.d.init(dr); |
|
d.t = t; |
|
d.r = [r.x, r.y]; |
|
d.rr = rr; |
|
d.dist = Math.sqrt(dr.x * dr.x + dr.y * dr.y); |
|
return d; |
|
}; |
|
|
|
lineseg_intersect = function(m, n, i, j) { |
|
var A1, A2, B1, B2, C1, C2, check1, check2, check3, check4, det, ri, rj, si, sj, x, y, _ref, _ref1, _ref2, _ref3; |
|
ri = m.ri.init(m.path[i]); |
|
rj = m.rj.init(z_check(m.path, i)); |
|
si = n.ri.init(n.path[j]); |
|
sj = n.rj.init(z_check(n.path, j)); |
|
A1 = rj.y - ri.y; |
|
B1 = ri.x - rj.x; |
|
C1 = A1 * (ri.x + m.r.x) + B1 * (ri.y + m.r.y); |
|
A2 = sj.y - si.y; |
|
B2 = si.x - sj.x; |
|
C2 = A2 * (si.x + n.r.x) + B2 * (si.y + n.r.y); |
|
det = A1 * B2 - A2 * B1; |
|
if (det === 0) { |
|
return false; |
|
} |
|
x = (B2 * C1 - B1 * C2) / det; |
|
y = (A1 * C2 - A2 * C1) / det; |
|
check1 = (Math.min(ri.x, rj.x) - m.tol <= (_ref = x - m.r.x) && _ref <= Math.max(ri.x, rj.x) + m.tol); |
|
check2 = (Math.min(si.x, sj.x) - n.tol <= (_ref1 = x - n.r.x) && _ref1 <= Math.max(si.x, sj.x) + n.tol); |
|
check3 = (Math.min(ri.y, rj.y) - m.tol <= (_ref2 = y - m.r.y) && _ref2 <= Math.max(ri.y, rj.y) + m.tol); |
|
check4 = (Math.min(si.y, sj.y) - n.tol <= (_ref3 = y - n.r.y) && _ref3 <= Math.max(si.y, sj.y) + n.tol); |
|
if (check1 && check2 && check3 && check4) { |
|
return true; |
|
} else { |
|
return false; |
|
} |
|
}; |
|
|
|
z_check = function(seg, i) { |
|
switch (seg[i + 1].pathSegTypeAsLetter) { |
|
case 'z': |
|
case 'Z': |
|
return seg[0]; |
|
default: |
|
return seg[i + 1]; |
|
} |
|
}; |
|
|
|
return Collision; |
|
|
|
})(); |
|
|
|
$z.Force = (function() { |
|
function Force() {} |
|
|
|
Force.dr = { |
|
x: 0, |
|
y: 0 |
|
}; |
|
|
|
Force.rpx = { |
|
x: 0, |
|
y: 0 |
|
}; |
|
|
|
Force.rmx = { |
|
x: 0, |
|
y: 0 |
|
}; |
|
|
|
Force.rpy = { |
|
x: 0, |
|
y: 0 |
|
}; |
|
|
|
Force.rmy = { |
|
x: 0, |
|
y: 0 |
|
}; |
|
|
|
Force["eval"] = function(element, param, f, accumulateSwitch) { |
|
var emx, emy, epx, epy, fx, fy, r2, r3; |
|
if (accumulateSwitch == null) { |
|
accumulateSwitch = false; |
|
} |
|
if ((param != null ? param.type : void 0) == null) { |
|
console.log('Force.eval: undefined param type, param:', param); |
|
f.x = 0; |
|
f.y = 0; |
|
return f; |
|
} |
|
switch (param.type) { |
|
case 'constant': |
|
fx = param.fx; |
|
fy = param.fy; |
|
break; |
|
case 'friction': |
|
fx = -param.alpha * element.v.x; |
|
fy = -param.alpha * element.v.y; |
|
break; |
|
case 'spring': |
|
fx = -(element.r.x - param.cx); |
|
fy = -(element.r.y - param.cy); |
|
break; |
|
case 'charge': |
|
case 'gravity': |
|
this.dr.x = param.cx - element.r.x; |
|
this.dr.y = param.cy - element.r.y; |
|
r2 = this.dr.x * this.dr.x + this.dr.y * this.dr.y; |
|
r3 = r2 * Math.sqrt(r2); |
|
fx = param.q * this.dr.x / r3; |
|
fy = param.q * this.dr.y / r3; |
|
break; |
|
case 'random': |
|
fx = 2 * (Math.random() - 0.5) * param.xScale; |
|
fy = 2 * (Math.random() - 0.5) * param.yScale; |
|
if (element.r.x > param.xBound) { |
|
fx = -param.fxBound; |
|
} |
|
if (element.r.y > param.yBound) { |
|
fy = -param.fyBound; |
|
} |
|
if (element.r.x < 0) { |
|
fx = param.fxBound; |
|
} |
|
if (element.r.y < 0) { |
|
fy = param.fyBound; |
|
} |
|
break; |
|
case 'gradient': |
|
this.rpx.x = element.r.x; |
|
this.rpx.y = element.r.y; |
|
this.rpx.x += param.tol; |
|
this.rmx.x = element.r.x; |
|
this.rmx.y = element.r.y; |
|
this.rmx.x -= param.tol; |
|
this.rpy.x = element.r.x; |
|
this.rpy.y = element.r.y; |
|
this.rpy.y += param.tol; |
|
this.rmy.x = element.r.x; |
|
this.rmy.y = element.r.y; |
|
this.rmy.y -= param.tol; |
|
epx = param.energy(this.rpx); |
|
emx = param.energy(this.rmx); |
|
epy = param.energy(this.rpy); |
|
emy = param.energy(this.rmy); |
|
if (!((epx != null) && (emx != null) && (epy != null) && (emy != null))) { |
|
fx = 0; |
|
fy = 0; |
|
break; |
|
} |
|
fx = -0.5 * (epx - emx) / param.tol; |
|
fy = -0.5 * (epy - emy) / param.tol; |
|
} |
|
if (accumulateSwitch) { |
|
f.x += fx; |
|
f.y += fy; |
|
} else { |
|
f.x = fx; |
|
f.y = fy; |
|
} |
|
return f; |
|
}; |
|
|
|
return Force; |
|
|
|
})(); |
|
|
|
$z.Physics = (function() { |
|
function Physics() {} |
|
|
|
Physics.fps = 240; |
|
|
|
Physics.elapsedTime = 0; |
|
|
|
Physics.tick = 1000 / Physics.fps; |
|
|
|
Physics.maxElapsedTime = 100 * Physics.tick; |
|
|
|
Physics.Nstep = 2; |
|
|
|
Physics.off = true; |
|
|
|
Physics.game = null; |
|
|
|
Physics.callbacks = []; |
|
|
|
Physics.debug = false; |
|
|
|
Physics.timestamp = void 0; |
|
|
|
Physics.paused = false; |
|
|
|
Physics.euler = function(element, dt) { |
|
var accumulateSwitch; |
|
if (dt == null) { |
|
dt = Physics.tick; |
|
} |
|
if (element.cleanup()) { |
|
return; |
|
} |
|
element.f.x = 0; |
|
element.f.y = 0; |
|
accumulateSwitch = true; |
|
element.force_param.forEach(function(param) { |
|
return $z.Force["eval"](element, param, element.f, accumulateSwitch); |
|
}); |
|
element.v.add(element.f.scale(dt)); |
|
element.dr.init(element.v).scale(dt); |
|
element.r.add(element.dr); |
|
}; |
|
|
|
Physics.integrate = function(t) { |
|
var fps; |
|
if (Physics.off) { |
|
return true; |
|
} |
|
Physics.elapsedTime = t - Physics.timestamp; |
|
if (Physics.debug) { |
|
fps = 1000 / elapsedTime; |
|
console.log('Physics.integrate:', 'dt: ', elapsedTime, 't: ', t, 'timestamp: ', Physics.timestamp, 'dt_chk: ', t - Physics.timestamp, 'fps: ' + fps); |
|
} |
|
Physics.timestamp = t; |
|
Physics.update(); |
|
return Physics.off; |
|
}; |
|
|
|
Physics.update = function() { |
|
Physics.step(); |
|
$z.Collision.detect(); |
|
Physics.draw_all(); |
|
return Physics.run_callbacks(); |
|
}; |
|
|
|
Physics.step = function() { |
|
var element, index, stepCount, _results; |
|
stepCount = 0; |
|
_results = []; |
|
while (stepCount < Physics.Nstep) { |
|
stepCount++; |
|
index = $z.Collision.list.length; |
|
_results.push((function() { |
|
var _results1; |
|
_results1 = []; |
|
while (index--) { |
|
if ($z.Collision.list[index].is_removed) { |
|
_results1.push($z.Utils.index_pop($z.Collision.list, index).sleep()); |
|
} else { |
|
element = $z.Collision.list[index]; |
|
element.tick(element, Physics.elapsedTime / Physics.Nstep); |
|
if (Physics.debug) { |
|
_results1.push(console.log('Physics.update', 'index:', index, 'fps:', fps, 'r.x:', $z.Collision.list[index].r.x, 'r.y:', $z.Collision.list[index].r.y)); |
|
} else { |
|
_results1.push(void 0); |
|
} |
|
} |
|
} |
|
return _results1; |
|
})()); |
|
} |
|
return _results; |
|
}; |
|
|
|
Physics.run_callbacks = function() { |
|
var bool, index, _results; |
|
index = Physics.callbacks.length; |
|
_results = []; |
|
while (index--) { |
|
if (Physics.callbacks.length === 0) { |
|
break; |
|
} |
|
bool = Physics.callbacks[index](Physics.timestamp); |
|
if (bool) { |
|
_results.push($z.Utils.index_pop(Physics.callbacks, index)); |
|
} else { |
|
_results.push(void 0); |
|
} |
|
} |
|
return _results; |
|
}; |
|
|
|
Physics.draw_all = function() { |
|
var element, index, _results; |
|
index = $z.Collision.list.length; |
|
_results = []; |
|
while (index--) { |
|
element = $z.Collision.list[index]; |
|
_results.push(element.draw()); |
|
} |
|
return _results; |
|
}; |
|
|
|
Physics.start = function() { |
|
var blurCallback; |
|
if (!Physics.off) { |
|
return; |
|
} |
|
Physics.off = false; |
|
Physics.timestamp = 0; |
|
d3.timer(Physics.integrate); |
|
blurCallback = function() { |
|
Physics.paused = true; |
|
return Physics.stop(); |
|
}; |
|
$(window).blur(null); |
|
$(window).focus(null); |
|
$(window).blur(blurCallback); |
|
$(window).focus(function() { |
|
if (!Physics.off) { |
|
return; |
|
} |
|
Physics.paused = false; |
|
if ($z.Gamescore.lives >= 0) { |
|
return $z.Game.instance.message('GET READY', function() { |
|
if (Physics.paused) { |
|
return; |
|
} |
|
Physics.timestamp = 0; |
|
return Physics.start(); |
|
}); |
|
} |
|
}); |
|
}; |
|
|
|
Physics.stop = function() { |
|
if (Physics.off) { |
|
return; |
|
} |
|
Physics.off = true; |
|
Physics.timestamp = void 0; |
|
setTimeout(Physics.update, 2 * Physics.tick); |
|
}; |
|
|
|
return Physics; |
|
|
|
})(); |
|
|
|
$z.Reaction = (function() { |
|
function Reaction() {} |
|
|
|
Reaction.circle_circle = function(m, n, d) { |
|
var line, overstep, shift; |
|
if (m.remove_check(n) || n.remove_check(m)) { |
|
return; |
|
} |
|
line = m.line.init(d).normalize(); |
|
overstep = Math.max(d.dmin - d.dist, 0); |
|
shift = 0.5 * (Math.max(m.tol, n.tol) + overstep); |
|
$z.Reaction.elastic_collision(m, n, line, shift); |
|
m.reaction(n); |
|
}; |
|
|
|
Reaction.circle_polygon = function(circle, polygon, d) { |
|
var intersecting_segment, normal, shift; |
|
if (circle.remove_check(polygon) || polygon.remove_check(circle)) { |
|
return; |
|
} |
|
intersecting_segment = polygon.path[d.i]; |
|
normal = intersecting_segment.n; |
|
shift = 0.5 * Math.max(circle.tol, polygon.tol); |
|
$z.Reaction.elastic_collision(circle, polygon, normal, shift); |
|
}; |
|
|
|
Reaction.polygon_polygon = function(m, n, d) { |
|
var dot_a, dot_b, mseg, normal, nseg, segj, shift; |
|
if (m.remove_check(n) || n.remove_check(m)) { |
|
return; |
|
} |
|
mseg = m.path[d.i]; |
|
nseg = n.path[d.j]; |
|
dot_a = mseg.n.dot(d); |
|
dot_b = nseg.n.dot(d); |
|
if (Math.abs(dot_a) > Math.abs(dot_b)) { |
|
normal = m.normal.init(mseg.n).scale(dot_a / Math.abs(dot_a)); |
|
segj = nseg; |
|
} else { |
|
normal = m.normal.init(nseg.n).scale(dot_b / Math.abs(dot_b)); |
|
segj = mseg; |
|
} |
|
shift = 0.5 * Math.max(m.tol, n.tol); |
|
$z.Reaction.elastic_collision(m, n, normal, shift); |
|
m.reaction(n); |
|
}; |
|
|
|
Reaction.elastic_collision = function(m, n, line, shift) { |
|
var cPar, dPar, iter, lshift, maxiter, reaction, uPar, uPerp, vPar, vPerp; |
|
lshift = m.lshift.init(line).scale(shift); |
|
maxiter = 32; |
|
iter = 1; |
|
reaction = false; |
|
while ($z.Collision.check(m, n, reaction) && iter <= maxiter) { |
|
m.r = m.r.add(lshift); |
|
n.r = n.r.subtract(lshift); |
|
iter++; |
|
} |
|
cPar = m.v.dot(line); |
|
vPar = m.vPar.init(line).scale(cPar); |
|
vPerp = m.vPerp.init(m.v).subtract(vPar); |
|
dPar = n.v.dot(line); |
|
uPar = m.uPar.init(line).scale(dPar); |
|
uPerp = m.uPerp.init(n.v).subtract(uPar); |
|
uPar.add(vPerp); |
|
vPar.add(uPerp); |
|
m.v.x = uPar.x; |
|
m.v.y = uPar.y; |
|
n.v.x = vPar.x; |
|
n.v.y = vPar.y; |
|
}; |
|
|
|
return Reaction; |
|
|
|
})(); |
|
|
|
$z.ForceParam = (function() { |
|
function ForceParam(config) { |
|
this.config = config != null ? config : {}; |
|
this.type = this.config.type || 'constant'; |
|
switch (this.type) { |
|
case 'constant': |
|
this.fx = this.config.x || 0; |
|
this.fy = this.config.y || 0; |
|
break; |
|
case 'friction': |
|
this.alpha = this.config.alpha || 1; |
|
this.vscale = this.config.vscale || .99; |
|
this.vcut = this.config.vcut || 1e-2; |
|
break; |
|
case 'spring': |
|
this.cx = this.config.cx || 0; |
|
this.cy = this.config.cy || 0; |
|
break; |
|
case 'charge': |
|
case 'gravity': |
|
this.cx = this.config.cx || 0; |
|
this.cy = this.config.cy || 0; |
|
this.q = this.config.q || 1; |
|
break; |
|
case 'random': |
|
this.xScale = this.config.xScale || 1; |
|
this.yScale = this.config.yScale || 1; |
|
this.fxBound = this.config.fxBound || 10; |
|
this.fyBound = this.config.fyBound || 10; |
|
break; |
|
case 'gradient': |
|
this.tol = this.config.tol || 0.1; |
|
this.energy = this.config.energy || function(r) {}; |
|
} |
|
} |
|
|
|
return ForceParam; |
|
|
|
})(); |
|
|
|
$z.Vec = (function() { |
|
function Vec(config) { |
|
this.config = config != null ? config : {}; |
|
this.x = this.config.x || 0; |
|
this.y = this.config.y || 0; |
|
} |
|
|
|
Vec.prototype.init = function(v) { |
|
if (v == null) { |
|
v = { |
|
x: 0, |
|
y: 0 |
|
}; |
|
} |
|
this.x = v.x; |
|
this.y = v.y; |
|
return this; |
|
}; |
|
|
|
Vec.prototype.scale = function(c) { |
|
this.x *= c; |
|
this.y *= c; |
|
return this; |
|
}; |
|
|
|
Vec.prototype.add = function(v) { |
|
this.x += v.x; |
|
this.y += v.y; |
|
return this; |
|
}; |
|
|
|
Vec.prototype.subtract = function(v) { |
|
this.x -= v.x; |
|
this.y -= v.y; |
|
return this; |
|
}; |
|
|
|
Vec.prototype.rotate = function(a) { |
|
var c, s; |
|
c = Math.cos(a); |
|
s = Math.sin(a); |
|
this.x = c * this.x - s * this.y; |
|
this.y = s * this.x + c * this.y; |
|
return this; |
|
}; |
|
|
|
Vec.prototype.dot = function(v) { |
|
return this.x * v.x + this.y * v.y; |
|
}; |
|
|
|
Vec.prototype.length_squared = function() { |
|
return this.dot(this); |
|
}; |
|
|
|
Vec.prototype.length = function() { |
|
return Math.sqrt(this.length_squared()); |
|
}; |
|
|
|
Vec.prototype.normalize = function(length) { |
|
var inverseLength; |
|
if (length == null) { |
|
length = 1; |
|
} |
|
inverseLength = length / this.length(); |
|
this.x *= inverseLength; |
|
this.y *= inverseLength; |
|
return this; |
|
}; |
|
|
|
Vec.prototype.dist_squared = function(v) { |
|
var dx, dy; |
|
dx = this.x - v.x; |
|
dy = this.y - v.y; |
|
return dx * dx + dy * dy; |
|
}; |
|
|
|
Vec.prototype.dist = function(v) { |
|
return Math.sqrt(this.dist_squared(v)); |
|
}; |
|
|
|
return Vec; |
|
|
|
})(); |
|
|
|
$z.Drop = (function(_super) { |
|
__extends(Drop, _super); |
|
|
|
function Drop(config) { |
|
var dur; |
|
this.config = config != null ? config : {}; |
|
this.config.size = this.config.size || .6; |
|
Drop.__super__.constructor.call(this, this.config); |
|
this.fill('navy'); |
|
this.stroke('none'); |
|
this.image.attr('opacity', '0.6'); |
|
this.lifetime = $z.Utils.timestamp(); |
|
this.max_lifetime = 2e4; |
|
this.fadeIn(dur = 250); |
|
} |
|
|
|
Drop.prototype.init = function() { |
|
this.lifetime = $z.Utils.timestamp(); |
|
return Drop.__super__.init.apply(this, arguments); |
|
}; |
|
|
|
Drop.prototype.cleanup = function() { |
|
this.lifetime = $z.Utils.timestamp() - this.lifetime; |
|
if (this.lifetime > this.max_lifetime) { |
|
this.remove(); |
|
} |
|
this.lifetime = $z.Utils.timestamp() - this.lifetime; |
|
if (this.offscreen()) { |
|
if (this.r.x > $z.Game.width) { |
|
this.r.x = this.r.x % $z.Game.width; |
|
} |
|
if (this.r.x < 0) { |
|
this.r.x = $z.Game.width + this.r.x; |
|
} |
|
if (this.r.y < 0) { |
|
this.r.y = 0; |
|
this.v.y = Math.abs(this.v.y); |
|
this.r.x = (this.r.x + $z.Game.width * 0.5) % $z.Game.width; |
|
} |
|
if (this.r.y > $z.Game.height) { |
|
this.r.y = $z.Game.height; |
|
this.v.y = -Math.abs(this.v.y); |
|
this.r.x = (this.r.x + $z.Game.width * 0.5) % $z.Game.width; |
|
} |
|
} |
|
}; |
|
|
|
return Drop; |
|
|
|
})($z.Circle); |
|
|
|
$z.Rainflow = (function(_super) { |
|
__extends(Rainflow, _super); |
|
|
|
function Rainflow(config) { |
|
var V, drops, dur, inst, prompt; |
|
this.config = config != null ? config : {}; |
|
this.drop = __bind(this.drop, this); |
|
$z.Game.height = 180; |
|
$z.Game.width = 360; |
|
Rainflow.__super__.constructor.apply(this, arguments); |
|
this.map_width = 360; |
|
this.map_height = 180; |
|
this.image = this.g.append('image').attr('xlink:href', 'earth_elevation6.png').attr('height', this.map_height).attr('width', this.map_width); |
|
this.root = $z.Factory.spawn($z.Root); |
|
this.numel = this.config.numel || 20; |
|
this.elevation = []; |
|
this.lastdrop = $z.Utils.timestamp(); |
|
this.raining = false; |
|
drops = (function(_this) { |
|
return function(text) { |
|
var row; |
|
row = text.split('\n'); |
|
_this.elevation = row.map(function(d) { |
|
return d.split(',').map(function(d) { |
|
return Number(d); |
|
}); |
|
}); |
|
_this.elevation.pop(); |
|
_this.gravity_param = { |
|
tol: 1, |
|
energy: V, |
|
type: 'gradient' |
|
}; |
|
_this.friction_param = { |
|
alpha: .02, |
|
type: 'friction' |
|
}; |
|
_this.svg.on("click", _this.drop); |
|
return _this.start(); |
|
}; |
|
})(this); |
|
prompt = this.g.append("text").text("").attr("stroke", "black").attr("fill", "deepskyblue").attr("font-size", "36").attr("x", this.map_width / 2 - 100).attr("y", this.map_height / 4).attr('font-family', 'arial').attr('font-weight', 'bold').attr('opacity', 0); |
|
prompt.text("RAINFLOW"); |
|
dur = 1500; |
|
prompt.transition().duration(dur).attr('opacity', 1).transition().duration(dur).delay(dur).attr('opacity', 0).remove(); |
|
inst = this.g.append("text").text("").attr("stroke", "none").attr("fill", "white").attr("font-size", 10).attr("x", this.map_width / 2 - 45).attr("y", this.map_height / 4 + 40).attr('font-family', 'arial').attr('font-weight', 'bold').attr('opacity', 0); |
|
inst.text("click to make it rain"); |
|
inst.transition().duration(dur).attr('opacity', 1).transition().duration(dur).attr('opacity', 0).remove().each('end', function() { |
|
return d3.text('topo_flip.csv', drops); |
|
}); |
|
V = (function(_this) { |
|
return function(r) { |
|
var energy, scale, x, y; |
|
x = r.x; |
|
y = r.y; |
|
if (x < 0) { |
|
x = _this.elevation[0].length - 1 + x % (_this.elevation[0].length - 1); |
|
} |
|
if (x > _this.elevation[0].length - 1) { |
|
x = x % (_this.elevation[0].length - 1); |
|
} |
|
if (y < 0) { |
|
y = _this.elevation.length - 1 + y % (_this.elevation.length - 1); |
|
} |
|
if (y > _this.elevation.length - 1) { |
|
y = y % (_this.elevation.length - 1); |
|
} |
|
scale = 1e-6; |
|
return energy = scale * $z.Utils.bilinear_interp(_this.elevation, x, y); |
|
}; |
|
})(this); |
|
} |
|
|
|
Rainflow.prototype.drop = function(r) { |
|
var clear, config, dr, dur, i, int, new_drop, stamp, _i, _ref; |
|
if (r == null) { |
|
r = this.root.r; |
|
} |
|
stamp = $z.Utils.timestamp(); |
|
if (this.raining) { |
|
return; |
|
} |
|
this.raining = true; |
|
this.lastdrop = stamp; |
|
config = []; |
|
for (i = _i = 0, _ref = this.numel - 1; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) { |
|
dr = $z.Factory.spawn($z.Vec, { |
|
x: 2 * this.root.size * (Math.random() - 0.5), |
|
y: 2 * this.root.size * (Math.random() - 0.5) |
|
}); |
|
config.push({ |
|
r: $z.Factory.spawn($z.Vec, r).add(dr), |
|
force_param: [$z.Factory.spawn($z.ForceParam, this.gravity_param), $z.Factory.spawn($z.ForceParam, this.friction_param)], |
|
width: this.map_width, |
|
height: this.map_height |
|
}); |
|
} |
|
if (!(config.length > 0)) { |
|
return; |
|
} |
|
dur = 10; |
|
new_drop = function() { |
|
$z.Factory.spawn($z.Drop, config.pop()).start(); |
|
if (config.length === 0) { |
|
clear(); |
|
return $z.Game.instance.raining = false; |
|
} |
|
}; |
|
int = setInterval(new_drop, dur); |
|
return clear = function() { |
|
return clearInterval(int); |
|
}; |
|
}; |
|
|
|
return Rainflow; |
|
|
|
})($z.Game); |
|
|
|
$(document).ready(function() { |
|
return new $z.Rainflow(); |
|
}); |
|
|
|
$z.Root = (function(_super) { |
|
__extends(Root, _super); |
|
|
|
function Root(config) { |
|
this.config = config != null ? config : {}; |
|
this.move = __bind(this.move, this); |
|
this.config.size = this.config.size || 1.5; |
|
Root.__super__.constructor.call(this, this.config); |
|
this.svg.style("cursor", "none"); |
|
this.fill('none'); |
|
this.stroke('navy'); |
|
this.image.attr('opacity', 0.4).attr('stroke-width', 1); |
|
this.svg.on("mousemove", this.move); |
|
this.is_root = true; |
|
this.tick = function() {}; |
|
this.g.style('opacity', 1); |
|
} |
|
|
|
Root.prototype.move = function(node) { |
|
var xy; |
|
if (node == null) { |
|
node = this.svg.node(); |
|
} |
|
xy = d3.mouse(node); |
|
this.r.x = xy[0]; |
|
this.r.y = xy[1]; |
|
return this.draw(); |
|
}; |
|
|
|
return Root; |
|
|
|
})($z.Circle); |
|
|
|
}).call(this); |