// // arbor.js - version 0.91 // a graph vizualization toolkit // // Copyright (c) 2011 Samizdat Drafting Co. // Physics code derived from springy.js, copyright (c) 2010 Dennis Hotson // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following // conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // (function ($) { /* etc.js */ var trace = function (msg) { if (typeof (window) == "undefined" || !window.console) { return } var len = arguments.length; var args = []; for (var i = 0; i < len; i++) { args.push("arguments[" + i + "]") } eval("console.log(" + args.join(",") + ")") }; var dirname = function (a) { var b = a.replace(/^\/?(.*?)\/?$/, "$1").split("/"); b.pop(); return "/" + b.join("/") }; var basename = function (b) { var c = b.replace(/^\/?(.*?)\/?$/, "$1").split("/"); var a = c.pop(); if (a == "") { return null } else { return a } }; var _ordinalize_re = /(\d)(?=(\d\d\d)+(?!\d))/g; var ordinalize = function (a) { var b = "" + a; if (a < 11000) { b = ("" + a).replace(_ordinalize_re, "$1,") } else { if (a < 1000000) { b = Math.floor(a / 1000) + "k" } else { if (a < 1000000000) { b = ("" + Math.floor(a / 1000)).replace(_ordinalize_re, "$1,") + "m" } } } return b }; var nano = function (a, b) { return a.replace(/\{([\w\-\.]*)}/g, function (f, c) { var d = c.split("."), e = b[d.shift()]; $.each(d, function () { if (e.hasOwnProperty(this)) { e = e[this] } else { e = f } }); return e }) }; var objcopy = function (a) { if (a === undefined) { return undefined } if (a === null) { return null } if (a.parentNode) { return a } switch (typeof a) { case"string": return a.substring(0); break; case"number": return a + 0; break; case"boolean": return a === true; break } var b = ($.isArray(a)) ? [] : {}; $.each(a, function (d, c) { b[d] = objcopy(c) }); return b }; var objmerge = function (d, b) { d = d || {}; b = b || {}; var c = objcopy(d); for (var a in b) { c[a] = b[a] } return c }; var objcmp = function (e, c, d) { if (!e || !c) { return e === c } if (typeof e != typeof c) { return false } if (typeof e != "object") { return e === c } else { if ($.isArray(e)) { if (!($.isArray(c))) { return false } if (e.length != c.length) { return false } } else { var h = []; for (var f in e) { if (e.hasOwnProperty(f)) { h.push(f) } } var g = []; for (var f in c) { if (c.hasOwnProperty(f)) { g.push(f) } } if (!d) { h.sort(); g.sort() } if (h.join(",") !== g.join(",")) { return false } } var i = true; $.each(e, function (a) { var b = objcmp(e[a], c[a]); i = i && b; if (!i) { return false } }); return i } }; var objkeys = function (b) { var a = []; $.each(b, function (d, c) { if (b.hasOwnProperty(d)) { a.push(d) } }); return a }; var objcontains = function (c) { if (!c || typeof c != "object") { return false } for (var b = 1, a = arguments.length; b < a; b++) { if (c.hasOwnProperty(arguments[b])) { return true } } return false }; var uniq = function (b) { var a = b.length; var d = {}; for (var c = 0; c < a; c++) { d[b[c]] = true } return objkeys(d) }; var arbor_path = function () { var a = $("script").map(function (b) { var c = $(this).attr("src"); if (!c) { return } if (c.match(/arbor[^\/\.]*.js|dev.js/)) { return c.match(/.*\//) || "/" } }); if (a.length > 0) { return a[0] } else { return null } }; /* kernel.js */ var Kernel = function (b) { var k = window.location.protocol == "file:" && navigator.userAgent.toLowerCase().indexOf("chrome") > -1; var a = (window.Worker !== undefined && !k); var i = null; var c = null; var f = []; f.last = new Date(); var l = null; var e = null; var d = null; var h = null; var g = false; var j = { system: b, tween: null, nodes: {}, init: function () { if (typeof (Tween) != "undefined") { c = Tween() } else { if (typeof (arbor.Tween) != "undefined") { c = arbor.Tween() } else { c = { busy: function () { return false }, tick: function () { return true }, to: function () { trace("Please include arbor-tween.js to enable tweens"); c.to = function () { }; return } } } } j.tween = c; var m = b.parameters(); if (a) { trace("using web workers"); l = setInterval(j.screenUpdate, m.timeout); i = new Worker(arbor_path() + "arbor.js"); i.onmessage = j.workerMsg; i.onerror = function (n) { trace("physics:", n) }; i.postMessage({type: "physics", physics: objmerge(m, {timeout: Math.ceil(m.timeout)})}) } else { trace("couldn't use web workers, be careful..."); i = Physics(m.dt, m.stiffness, m.repulsion, m.friction, j.system._updateGeometry); j.start() } return j }, graphChanged: function (m) { if (a) { i.postMessage({type: "changes", changes: m}) } else { i._update(m) } j.start() }, particleModified: function (n, m) { if (a) { i.postMessage({type: "modify", id: n, mods: m}) } else { i.modifyNode(n, m) } j.start() }, physicsModified: function (m) { if (!isNaN(m.timeout)) { if (a) { clearInterval(l); l = setInterval(j.screenUpdate, m.timeout) } else { clearInterval(d); d = null } } if (a) { i.postMessage({type: "sys", param: m}) } else { i.modifyPhysics(m) } j.start() }, workerMsg: function (n) { var m = n.data.type; if (m == "geometry") { j.workerUpdate(n.data) } else { trace("physics:", n.data) } }, _lastPositions: null, workerUpdate: function (m) { j._lastPositions = m; j._lastBounds = m.bounds }, _lastFrametime: new Date().valueOf(), _lastBounds: null, _currentRenderer: null, screenUpdate: function () { var n = new Date().valueOf(); var m = false; if (j._lastPositions !== null) { j.system._updateGeometry(j._lastPositions); j._lastPositions = null; m = true } if (c && c.busy()) { m = true } if (j.system._updateBounds(j._lastBounds)) { m = true } if (m) { var o = j.system.renderer; if (o !== undefined) { if (o !== e) { o.init(j.system); e = o } if (c) { c.tick() } o.redraw(); var p = f.last; f.last = new Date(); f.push(f.last - p); if (f.length > 50) { f.shift() } } } }, physicsUpdate: function () { if (c) { c.tick() } i.tick(); var n = j.system._updateBounds(); if (c && c.busy()) { n = true } var o = j.system.renderer; var m = new Date(); var o = j.system.renderer; if (o !== undefined) { if (o !== e) { o.init(j.system); e = o } o.redraw({timestamp: m}) } var q = f.last; f.last = m; f.push(f.last - q); if (f.length > 50) { f.shift() } var p = i.systemEnergy(); if ((p.mean + p.max) / 2 < 0.05) { if (h === null) { h = new Date().valueOf() } if (new Date().valueOf() - h > 1000) { clearInterval(d); d = null } else { } } else { h = null } }, fps: function (n) { if (n !== undefined) { var q = 1000 / Math.max(1, targetFps); j.physicsModified({timeout: q}) } var r = 0; for (var p = 0, o = f.length; p < o; p++) { r += f[p] } var m = r / Math.max(1, f.length); if (!isNaN(m)) { return Math.round(1000 / m) } else { return 0 } }, start: function (m) { if (d !== null) { return } if (g && !m) { return } g = false; if (a) { i.postMessage({type: "start"}) } else { h = null; d = setInterval(j.physicsUpdate, j.system.parameters().timeout) } }, stop: function () { g = true; if (a) { i.postMessage({type: "stop"}) } else { if (d !== null) { clearInterval(d); d = null } } } }; return j.init() }; /* atoms.js */ var Node = function (a) { this._id = _nextNodeId++; this.data = a || {}; this._mass = (a.mass !== undefined) ? a.mass : 1; this._fixed = (a.fixed === true) ? true : false; this._p = new Point((typeof (a.x) == "number") ? a.x : null, (typeof (a.y) == "number") ? a.y : null); delete this.data.x; delete this.data.y; delete this.data.mass; delete this.data.fixed }; var _nextNodeId = 1; var Edge = function (b, c, a) { this._id = _nextEdgeId--; this.source = b; this.target = c; this.length = (a.length !== undefined) ? a.length : 1; this.data = (a !== undefined) ? a : {}; delete this.data.length }; var _nextEdgeId = -1; var Particle = function (a, b) { this.p = a; this.m = b; this.v = new Point(0, 0); this.f = new Point(0, 0) }; Particle.prototype.applyForce = function (a) { this.f = this.f.add(a.divide(this.m)) }; var Spring = function (c, b, d, a) { this.point1 = c; this.point2 = b; this.length = d; this.k = a }; Spring.prototype.distanceToParticle = function (a) { var c = that.point2.p.subtract(that.point1.p).normalize().normal(); var b = a.p.subtract(that.point1.p); return Math.abs(b.x * c.x + b.y * c.y) }; var Point = function (a, b) { if (a && a.hasOwnProperty("y")) { b = a.y; a = a.x } this.x = a; this.y = b }; Point.random = function (a) { a = (a !== undefined) ? a : 5; return new Point(2 * a * (Math.random() - 0.5), 2 * a * (Math.random() - 0.5)) }; Point.prototype = { exploded: function () { return (isNaN(this.x) || isNaN(this.y)) }, add: function (a) { return new Point(this.x + a.x, this.y + a.y) }, subtract: function (a) { return new Point(this.x - a.x, this.y - a.y) }, multiply: function (a) { return new Point(this.x * a, this.y * a) }, divide: function (a) { return new Point(this.x / a, this.y / a) }, magnitude: function () { return Math.sqrt(this.x * this.x + this.y * this.y) }, normal: function () { return new Point(-this.y, this.x) }, normalize: function () { return this.divide(this.magnitude()) } }; /* system.js */ var ParticleSystem = function (d, p, e, f, t, l, q) { var j = []; var h = null; var k = 0; var u = null; var m = 0.04; var i = [20, 20, 20, 20]; var n = null; var o = null; if (typeof p == "object") { var s = p; e = s.friction; d = s.repulsion; t = s.fps; l = s.dt; p = s.stiffness; f = s.gravity; q = s.precision } e = isNaN(e) ? 0.5 : e; d = isNaN(d) ? 1000 : d; t = isNaN(t) ? 55 : t; p = isNaN(p) ? 600 : p; l = isNaN(l) ? 0.02 : l; q = isNaN(q) ? 0.6 : q; f = (f === true); var r = (t !== undefined) ? 1000 / t : 1000 / 50; var b = {repulsion: d, stiffness: p, friction: e, dt: l, gravity: f, precision: q, timeout: r}; var a; var c = {renderer: null, tween: null, nodes: {}, edges: {}, adjacency: {}, names: {}, kernel: null}; var g = { parameters: function (v) { if (v !== undefined) { if (!isNaN(v.precision)) { v.precision = Math.max(0, Math.min(1, v.precision)) } $.each(b, function (x, w) { if (v[x] !== undefined) { b[x] = v[x] } }); c.kernel.physicsModified(v) } return b }, fps: function (v) { if (v === undefined) { return c.kernel.fps() } else { g.parameters({timeout: 1000 / (v || 50)}) } }, start: function () { c.kernel.start() }, stop: function () { c.kernel.stop() }, addNode: function (w, B) { B = B || {}; var C = c.names[w]; if (C) { C.data = B; return C } else { if (w != undefined) { var v = (B.x != undefined) ? B.x : null; var D = (B.y != undefined) ? B.y : null; var A = (B.fixed) ? 1 : 0; var z = new Node(B); z.name = w; c.names[w] = z; c.nodes[z._id] = z; j.push({t: "addNode", id: z._id, m: z.mass, x: v, y: D, f: A}); g._notify(); return z } } }, pruneNode: function (w) { var v = g.getNode(w); if (typeof (c.nodes[v._id]) !== "undefined") { delete c.nodes[v._id]; delete c.names[v.name] } $.each(c.edges, function (y, x) { if (x.source._id === v._id || x.target._id === v._id) { g.pruneEdge(x) } }); j.push({t: "dropNode", id: v._id}); g._notify() }, getNode: function (v) { if (v._id !== undefined) { return v } else { if (typeof v == "string" || typeof v == "number") { return c.names[v] } } }, eachNode: function (v) { $.each(c.nodes, function (y, x) { if (x._p.x == null || x._p.y == null) { return } var w = (u !== null) ? g.toScreen(x._p) : x._p; v.call(g, x, w) }) }, addEdge: function (z, A, y) { z = g.getNode(z) || g.addNode(z); A = g.getNode(A) || g.addNode(A); y = y || {}; var x = new Edge(z, A, y); var B = z._id; var C = A._id; c.adjacency[B] = c.adjacency[B] || {}; c.adjacency[B][C] = c.adjacency[B][C] || []; var w = (c.adjacency[B][C].length > 0); if (w) { $.extend(c.adjacency[B][C].data, x.data); return } else { c.edges[x._id] = x; c.adjacency[B][C].push(x); var v = (x.length !== undefined) ? x.length : 1; j.push({t: "addSpring", id: x._id, fm: B, to: C, l: v}); g._notify() } return x }, pruneEdge: function (A) { j.push({t: "dropSpring", id: A._id}); delete c.edges[A._id]; for (var v in c.adjacency) { for (var B in c.adjacency[v]) { var w = c.adjacency[v][B]; for (var z = w.length - 1; z >= 0; z--) { if (c.adjacency[v][B][z]._id === A._id) { c.adjacency[v][B].splice(z, 1) } } } } g._notify() }, getEdges: function (w, v) { w = g.getNode(w); v = g.getNode(v); if (!w || !v) { return [] } if (typeof (c.adjacency[w._id]) !== "undefined" && typeof (c.adjacency[w._id][v._id]) !== "undefined") { return c.adjacency[w._id][v._id] } return [] }, getEdgesFrom: function (v) { v = g.getNode(v); if (!v) { return [] } if (typeof (c.adjacency[v._id]) !== "undefined") { var w = []; $.each(c.adjacency[v._id], function (y, x) { w = w.concat(x) }); return w } return [] }, getEdgesTo: function (v) { v = g.getNode(v); if (!v) { return [] } var w = []; $.each(c.edges, function (y, x) { if (x.target == v) { w.push(x) } }); return w }, eachEdge: function (v) { $.each(c.edges, function (z, x) { var y = c.nodes[x.source._id]._p; var w = c.nodes[x.target._id]._p; if (y.x == null || w.x == null) { return } y = (u !== null) ? g.toScreen(y) : y; w = (u !== null) ? g.toScreen(w) : w; if (y && w) { v.call(g, x, y, w) } }) }, prune: function (w) { var v = {dropped: {nodes: [], edges: []}}; if (w === undefined) { $.each(c.nodes, function (y, x) { v.dropped.nodes.push(x); g.pruneNode(x) }) } else { g.eachNode(function (y) { var x = w.call(g, y, {from: g.getEdgesFrom(y), to: g.getEdgesTo(y)}); if (x) { v.dropped.nodes.push(y); g.pruneNode(y) } }) } return v }, graft: function (w) { var v = {added: {nodes: [], edges: []}}; if (w.nodes) { $.each(w.nodes, function (y, x) { var z = g.getNode(y); if (z) { z.data = x } else { v.added.nodes.push(g.addNode(y, x)) } c.kernel.start() }) } if (w.edges) { $.each(w.edges, function (z, x) { var y = g.getNode(z); if (!y) { v.added.nodes.push(g.addNode(z, {})) } $.each(x, function (D, A) { var C = g.getNode(D); if (!C) { v.added.nodes.push(g.addNode(D, {})) } var B = g.getEdges(z, D); if (B.length > 0) { B[0].data = A } else { v.added.edges.push(g.addEdge(z, D, A)) } }) }) } return v }, merge: function (w) { var v = {added: {nodes: [], edges: []}, dropped: {nodes: [], edges: []}}; $.each(c.edges, function (A, z) { if ((w.edges[z.source.name] === undefined || w.edges[z.source.name][z.target.name] === undefined)) { g.pruneEdge(z); v.dropped.edges.push(z) } }); var y = g.prune(function (A, z) { if (w.nodes[A.name] === undefined) { v.dropped.nodes.push(A); return true } }); var x = g.graft(w); v.added.nodes = v.added.nodes.concat(x.added.nodes); v.added.edges = v.added.edges.concat(x.added.edges); v.dropped.nodes = v.dropped.nodes.concat(y.dropped.nodes); v.dropped.edges = v.dropped.edges.concat(y.dropped.edges); return v }, tweenNode: function (y, v, x) { var w = g.getNode(y); if (w) { c.tween.to(w, v, x) } }, tweenEdge: function (w, v, z, y) { if (y === undefined) { g._tweenEdge(w, v, z) } else { var x = g.getEdges(w, v); $.each(x, function (A, B) { g._tweenEdge(B, z, y) }) } }, _tweenEdge: function (w, v, x) { if (w && w._id !== undefined) { c.tween.to(w, v, x) } }, _updateGeometry: function (y) { if (y != undefined) { var v = (y.epoch < k); a = y.energy; var z = y.geometry; if (z !== undefined) { for (var x = 0, w = z.length / 3; x < w; x++) { var A = z[3 * x]; if (v && c.nodes[A] == undefined) { continue } c.nodes[A]._p.x = z[3 * x + 1]; c.nodes[A]._p.y = z[3 * x + 2] } } } }, screen: function (v) { if (v == undefined) { return {size: (u) ? objcopy(u) : undefined, padding: i.concat(), step: m} } if (v.size !== undefined) { g.screenSize(v.size.width, v.size.height) } if (!isNaN(v.step)) { g.screenStep(v.step) } if (v.padding !== undefined) { g.screenPadding(v.padding) } }, screenSize: function (v, w) { u = {width: v, height: w}; g._updateBounds() }, screenPadding: function (y, z, v, w) { if ($.isArray(y)) { trbl = y } else { trbl = [y, z, v, w] } var A = trbl[0]; var x = trbl[1]; var B = trbl[2]; if (x === undefined) { trbl = [A, A, A, A] } else { if (B == undefined) { trbl = [A, x, A, x] } } i = trbl }, screenStep: function (v) { m = v }, toScreen: function (x) { if (!n || !u) { return } var w = i || [0, 0, 0, 0]; var v = n.bottomright.subtract(n.topleft); var z = w[3] + x.subtract(n.topleft).divide(v.x).x * (u.width - (w[1] + w[3])); var y = w[0] + x.subtract(n.topleft).divide(v.y).y * (u.height - (w[0] + w[2])); return arbor.Point(z, y) }, fromScreen: function (z) { if (!n || !u) { return } var y = i || [0, 0, 0, 0]; var x = n.bottomright.subtract(n.topleft); var w = (z.x - y[3]) / (u.width - (y[1] + y[3])) * x.x + n.topleft.x; var v = (z.y - y[0]) / (u.height - (y[0] + y[2])) * x.y + n.topleft.y; return arbor.Point(w, v) }, _updateBounds: function (w) { if (u === null) { return } if (w) { o = w } else { o = g.bounds() } var z = new Point(o.bottomright.x, o.bottomright.y); var y = new Point(o.topleft.x, o.topleft.y); var B = z.subtract(y); var v = y.add(B.divide(2)); var x = 4; var D = new Point(Math.max(B.x, x), Math.max(B.y, x)); o.topleft = v.subtract(D.divide(2)); o.bottomright = v.add(D.divide(2)); if (!n) { if ($.isEmptyObject(c.nodes)) { return false } n = o; return true } var C = m; _newBounds = { bottomright: n.bottomright.add(o.bottomright.subtract(n.bottomright).multiply(C)), topleft: n.topleft.add(o.topleft.subtract(n.topleft).multiply(C)) }; var A = new Point(n.topleft.subtract(_newBounds.topleft).magnitude(), n.bottomright.subtract(_newBounds.bottomright).magnitude()); if (A.x * u.width > 1 || A.y * u.height > 1) { n = _newBounds; return true } else { return false } }, energy: function () { return a }, bounds: function () { var w = null; var v = null; $.each(c.nodes, function (z, y) { if (!w) { w = new Point(y._p); v = new Point(y._p); return } var x = y._p; if (x.x === null || x.y === null) { return } if (x.x > w.x) { w.x = x.x } if (x.y > w.y) { w.y = x.y } if (x.x < v.x) { v.x = x.x } if (x.y < v.y) { v.y = x.y } }); if (w && v) { return {bottomright: w, topleft: v} } else { return {topleft: new Point(-1, -1), bottomright: new Point(1, 1)} } }, nearest: function (x) { if (u !== null) { x = g.fromScreen(x) } var w = {node: null, point: null, distance: null}; var v = g; $.each(c.nodes, function (B, y) { var z = y._p; if (z.x === null || z.y === null) { return } var A = z.subtract(x).magnitude(); if (w.distance === null || A < w.distance) { w = {node: y, point: z, distance: A}; if (u !== null) { w.screenPoint = g.toScreen(z) } } }); if (w.node) { if (u !== null) { w.distance = g.toScreen(w.node.p).subtract(g.toScreen(x)).magnitude() } return w } else { return null } }, _notify: function () { if (h === null) { k++ } else { clearTimeout(h) } h = setTimeout(g._synchronize, 20) }, _synchronize: function () { if (j.length > 0) { c.kernel.graphChanged(j); j = []; h = null } }, }; c.kernel = Kernel(g); c.tween = c.kernel.tween || null; Node.prototype.__defineGetter__("p", function () { var w = this; var v = {}; v.__defineGetter__("x", function () { return w._p.x }); v.__defineSetter__("x", function (x) { c.kernel.particleModified(w._id, {x: x}) }); v.__defineGetter__("y", function () { return w._p.y }); v.__defineSetter__("y", function (x) { c.kernel.particleModified(w._id, {y: x}) }); v.__proto__ = Point.prototype; return v }); Node.prototype.__defineSetter__("p", function (v) { this._p.x = v.x; this._p.y = v.y; c.kernel.particleModified(this._id, {x: v.x, y: v.y}) }); Node.prototype.__defineGetter__("mass", function () { return this._mass }); Node.prototype.__defineSetter__("mass", function (v) { this._mass = v; c.kernel.particleModified(this._id, {m: v}) }); Node.prototype.__defineSetter__("tempMass", function (v) { c.kernel.particleModified(this._id, {_m: v}) }); Node.prototype.__defineGetter__("fixed", function () { return this._fixed }); Node.prototype.__defineSetter__("fixed", function (v) { this._fixed = v; c.kernel.particleModified(this._id, {f: v ? 1 : 0}) }); return g }; /* barnes-hut.js */ var BarnesHutTree = function () { var b = []; var a = 0; var e = null; var d = 0.5; var c = { init: function (g, h, f) { d = f; a = 0; e = c._newBranch(); e.origin = g; e.size = h.subtract(g) }, insert: function (j) { var f = e; var g = [j]; while (g.length) { var h = g.shift(); var m = h._m || h.m; var p = c._whichQuad(h, f); if (f[p] === undefined) { f[p] = h; f.mass += m; if (f.p) { f.p = f.p.add(h.p.multiply(m)) } else { f.p = h.p.multiply(m) } } else { if ("origin" in f[p]) { f.mass += (m); if (f.p) { f.p = f.p.add(h.p.multiply(m)) } else { f.p = h.p.multiply(m) } f = f[p]; g.unshift(h) } else { var l = f.size.divide(2); var n = new Point(f.origin); if (p[0] == "s") { n.y += l.y } if (p[1] == "e") { n.x += l.x } var o = f[p]; f[p] = c._newBranch(); f[p].origin = n; f[p].size = l; f.mass = m; f.p = h.p.multiply(m); f = f[p]; if (o.p.x === h.p.x && o.p.y === h.p.y) { var k = l.x * 0.08; var i = l.y * 0.08; o.p.x = Math.min(n.x + l.x, Math.max(n.x, o.p.x - k / 2 + Math.random() * k)); o.p.y = Math.min(n.y + l.y, Math.max(n.y, o.p.y - i / 2 + Math.random() * i)) } g.push(o); g.unshift(h) } } } }, applyForces: function (m, g) { var f = [e]; while (f.length) { node = f.shift(); if (node === undefined) { continue } if (m === node) { continue } if ("f" in node) { var k = m.p.subtract(node.p); var l = Math.max(1, k.magnitude()); var i = ((k.magnitude() > 0) ? k : Point.random(1)).normalize(); m.applyForce(i.multiply(g * (node._m || node.m)).divide(l * l)) } else { var j = m.p.subtract(node.p.divide(node.mass)).magnitude(); var h = Math.sqrt(node.size.x * node.size.y); if (h / j > d) { f.push(node.ne); f.push(node.nw); f.push(node.se); f.push(node.sw) } else { var k = m.p.subtract(node.p.divide(node.mass)); var l = Math.max(1, k.magnitude()); var i = ((k.magnitude() > 0) ? k : Point.random(1)).normalize(); m.applyForce(i.multiply(g * (node.mass)).divide(l * l)) } } } }, _whichQuad: function (i, f) { if (i.p.exploded()) { return null } var h = i.p.subtract(f.origin); var g = f.size.divide(2); if (h.y < g.y) { if (h.x < g.x) { return "nw" } else { return "ne" } } else { if (h.x < g.x) { return "sw" } else { return "se" } } }, _newBranch: function () { if (b[a]) { var f = b[a]; f.ne = f.nw = f.se = f.sw = undefined; f.mass = 0; delete f.p } else { f = {origin: null, size: null, nw: undefined, ne: undefined, sw: undefined, se: undefined, mass: 0}; b[a] = f } a++; return f } }; return c }; /* physics.js */ var Physics = function (a, m, n, e, h) { var f = BarnesHutTree(); var c = {particles: {}, springs: {}}; var l = {particles: {}}; var o = []; var k = []; var d = 0; var b = {sum: 0, max: 0, mean: 0}; var g = {topleft: new Point(-1, -1), bottomright: new Point(1, 1)}; var j = 1000; var i = { stiffness: (m !== undefined) ? m : 1000, repulsion: (n !== undefined) ? n : 600, friction: (e !== undefined) ? e : 0.3, gravity: false, dt: (a !== undefined) ? a : 0.02, theta: 0.4, init: function () { return i }, modifyPhysics: function (p) { $.each(["stiffness", "repulsion", "friction", "gravity", "dt", "precision"], function (r, s) { if (p[s] !== undefined) { if (s == "precision") { i.theta = 1 - p[s]; return } i[s] = p[s]; if (s == "stiffness") { var q = p[s]; $.each(c.springs, function (u, t) { t.k = q }) } } }) }, addNode: function (u) { var t = u.id; var q = u.m; var p = g.bottomright.x - g.topleft.x; var s = g.bottomright.y - g.topleft.y; var r = new Point((u.x != null) ? u.x : g.topleft.x + p * Math.random(), (u.y != null) ? u.y : g.topleft.y + s * Math.random()); c.particles[t] = new Particle(r, q); c.particles[t].connections = 0; c.particles[t].fixed = (u.f === 1); l.particles[t] = c.particles[t]; o.push(c.particles[t]) }, dropNode: function (s) { var r = s.id; var q = c.particles[r]; var p = $.inArray(q, o); if (p > -1) { o.splice(p, 1) } delete c.particles[r]; delete l.particles[r] }, modifyNode: function (r, p) { if (r in c.particles) { var q = c.particles[r]; if ("x" in p) { q.p.x = p.x } if ("y" in p) { q.p.y = p.y } if ("m" in p) { q.m = p.m } if ("f" in p) { q.fixed = (p.f === 1) } if ("_m" in p) { if (q._m === undefined) { q._m = q.m } q.m = p._m } } }, addSpring: function (t) { var s = t.id; var p = t.l; var r = c.particles[t.fm]; var q = c.particles[t.to]; if (r !== undefined && q !== undefined) { c.springs[s] = new Spring(r, q, p, i.stiffness); k.push(c.springs[s]); r.connections++; q.connections++; delete l.particles[t.fm]; delete l.particles[t.to] } }, dropSpring: function (s) { var r = s.id; var q = c.springs[r]; q.point1.connections--; q.point2.connections--; var p = $.inArray(q, k); if (p > -1) { k.splice(p, 1) } delete c.springs[r] }, _update: function (p) { d++; $.each(p, function (q, r) { if (r.t in i) { i[r.t](r) } }); return d }, tick: function () { i.tendParticles(); i.eulerIntegrator(i.dt); i.tock() }, tock: function () { var p = []; $.each(c.particles, function (r, q) { p.push(r); p.push(q.p.x); p.push(q.p.y) }); if (h) { h({geometry: p, epoch: d, energy: b, bounds: g}) } }, tendParticles: function () { $.each(c.particles, function (q, p) { if (p._m !== undefined) { if (Math.abs(p.m - p._m) < 1) { p.m = p._m; delete p._m } else { p.m *= 0.98 } } p.v.x = p.v.y = 0 }) }, eulerIntegrator: function (p) { if (i.repulsion > 0) { if (i.theta > 0) { i.applyBarnesHutRepulsion() } else { i.applyBruteForceRepulsion() } } if (i.stiffness > 0) { i.applySprings() } i.applyCenterDrift(); if (i.gravity) { i.applyCenterGravity() } i.updateVelocity(p); i.updatePosition(p) }, applyBruteForceRepulsion: function () { $.each(c.particles, function (q, p) { $.each(c.particles, function (s, r) { if (p !== r) { var u = p.p.subtract(r.p); var v = Math.max(1, u.magnitude()); var t = ((u.magnitude() > 0) ? u : Point.random(1)).normalize(); p.applyForce(t.multiply(i.repulsion * (r._m || r.m) * 0.5).divide(v * v * 0.5)); r.applyForce(t.multiply(i.repulsion * (p._m || p.m) * 0.5).divide(v * v * -0.5)) } }) }) }, applyBarnesHutRepulsion: function () { if (!g.topleft || !g.bottomright) { return } var q = new Point(g.bottomright); var p = new Point(g.topleft); f.init(p, q, i.theta); $.each(c.particles, function (s, r) { f.insert(r) }); $.each(c.particles, function (s, r) { f.applyForces(r, i.repulsion) }) }, applySprings: function () { $.each(c.springs, function (t, p) { var s = p.point2.p.subtract(p.point1.p); var q = p.length - s.magnitude(); var r = ((s.magnitude() > 0) ? s : Point.random(1)).normalize(); p.point1.applyForce(r.multiply(p.k * q * -0.5)); p.point2.applyForce(r.multiply(p.k * q * 0.5)) }) }, applyCenterDrift: function () { var q = 0; var r = new Point(0, 0); $.each(c.particles, function (t, s) { r.add(s.p); q++ }); if (q == 0) { return } var p = r.divide(-q); $.each(c.particles, function (t, s) { s.applyForce(p) }) }, applyCenterGravity: function () { $.each(c.particles, function (r, p) { var q = p.p.multiply(-1); p.applyForce(q.multiply(i.repulsion / 100)) }) }, updateVelocity: function (p) { $.each(c.particles, function (t, q) { if (q.fixed) { q.v = new Point(0, 0); q.f = new Point(0, 0); return } var s = q.v.magnitude(); q.v = q.v.add(q.f.multiply(p)).multiply(1 - i.friction); q.f.x = q.f.y = 0; var r = q.v.magnitude(); if (r > j) { q.v = q.v.divide(r * r) } }) }, updatePosition: function (q) { var r = 0, p = 0, u = 0; var t = null; var s = null; $.each(c.particles, function (w, v) { v.p = v.p.add(v.v.multiply(q)); var x = v.v.magnitude(); var z = x * x; r += z; p = Math.max(z, p); u++; if (!t) { t = new Point(v.p.x, v.p.y); s = new Point(v.p.x, v.p.y); return } var y = v.p; if (y.x === null || y.y === null) { return } if (y.x > t.x) { t.x = y.x } if (y.y > t.y) { t.y = y.y } if (y.x < s.x) { s.x = y.x } if (y.y < s.y) { s.y = y.y } }); b = {sum: r, max: p, mean: r / u, n: u}; g = {topleft: s || new Point(-1, -1), bottomright: t || new Point(1, 1)} }, systemEnergy: function (p) { return b } }; return i.init() }; var _nearParticle = function (b, c) { var c = c || 0; var a = b.x; var f = b.y; var e = c * 2; return new Point(a - c + Math.random() * e, f - c + Math.random() * e) }; // if called as a worker thread, set up a run loop for the Physics object and bail out if (typeof (window) == 'undefined') return (function () { /* hermetic.js */ $ = { each: function (d, e) { if ($.isArray(d)) { for (var c = 0, b = d.length; c < b; c++) { e(c, d[c]) } } else { for (var a in d) { e(a, d[a]) } } }, map: function (a, c) { var b = []; $.each(a, function (f, e) { var d = c(e); if (d !== undefined) { b.push(d) } }); return b }, extend: function (c, b) { if (typeof b != "object") { return c } for (var a in b) { if (b.hasOwnProperty(a)) { c[a] = b[a] } } return c }, isArray: function (a) { if (!a) { return false } return (a.constructor.toString().indexOf("Array") != -1) }, inArray: function (c, a) { for (var d = 0, b = a.length; d < b; d++) { if (a[d] === c) { return d } } return -1 }, isEmptyObject: function (a) { if (typeof a !== "object") { return false } var b = true; $.each(a, function (c, d) { b = false }); return b }, }; /* worker.js */ var PhysicsWorker = function () { var b = 20; var a = null; var d = null; var c = null; var g = []; var f = new Date().valueOf(); var e = { init: function (h) { e.timeout(h.timeout); a = Physics(h.dt, h.stiffness, h.repulsion, h.friction, e.tock); return e }, timeout: function (h) { if (h != b) { b = h; if (d !== null) { e.stop(); e.go() } } }, go: function () { if (d !== null) { return } c = null; d = setInterval(e.tick, b) }, stop: function () { if (d === null) { return } clearInterval(d); d = null }, tick: function () { a.tick(); var h = a.systemEnergy(); if ((h.mean + h.max) / 2 < 0.05) { if (c === null) { c = new Date().valueOf() } if (new Date().valueOf() - c > 1000) { e.stop() } else { } } else { c = null } }, tock: function (h) { h.type = "geometry"; postMessage(h) }, modifyNode: function (i, h) { a.modifyNode(i, h); e.go() }, modifyPhysics: function (h) { a.modifyPhysics(h) }, update: function (h) { var i = a._update(h) } }; return e }; var physics = PhysicsWorker(); onmessage = function (a) { if (!a.data.type) { postMessage("¿kérnèl?"); return } if (a.data.type == "physics") { var b = a.data.physics; physics.init(a.data.physics); return } switch (a.data.type) { case"modify": physics.modifyNode(a.data.id, a.data.mods); break; case"changes": physics.update(a.data.changes); physics.go(); break; case"start": physics.go(); break; case"stop": physics.stop(); break; case"sys": var b = a.data.param || {}; if (!isNaN(b.timeout)) { physics.timeout(b.timeout) } physics.modifyPhysics(b); physics.go(); break } }; })() arbor = (typeof (arbor) !== 'undefined') ? arbor : {} $.extend(arbor, { // object constructors (don't use ‘new’, just call them) ParticleSystem: ParticleSystem, Point: function (x, y) { return new Point(x, y) }, // immutable object with useful methods etc: { trace: trace, // ƒ(msg) -> safe console logging dirname: dirname, // ƒ(path) -> leading part of path basename: basename, // ƒ(path) -> trailing part of path ordinalize: ordinalize, // ƒ(num) -> abbrev integers (and add commas) objcopy: objcopy, // ƒ(old) -> clone an object objcmp: objcmp, // ƒ(a, b, strict_ordering) -> t/f comparison objkeys: objkeys, // ƒ(obj) -> array of all keys in obj objmerge: objmerge, // ƒ(dst, src) -> like $.extend but non-destructive uniq: uniq, // ƒ(arr) -> array of unique items in arr arbor_path: arbor_path, // ƒ() -> guess the directory of the lib code } }) })(this.jQuery)