// 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);