Skip to content

Instantly share code, notes, and snippets.

@dannyko
Last active August 29, 2015 14:06
Show Gist options
  • Save dannyko/c31420dfd63a7c2304bf to your computer and use it in GitHub Desktop.
Save dannyko/c31420dfd63a7c2304bf to your computer and use it in GitHub Desktop.

Revisions

  1. dannyko revised this gist Sep 14, 2014. 1 changed file with 1 addition and 2 deletions.
    3 changes: 1 addition & 2 deletions Readme.md
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,4 @@
    Rainflow
    ========
    ### Rainflow

    Gradient flow example using bilinear interpolation of matrix map of Earth altitudes.

  2. dannyko revised this gist Sep 14, 2014. No changes.
  3. dannyko revised this gist Sep 14, 2014. 1 changed file with 5 additions and 3 deletions.
    8 changes: 5 additions & 3 deletions Readme.md
    Original file line number Diff line number Diff line change
    @@ -3,10 +3,12 @@ Rainflow

    Gradient flow example using bilinear interpolation of matrix map of Earth altitudes.

    For this example, I computed a Newtonian force field from a 2-D potential energy function, in this case coming from the matrix map of average altitudes of the Earth at every degree (a 180-by-360 dataset).
    For this example, I computed the gravitational force field from a gradient of a 2-D potential energy function, in this case a bilinearly interpolated matrix map of average altitudes of the Earth at every degree (a 180-by-360 dataset).

    This global elevation dataset is included with Matlab and is available by running "Load topo" i think. Their doc page references National Geophysical Data Center, NOAA US Department of Commerce under data announcement 88-MGG-02.
    This global elevation dataset is included with Matlab and is available by running "Load topo". Their doc page references National Geophysical Data Center, NOAA US Department of Commerce under data announcement 88-MGG-02:

    [http://www.ngdc.noaa.gov/mgg/global/etopo5.HTML](http://www.ngdc.noaa.gov/mgg/global/etopo5.HTML)

    I used the physics engine from the open-source HTML5 game dev kit (built using d3) that I've been working on, available on Github at:

    http://github.com/gameprez/gpdk
    [http://github.com/gameprez/gpdk](http://github.com/gameprez/gpdk)
  4. dannyko revised this gist Sep 14, 2014. No changes.
  5. dannyko revised this gist Sep 14, 2014. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion Readme.md
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,5 @@
    =Rainflow=
    Rainflow
    ========

    Gradient flow example using bilinear interpolation of matrix map of Earth altitudes.

  6. dannyko revised this gist Sep 14, 2014. 1 changed file with 10 additions and 2 deletions.
    12 changes: 10 additions & 2 deletions Readme.md
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,11 @@
    Rainflow
    =Rainflow=

    Gradient flow example using bilinear interpolation of matrix map of Earth altitudes
    Gradient flow example using bilinear interpolation of matrix map of Earth altitudes.

    For this example, I computed a Newtonian force field from a 2-D potential energy function, in this case coming from the matrix map of average altitudes of the Earth at every degree (a 180-by-360 dataset).

    This global elevation dataset is included with Matlab and is available by running "Load topo" i think. Their doc page references National Geophysical Data Center, NOAA US Department of Commerce under data announcement 88-MGG-02.

    I used the physics engine from the open-source HTML5 game dev kit (built using d3) that I've been working on, available on Github at:

    http://github.com/gameprez/gpdk
  7. dannyko revised this gist Sep 14, 2014. 1 changed file with 1 addition and 8 deletions.
    9 changes: 1 addition & 8 deletions rainflow.js
    Original file line number Diff line number Diff line change
    @@ -1351,18 +1351,11 @@
    };

    Physics.integrate = function(t) {
    var dur, fps;
    var fps;
    if (Physics.off) {
    return true;
    }
    Physics.elapsedTime = t - Physics.timestamp;
    if (Physics.elapsedTime > Physics.maxElapsedTime) {
    dur = 2000;
    Physics.stop();
    $z.Game.instance.message('CPU SPEED ERROR', function() {
    return $z.Game.instance.stop();
    }, dur);
    }
    if (Physics.debug) {
    fps = 1000 / elapsedTime;
    console.log('Physics.integrate:', 'dt: ', elapsedTime, 't: ', t, 'timestamp: ', Physics.timestamp, 'dt_chk: ', t - Physics.timestamp, 'fps: ' + fps);
  8. dannyko revised this gist Sep 14, 2014. 1 changed file with 53 additions and 49 deletions.
    102 changes: 53 additions & 49 deletions rainflow.js
    Original file line number Diff line number Diff line change
    @@ -550,13 +550,6 @@
    return this;
    };

    Element.prototype.update = function(elapsedTime) {
    if (typeof this.tick === "function") {
    this.tick(this, elapsedTime);
    }
    this.draw();
    };

    Element.prototype.scale = function(scalingFactor, dur, callback) {
    if (scalingFactor == null) {
    scalingFactor = 10;
    @@ -1322,6 +1315,10 @@

    Physics.tick = 1000 / Physics.fps;

    Physics.maxElapsedTime = 100 * Physics.tick;

    Physics.Nstep = 2;

    Physics.off = true;

    Physics.game = null;
    @@ -1354,63 +1351,59 @@
    };

    Physics.integrate = function(t) {
    var elapsedTime, fps;
    var dur, fps;
    if (Physics.off) {
    return true;
    }
    elapsedTime = t - Physics.timestamp;
    Physics.elapsedTime = elapsedTime;
    Physics.elapsedTime = t - Physics.timestamp;
    if (Physics.elapsedTime > Physics.maxElapsedTime) {
    dur = 2000;
    Physics.stop();
    $z.Game.instance.message('CPU SPEED ERROR', function() {
    return $z.Game.instance.stop();
    }, dur);
    }
    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(elapsedTime);
    Physics.update();
    return Physics.off;
    };

    Physics.update = function(elapsedTime) {
    var Nmax, Nstep, dur, step;
    if (elapsedTime == null) {
    elapsedTime = Physics.elapsedTime;
    }
    Nstep = 2;
    Nmax = 600;
    if (Nstep > Nmax) {
    dur = 2000;
    Physics.stop();
    $z.Game.instance.message('CPU SPEED ERROR', function() {
    return $z.Game.instance.stop();
    }, dur);
    }
    step = 0;
    while (step < Nstep) {
    Physics.step();
    $z.Collision.detect();
    Physics.run_callbacks();
    ++step;
    }
    Physics.update = function() {
    Physics.step();
    $z.Collision.detect();
    Physics.draw_all();
    return Physics.run_callbacks();
    };

    Physics.step = function(elapsedTime) {
    var index, _results;
    if (elapsedTime == null) {
    elapsedTime = Physics.tick;
    }
    index = $z.Collision.list.length;
    Physics.step = function() {
    var element, index, stepCount, _results;
    stepCount = 0;
    _results = [];
    while (index--) {
    if ($z.Collision.list[index].is_removed) {
    _results.push($z.Utils.index_pop($z.Collision.list, index).sleep());
    } else {
    $z.Collision.list[index].update(elapsedTime);
    if (Physics.debug) {
    _results.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 {
    _results.push(void 0);
    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;
    };
    @@ -1433,6 +1426,17 @@
    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) {
    @@ -1796,7 +1800,7 @@
    if (y > _this.elevation.length - 1) {
    y = y % (_this.elevation.length - 1);
    }
    scale = 4e-6;
    scale = 1e-6;
    return energy = scale * $z.Utils.bilinear_interp(_this.elevation, x, y);
    };
    })(this);
  9. dannyko revised this gist Sep 14, 2014. 1 changed file with 2 additions and 4 deletions.
    6 changes: 2 additions & 4 deletions rainflow.js
    Original file line number Diff line number Diff line change
    @@ -1796,9 +1796,8 @@
    if (y > _this.elevation.length - 1) {
    y = y % (_this.elevation.length - 1);
    }
    scale = 1e-6;
    energy = scale * $z.Utils.bilinear_interp(_this.elevation, x, y);
    return energy;
    scale = 4e-6;
    return energy = scale * $z.Utils.bilinear_interp(_this.elevation, x, y);
    };
    })(this);
    }
    @@ -1834,7 +1833,6 @@
    new_drop = function() {
    $z.Factory.spawn($z.Drop, config.pop()).start();
    if (config.length === 0) {
    console.log('clearing');
    clear();
    return $z.Game.instance.raining = false;
    }
  10. dannyko revised this gist Sep 13, 2014. 4 changed files with 2132 additions and 0 deletions.
    Binary file added earth_elevation6.png
    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.
    63 changes: 63 additions & 0 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,63 @@
    <!DOCTYPE HTML>
    <html style="height:100%;width:100%;">
    <head>
    <script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script><!-- http://d3js.org/-->
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.8.2.min.js"></script>
    </head>
    <body oncontextmenu="return false;" style="border:0;padding:0;margin:0;overflow:hidden;height:100%;width:100%;">
    <div id="game_div" style="margin-left: auto; margin-right: auto; height:100%;width:100%;">
    <svg id="game_svg" style="margin-left:auto; margin-right: auto; display:block;">
    <g id="game_g">
    </g>
    </svg>
    </div>
    <!-- run the game: -->
    <script type="text/javascript" src="rainflow.js"></script><!-- use a single .js file for deployment, run "cake build" to recompile -->
    </body>
    </html>


    <!--
    <!DOCTYPE html>
    <html>
    <head>
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no"/>
    <script type="text/javascript" src="http://underscorejs.org/underscore-min.js"></script><!-- http://coffeescript.org/extras/-->
    <!--
    <script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script><!-- http://d3js.org/-->
    <!-- <script type="text/javascript" src="http://code.jquery.com/jquery-1.8.2.min.js"></script>
    <style type="text/css">
    body {
    width: 100%;
    margin: 0;
    padding: 0;
    padding-left: 10px;
    overflow: hidden;
    }
    svg {
    display: block;
    margin:auto;
    }
    </style>
    </head>
    <body>
    <div id="map" oncontextmenu="return false">
    <svg id="game_svg" width="360px" height="180px">
    </svg>
    </div>
    <!-- run the game: -->
    <!--<script type="text/javascript" src="rainflow.js"></script><!-- use a single .js file for deployment, run "cake build" to recompile -->
    <!--<script type="text/javascript">
    $(document).ready( function() {
    new Rainflow().start() ; // create and start the visualization
    }) ;
    </script>
    </body>
    </html>
    -->
    1,889 changes: 1,889 additions & 0 deletions rainflow.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,1889 @@
    // 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.update = function(elapsedTime) {
    if (typeof this.tick === "function") {
    this.tick(this, elapsedTime);
    }
    this.draw();
    };

    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.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 elapsedTime, fps;
    if (Physics.off) {
    return true;
    }
    elapsedTime = t - Physics.timestamp;
    Physics.elapsedTime = elapsedTime;
    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(elapsedTime);
    return Physics.off;
    };

    Physics.update = function(elapsedTime) {
    var Nmax, Nstep, dur, step;
    if (elapsedTime == null) {
    elapsedTime = Physics.elapsedTime;
    }
    Nstep = 2;
    Nmax = 600;
    if (Nstep > Nmax) {
    dur = 2000;
    Physics.stop();
    $z.Game.instance.message('CPU SPEED ERROR', function() {
    return $z.Game.instance.stop();
    }, dur);
    }
    step = 0;
    while (step < Nstep) {
    Physics.step();
    $z.Collision.detect();
    Physics.run_callbacks();
    ++step;
    }
    return Physics.run_callbacks();
    };

    Physics.step = function(elapsedTime) {
    var index, _results;
    if (elapsedTime == null) {
    elapsedTime = Physics.tick;
    }
    index = $z.Collision.list.length;
    _results = [];
    while (index--) {
    if ($z.Collision.list[index].is_removed) {
    _results.push($z.Utils.index_pop($z.Collision.list, index).sleep());
    } else {
    $z.Collision.list[index].update(elapsedTime);
    if (Physics.debug) {
    _results.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 {
    _results.push(void 0);
    }
    }
    }
    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.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;
    energy = scale * $z.Utils.bilinear_interp(_this.elevation, x, y);
    return energy;
    };
    })(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) {
    console.log('clearing');
    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);
    180 changes: 180 additions & 0 deletions topo_flip.csv
    180 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
  11. dannyko created this gist Sep 13, 2014.
    3 changes: 3 additions & 0 deletions Readme.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,3 @@
    Rainflow

    Gradient flow example using bilinear interpolation of matrix map of Earth altitudes