diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b0541cc
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+*~
+#~
diff --git a/docs/reference.html b/docs/reference.html
index 2696279..1e2965c 100644
--- a/docs/reference.html
+++ b/docs/reference.html
@@ -31,7 +31,7 @@
× creation
The parameters and their defaults are:
- repulsion 1,000 the force repelling nodes from each other
- - stiffness 600 the rigidity of the edges
+ - stiffness 600 the rigidity of the edges (reduce to prevent shaking)
- friction 0.5 the amount of damping in the system
- gravity false an additional force attracting nodes to the origin
- fps 55 frames per second
@@ -572,6 +572,8 @@ vector math
- Returns the point’s vector normal.
- normalize( ) → Point
- Returns a scaled copy of the point with a magnitude of one.
+ - radian(pt) → Number
+ - Returns the angle in radians to the given point.
sanity checking
diff --git a/lib/arbor-graphics.js b/lib/arbor-graphics.js
new file mode 100644
index 0000000..b4078d0
--- /dev/null
+++ b/lib/arbor-graphics.js
@@ -0,0 +1,51 @@
+//
+// arbor-graphics.js
+// canvas fructose
+//
+// Copyright (c) 2011 Samizdat Drafting Co.
+//
+// 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;i0){return a[0]}else{return null}};
+ /* colors.js */ var Colors=(function(){var f=/#[0-9a-f]{6}/i;var b=/#(..)(..)(..)/;var c=function(h){var g=h.toString(16);return(g.length==2)?g:"0"+g};var a=function(g){return parseInt(g,16)};var d=function(g){if(!g||typeof g!="object"){return false}var h=objkeys(g).sort().join("");if(h=="abgr"){return true}};var e={CSS:{aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgrey:"#a9a9a9",darkgreen:"#006400",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",grey:"#808080",green:"#008000",greenyellow:"#adff2f",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgrey:"#d3d3d3",lightgreen:"#90ee90",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370d8",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#d87093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",slategrey:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32"},decode:function(h){var g=arguments.length;for(var l=g-1;l>=0;l--){if(arguments[l]===undefined){g--}}var k=arguments;if(!h){return null}if(g==1&&d(h)){return h}var j=null;if(typeof h=="string"){var o=1;if(g==2){o=k[1]}var n=e.CSS[h.toLowerCase()];if(n!==undefined){h=n}var m=h.match(f);if(m){vals=h.match(b);if(!vals||!vals.length||vals.length!=4){return null}j={r:a(vals[1]),g:a(vals[2]),b:a(vals[3]),a:o}}}else{if(typeof h=="number"){if(g>=3){j={r:k[0],g:k[1],b:k[2],a:1};if(g>=4){j.a*=k[3]}}else{if(g>=1){j={r:k[0],g:k[0],b:k[0],a:1};if(g==2){j.a*=k[1]}}}}}return j},validate:function(g){if(!g||typeof g!="string"){return false}if(e.CSS[g.toLowerCase()]!==undefined){return true}if(g.match(f)){return true}return false},mix:function(h,g,k){var j=e.decode(h);var i=e.decode(g)},blend:function(g,j){j=(j!==undefined)?Math.max(0,Math.min(1,j)):1;var h=e.decode(g);if(!h){return null}if(j==1){return g}var h=g;if(typeof g=="string"){h=e.decode(g)}var i=objcopy(h);i.a*=j;return nano("rgba({r},{g},{b},{a})",i)},encode:function(g){if(!d(g)){g=e.decode(g);if(!d(g)){return null}}if(g.a==1){return nano("#{r}{g}{b}",{r:c(g.r),g:c(g.g),b:c(g.b)})}else{return nano("rgba({r},{g},{b},{a})",g)}}};return e})();
+ /* primitives.js */ var Primitives=function(c,f,g){var b=function(i,m,j,l,k){this.x=i;this.y=m;this.w=j;this.h=l;this.style=(k!==undefined)?k:{}};b.prototype={draw:function(h){this._draw(h)},_draw:function(i,n,j,l,k){if(objcontains(i,"stroke","fill","width")){k=i}if(this.x!==undefined){i=this.x,n=this.y,j=this.w,l=this.h;k=objmerge(this.style,k)}k=objmerge(f,k);if(!k.stroke&&!k.fill){return}var m=0.5522848;ox=(j/2)*m,oy=(l/2)*m,xe=i+j,ye=n+l,xm=i+j/2,ym=n+l/2;c.save();c.beginPath();c.moveTo(i,ym);c.bezierCurveTo(i,ym-oy,xm-ox,n,xm,n);c.bezierCurveTo(xm+ox,n,xe,ym-oy,xe,ym);c.bezierCurveTo(xe,ym+oy,xm+ox,ye,xm,ye);c.bezierCurveTo(xm-ox,ye,i,ym+oy,i,ym);c.closePath();if(k.fill!==null){if(k.alpha!==undefined){c.fillStyle=Colors.blend(k.fill,k.alpha)}else{c.fillStyle=Colors.encode(k.fill)}c.fill()}if(k.stroke!==null){c.strokeStyle=Colors.encode(k.stroke);if(!isNaN(k.width)){c.lineWidth=k.width}c.stroke()}c.restore()}};var a=function(i,n,j,l,m,k){if(objcontains(m,"stroke","fill","width")){k=m;m=0}this.x=i;this.y=n;this.w=j;this.h=l;this.r=(m!==undefined)?m:0;this.style=(k!==undefined)?k:{}};a.prototype={draw:function(h){this._draw(h)},_draw:function(j,o,k,m,n,l){if(objcontains(n,"stroke","fill","width","alpha")){l=n;n=0}else{if(objcontains(j,"stroke","fill","width","alpha")){l=j}}if(this.x!==undefined){j=this.x,o=this.y,k=this.w,m=this.h;l=objmerge(this.style,l)}l=objmerge(f,l);if(!l.stroke&&!l.fill){return}var i=(n>0);c.save();c.beginPath();c.moveTo(j+n,o);c.lineTo(j+k-n,o);if(i){c.quadraticCurveTo(j+k,o,j+k,o+n)}c.lineTo(j+k,o+m-n);if(i){c.quadraticCurveTo(j+k,o+m,j+k-n,o+m)}c.lineTo(j+n,o+m);if(i){c.quadraticCurveTo(j,o+m,j,o+m-n)}c.lineTo(j,o+n);if(i){c.quadraticCurveTo(j,o,j+n,o)}if(l.fill!==null){if(l.alpha!==undefined){c.fillStyle=Colors.blend(l.fill,l.alpha)}else{c.fillStyle=Colors.encode(l.fill)}c.fill()}if(l.stroke!==null){c.strokeStyle=Colors.encode(l.stroke);if(!isNaN(l.width)){c.lineWidth=l.width}c.stroke()}c.restore()}};var e=function(i,l,h,j,k){if(k!==undefined||typeof j=="number"){this.points=[{x:i,y:l},{x:h,y:j}];this.style=k||{}}else{if($.isArray(i)){this.points=i;this.style=l||{}}else{this.points=[i,l];this.style=h||{}}}};e.prototype={draw:function(h){if(this.points.length<2){return}var j=[];if(!$.isArray(this.points[0])){j.push(this.points)}else{j=this.points}c.save();c.beginPath();$.each(j,function(n,m){c.moveTo(m[0].x+0.5,m[0].y+0.5);$.each(m,function(o,p){if(o==0){return}c.lineTo(p.x+0.5,p.y+0.5)})});var i=$.extend(objmerge(f,this.style),h);if(i.closed){c.closePath()}if(i.fill!==undefined){var l=Colors.decode(i.fill,(i.alpha!==undefined)?i.alpha:1);if(l){c.fillStyle=Colors.encode(l)}c.fill()}if(i.stroke!==undefined){var k=Colors.decode(i.stroke,(i.alpha!==undefined)?i.alpha:1);if(k){c.strokeStyle=Colors.encode(k)}if(!isNaN(i.width)){c.lineWidth=i.width}c.stroke()}c.restore()}};var d=function(i,h,l,k){var j=Colors.decode(i,h,l,k);if(j){this.r=j.r;this.g=j.g;this.b=j.b;this.a=j.a}};d.prototype={toString:function(){return Colors.encode(this)},blend:function(){trace("blend",this.r,this.g,this.b,this.a)}};return{_Oval:b,_Rect:a,_Color:d,_Path:e}};
+ /* graphics.js */ var Graphics=function(c){var h=$(c);var q=$(h).get(0).getContext("2d");var i=null;var l="rgb";var e="origin";var m={};var p={background:null,fill:null,stroke:null,width:0};var b={};var g={font:"sans-serif",size:12,align:"left",color:Colors.decode("black"),alpha:1,baseline:"ideographic"};var k=[];var o=Primitives(q,p,g);var f=o._Oval;var n=o._Rect;var d=o._Color;var a=o._Path;var j={init:function(){if(!q){return null}return j},size:function(s,r){if(!isNaN(s)&&!isNaN(r)){h.attr({width:s,height:r})}return{width:h.attr("width"),height:h.attr("height")}},clear:function(r,u,s,t){if(arguments.length<4){r=0;u=0;s=h.attr("width");t=h.attr("height")}q.clearRect(r,u,s,t);if(p.background!==null){q.save();q.fillStyle=Colors.encode(p.background);q.fillRect(r,u,s,t);q.restore()}},background:function(s,r,v,t){if(s==null){p.background=null;return null}var u=Colors.decode(s,r,v,t);if(u){p.background=u;j.clear()}},noFill:function(){p.fill=null},fill:function(s,r,v,t){if(arguments.length==0){return p.fill}else{if(arguments.length>0){var u=Colors.decode(s,r,v,t);p.fill=u;q.fillStyle=Colors.encode(u)}}},noStroke:function(){p.stroke=null;q.strokeStyle=null},stroke:function(s,r,v,u){if(arguments.length==0&&p.stroke!==null){return p.stroke}else{if(arguments.length>0){var t=Colors.decode(s,r,v,u);p.stroke=t;q.strokeStyle=Colors.encode(t)}}},strokeWidth:function(r){if(r===undefined){return q.lineWidth}q.lineWidth=p.width=r},Color:function(r){return new d(r)},drawStyle:function(s){if(arguments.length==0){return objcopy(p)}if(arguments.length==2){var r=arguments[0];var v=arguments[1];if(typeof r=="string"&&typeof v=="object"){var u={};if(v.color!==undefined){var t=Colors.decode(v.color);if(t){u.color=t}}$.each("background fill stroke width".split(" "),function(w,x){if(v[x]!==undefined){u[x]=v[x]}});if(!$.isEmptyObject(u)){m[r]=u}}return}if(arguments.length==1&&m[arguments[0]]!==undefined){s=m[arguments[0]]}if(s.width!==undefined){p.width=s.width}q.lineWidth=p.width;$.each("background fill stroke",function(y,x){if(s[x]!==undefined){if(s[x]===null){p[x]=null}else{var w=Colors.decode(s[x]);if(w){p[x]=w}}}});q.fillStyle=p.fill;q.strokeStyle=p.stroke},textStyle:function(s){if(arguments.length==0){return objcopy(g)}if(arguments.length==2){var r=arguments[0];var v=arguments[1];if(typeof r=="string"&&typeof v=="object"){var u={};if(v.color!==undefined){var t=Colors.decode(v.color);if(t){u.color=t}}$.each("font size align baseline alpha".split(" "),function(w,x){if(v[x]!==undefined){u[x]=v[x]}});if(!$.isEmptyObject(u)){b[r]=u}}return}if(arguments.length==1&&b[arguments[0]]!==undefined){s=b[arguments[0]]}if(s.font!==undefined){g.font=s.font}if(s.size!==undefined){g.size=s.size}q.font=nano("{size}px {font}",g);if(s.align!==undefined){q.textAlign=g.align=s.align}if(s.baseline!==undefined){q.textBaseline=g.baseline=s.baseline}if(s.alpha!==undefined){g.alpha=s.alpha}if(s.color!==undefined){var t=Colors.decode(s.color);if(t){g.color=t}}if(g.color){var t=Colors.blend(g.color,g.alpha);if(t){q.fillStyle=t}}},text:function(s,r,z,v){if(arguments.length>=3&&!isNaN(r)){v=v||{};v.x=r;v.y=z}else{if(arguments.length==2&&typeof(r)=="object"){v=r}else{v=v||{}}}var u=objmerge(g,v);q.save();if(u.align!==undefined){q.textAlign=u.align}if(u.baseline!==undefined){q.textBaseline=u.baseline}if(u.font!==undefined&&!isNaN(u.size)){q.font=nano("{size}px {font}",u)}var w=(u.alpha!==undefined)?u.alpha:g.alpha;var t=(u.color!==undefined)?u.color:g.color;q.fillStyle=Colors.blend(t,w);if(w>0){q.fillText(s,Math.round(u.x),u.y)}q.restore()},textWidth:function(r,t){t=objmerge(g,t||{});q.save();q.font=nano("{size}px {font}",t);var s=q.measureText(r).width;q.restore();return s},Rect:function(s,A,t,v,z,u){return new n(s,A,t,v,z,u)},rect:function(s,A,t,v,z,u){n.prototype._draw(s,A,t,v,z,u)},Oval:function(r,v,s,u,t){return new f(r,v,s,u,t)},oval:function(r,v,s,u,t){t=t||{};f.prototype._draw(r,v,s,u,t)},line:function(s,v,r,t,u){var w=new a(s,v,r,t);w.draw(u)},lines:function(s,u,r,t){if(typeof t=="number"){k.push([{x:s,y:u},{x:r,y:t}])}else{k.push([s,u])}},drawLines:function(r){var s=new a(k);s.draw(r);k=[]}};return j.init()};
+
+ arbor = (typeof(arbor)!=='undefined') ? arbor : {}
+ $.extend(arbor, {
+ // object constructor (don't use ‘new’, just call it)
+ Graphics:function(ctx){ return Graphics(ctx) },
+
+ // useful methods for dealing with the r/g/b
+ colors:{
+ CSS:Colors.CSS, // dict:{colorname:"#fef2e2", ...}
+ validate:Colors.validate, // ƒ(str) -> t/f
+ decode:Colors.decode, // ƒ(hexString_or_cssColor) -> {r,g,b,a}
+ encode:Colors.encode, // ƒ({r,g,b,a}) -> hexOrRgbaString
+ blend:Colors.blend // ƒ(color, opacity) -> rgbaString
+ }
+ })
+
+})(this.jQuery)
\ No newline at end of file
diff --git a/lib/arbor.js b/lib/arbor.js
index a25dd24..367f77a 100644
--- a/lib/arbor.js
+++ b/lib/arbor.js
@@ -31,8 +31,8 @@
/* etc.js */ var trace=function(msg){if(typeof(window)=="undefined"||!window.console){return}var len=arguments.length;var args=[];for(var i=0;i0){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;p0);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.epoch1||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.x0){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};
+ /* 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())},radian:function(b){var a=b.subtract(this);rad=Math.acos(Math.abs(a.x)/a.magnitude());if(a.x<0){if(a.y<0){}else{rad*=-1}}else{if(a.y<0){rad+=Math.PI;rad*=-1}else{rad+=Math.PI}}return rad}};
+ /* 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 d=="object"){var s=d;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},edgeCount:function(v){return g.getEdgesFrom(v).length+g.getEdgesTo(v).length},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.epoch1||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.x0){c.kernel.graphChanged(j);j=[];h=null}}};c.kernel=Kernel(g);c.tween=c.kernel.tween||null;if(typeof Object.defineProperty!="function"){Object.defineProperty=function(w,x,v){if(v.get){w.__defineGetter__(x,v.get)}if(v.set){w.__defineSetter__(x,v.set)}}}Object.defineProperty(Node.prototype,"p",{get:function(){var w=this;var v={};Object.defineProperty(v,"x",{get:function(){return w._p.x},set:function(x){c.kernel.particleModified(w._id,{x:x})}});Object.defineProperty(v,"y",{get:function(){return w._p.y},set:function(x){c.kernel.particleModified(w._id,{y:x})}});v.__proto__=Point.prototype;return v},set:function(v){this._p.x=v.x;this._p.y=v.y;c.kernel.particleModified(this._id,{x:v.x,y:v.y})}});Object.defineProperty(Node.prototype,"mass",{get:function(){return this._mass},set:function(v){this._mass=v;c.kernel.particleModified(this._id,{m:v})}});Object.defineProperty(Node.prototype,"fixed",{add:function(){return this._fixed},set: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-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 hexOrRgbaString
blend:Colors.blend // ƒ(color, opacity) -> rgbaString
},
- etc:{
+ etc:{
trace:trace, // ƒ(msg) -> safe console logging
dirname:dirname, // ƒ(path) -> leading part of path
basename:basename, // ƒ(path) -> trailing part of path
@@ -46,10 +46,9 @@
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
+ arbor_path:arbor_path // ƒ() -> guess the directory of the lib code
}
- })
+ });
-
-})()
+ })();
diff --git a/src/physics/atoms.js b/src/physics/atoms.js
index 7cbff49..99306a9 100644
--- a/src/physics/atoms.js
+++ b/src/physics/atoms.js
@@ -9,7 +9,7 @@ var Node = function(data){
this.data = data || {}; // the user-serviceable parts
this._mass = (data.mass!==undefined) ? data.mass : 1
this._fixed = (data.fixed===true) ? true : false
- this._p = new Point((typeof(data.x)=='number') ? data.x : null,
+ this._p = new Point((typeof(data.x)=='number') ? data.x : null,
(typeof(data.y)=='number') ? data.y : null)
delete this.data.x
delete this.data.y
@@ -58,7 +58,7 @@ var Point = function(x, y){
y = x.y; x=x.x;
}
this.x = x;
- this.y = y;
+ this.y = y;
}
Point.random = function(radius){
@@ -90,6 +90,30 @@ Point.prototype = {
},
normalize:function(){
return this.divide(this.magnitude());
+ },
+ radian:function(n){
+ var p = n.subtract(this);
+ rad = Math.acos(Math.abs(p.x)/p.magnitude());
+
+ // I'm sure there is a better way to do this. I think my
+ // trig book is in the attic some place
+ if(p.x<0){
+ if(p.y<0){
+ //console.log('a', rad);
+ }else{
+ rad *= -1;
+ //console.log('b',rad);
+ }
+ }else if(p.y<0){
+ rad += Math.PI;
+ rad *= -1;
+ //console.log('c',rad);
+ }else{
+ rad += Math.PI;
+ //console.log('d',rad);
+ }
+
+ return rad;
}
-}
+};
diff --git a/src/physics/system.js b/src/physics/system.js
index b1b722f..87fb512 100644
--- a/src/physics/system.js
+++ b/src/physics/system.js
@@ -1,12 +1,12 @@
//
// system.js
//
-// the main controller object for creating/modifying graphs
+// the main controller object for creating/modifying graphs
//
var ParticleSystem = function(repulsion, stiffness, friction, centerGravity, targetFps, dt, precision){
// also callable with ({stiffness:, repulsion:, friction:, timestep:, fps:, dt:, gravity:})
-
+
var _changes=[]
var _notification=null
var _epoch = 0
@@ -17,8 +17,8 @@
var _bounds = null
var _boundsTarget = null
- if (typeof stiffness=='object'){
- var _p = stiffness
+ if (typeof repulsion=='object'){
+ var _p = repulsion
friction = _p.friction
repulsion = _p.repulsion
targetFps = _p.fps
@@ -87,7 +87,7 @@
// by the simulation:
// 'mass' overrides the default of 1
// 'fixed' overrides the default of false
- // 'x' & 'y' will set a starting position rather than
+ // 'x' & 'y' will set a starting position rather than
// defaulting to random placement
var x = (data.x!=undefined) ? data.x : null
var y = (data.y!=undefined) ? data.y : null
@@ -108,7 +108,7 @@
// remove a node and its associated edges from the graph
pruneNode:function(nodeOrName) {
var node = that.getNode(nodeOrName)
-
+
if (typeof(state.nodes[node._id]) !== 'undefined'){
delete state.nodes[node._id]
delete state.names[node.name]
@@ -177,7 +177,7 @@
_changes.push({t:"dropSpring", id:edge._id})
delete state.edges[edge._id]
-
+
for (var x in state.adjacency){
for (var y in state.adjacency[x]){
var edges = state.adjacency[x][y];
@@ -198,7 +198,7 @@
node1 = that.getNode(node1)
node2 = that.getNode(node2)
if (!node1 || !node2) return []
-
+
if (typeof(state.adjacency[node1._id]) !== 'undefined'
&& typeof(state.adjacency[node1._id][node2._id]) !== 'undefined'){
return state.adjacency[node1._id][node2._id];
@@ -210,7 +210,7 @@
getEdgesFrom:function(node) {
node = that.getNode(node)
if (!node) return []
-
+
if (typeof(state.adjacency[node._id]) !== 'undefined'){
var nodeEdges = []
$.each(state.adjacency[node._id], function(id, subEdges){
@@ -230,10 +230,14 @@
$.each(state.edges, function(edgeId, edge){
if (edge.target == node) nodeEdges.push(edge)
})
-
+
return nodeEdges;
},
+ edgeCount:function(node){
+ return that.getEdgesFrom(node).length + that.getEdgesTo(node).length;
+ },
+
eachEdge:function(callback){
// callback should accept two arguments: Edge, Point
$.each(state.edges, function(id, e){
@@ -242,10 +246,10 @@
if (p1.x==null || p2.x==null) return
-
+
p1 = (_screenSize!==null) ? that.toScreen(p1) : p1
p2 = (_screenSize!==null) ? that.toScreen(p2) : p2
-
+
if (p1 && p2) callback.call(that, e, p1, p2);
})
},
@@ -272,9 +276,9 @@
// trace('prune', changes.dropped)
return changes
},
-
+
graft:function(branch){
- // branch is of the form: { nodes:{name1:{d}, name2:{d},...},
+ // branch is of the form: { nodes:{name1:{d}, name2:{d},...},
// edges:{fromNm:{toNm1:{d}, toNm2:{d}}, ...} }
var changes = {added:{nodes:[], edges:[]}}
@@ -282,13 +286,13 @@
var oldNode = that.getNode(name)
// should probably merge any x/y/m data as well...
// if (oldNode) $.extend(oldNode.data, nodeData)
-
+
if (oldNode) oldNode.data = nodeData
else changes.added.nodes.push( that.addNode(name, nodeData) )
-
+
state.kernel.start()
})
-
+
if (branch.edges) $.each(branch.edges, function(src, dsts){
var srcNode = that.getNode(src)
if (!srcNode) changes.added.nodes.push( that.addNode(src, {}) )
@@ -330,24 +334,24 @@
changes.dropped.edges.push(edge)
}
})
-
+
var prune_changes = that.prune(function(node, edges){
if (branch.nodes[node.name] === undefined){
changes.dropped.nodes.push(node)
return true
}
})
- var graft_changes = that.graft(branch)
+ var graft_changes = that.graft(branch)
changes.added.nodes = changes.added.nodes.concat(graft_changes.added.nodes)
changes.added.edges = changes.added.edges.concat(graft_changes.added.edges)
changes.dropped.nodes = changes.dropped.nodes.concat(prune_changes.dropped.nodes)
changes.dropped.edges = changes.dropped.edges.concat(prune_changes.dropped.edges)
-
+
// trace('changes', changes)
return changes
},
-
+
tweenNode:function(nodeOrName, dur, to){
var node = that.getNode(nodeOrName)
if (node) state.tween.to(node, dur, to)
@@ -361,7 +365,7 @@
// called with (node1, node2, dur, to)
var edges = that.getEdges(a,b)
$.each(edges, function(i, edge){
- that._tweenEdge(edge, c, d)
+ that._tweenEdge(edge, c, d)
})
}
},
@@ -371,7 +375,7 @@
},
_updateGeometry:function(e){
- if (e != undefined){
+ if (e != undefined){
var stale = (e.epoch<_epoch)
_energy = e.energy
@@ -379,27 +383,27 @@
if (pts!==undefined){
for (var i=0, j=pts.length/3; i1 || diff.y*_screenSize.height>1){
_bounds = _newBounds
return true
}else{
- return false
+ return false
}
},
@@ -513,11 +517,11 @@
topleft = new Point(node._p)
return
}
-
+
var point = node._p
if (point.x===null || point.y===null) return
if (point.x > bottomright.x) bottomright.x = point.x;
- if (point.y > bottomright.y) bottomright.y = point.y;
+ if (point.y > bottomright.y) bottomright.y = point.y;
if (point.x < topleft.x) topleft.x = point.x;
if (point.y < topleft.y) topleft.y = point.y;
})
@@ -536,10 +540,10 @@
if (_screenSize!==null) pos = that.fromScreen(pos)
// if screen size has been specified, presume pos is in screen pixel
// units and convert it back to the particle system coordinates
-
+
var min = {node: null, point: null, distance: null};
var t = that;
-
+
$.each(state.nodes, function(id, node){
var pt = node._p
if (pt.x===null || pt.y===null) return
@@ -549,7 +553,7 @@
if (_screenSize!==null) min.screenPoint = that.toScreen(pt)
}
})
-
+
if (min.node){
if (_screenSize!==null) min.distance = that.toScreen(min.node.p).subtract(that.toScreen(pos)).magnitude()
return min
@@ -563,7 +567,7 @@
// (using a short timeout to batch changes)
if (_notification===null) _epoch++
else clearTimeout(_notification)
-
+
_notification = setTimeout(that._synchronize,20)
// that._synchronize()
},
@@ -573,45 +577,80 @@
_changes = []
_notification = null
}
- },
- }
-
+ }
+ };
+
state.kernel = Kernel(that)
state.tween = state.kernel.tween || null
-
- // some magic attrs to make the Node objects phone-home their physics-relevant changes
- Node.prototype.__defineGetter__("p", function() {
- var self = this
- var roboPoint = {}
- roboPoint.__defineGetter__('x', function(){ return self._p.x; })
- roboPoint.__defineSetter__('x', function(newX){ state.kernel.particleModified(self._id, {x:newX}) })
- roboPoint.__defineGetter__('y', function(){ return self._p.y; })
- roboPoint.__defineSetter__('y', function(newY){ state.kernel.particleModified(self._id, {y:newY}) })
- roboPoint.__proto__ = Point.prototype
- return roboPoint
- })
- Node.prototype.__defineSetter__("p", function(newP) {
- this._p.x = newP.x
- this._p.y = newP.y
- state.kernel.particleModified(this._id, {x:newP.x, y:newP.y})
- })
- Node.prototype.__defineGetter__("mass", function() { return this._mass; });
- Node.prototype.__defineSetter__("mass", function(newM) {
- this._mass = newM
- state.kernel.particleModified(this._id, {m:newM})
- })
+ // Firefox 3 doesn't support defineProperty, so we set it here
+ if (typeof Object.defineProperty != 'function'){
+ Object.defineProperty = function(obj, prop, descriptor){
+ if (descriptor.get){
+ obj.__defineGetter__(prop, descriptor.get);
+ }
+ if (descriptor.set) {
+ obj.__defineSetter__(prop, descriptor.set);
+ }
+ };
+ }
- Node.prototype.__defineSetter__("tempMass", function(newM) {
+ // some magic attrs to make the Node objects phone-home their physics-relevant changes
+ Object.defineProperty
+ (Node.prototype, "p", {
+ get:function() {
+ var self = this
+ var roboPoint = {}
+
+ Object.defineProperty
+ (roboPoint, 'x', {
+ get:function(){ return self._p.x; },
+ set:function(newX){ state.kernel.particleModified(self._id, {x:newX}) }
+ }
+ );
+
+ Object.defineProperty
+ (roboPoint, 'y', {
+ get:function(){ return self._p.y; },
+ set:function(newY){ state.kernel.particleModified(self._id, {y:newY}) }
+ }
+ );
+ roboPoint.__proto__ = Point.prototype;
+ return roboPoint;
+ },
+ set:function(newP) {
+ this._p.x = newP.x
+ this._p.y = newP.y
+ state.kernel.particleModified(this._id, {x:newP.x, y:newP.y})
+ }
+ }
+ );
+
+ Object.defineProperty
+ (Node.prototype, "mass", {
+ get:function() { return this._mass; },
+ set:function(newM) {
+ this._mass = newM
+ state.kernel.particleModified(this._id, {m:newM})
+ }
+ }
+ );
+
+ /*
+ Node.prototype.__defineSetter__("tempMass", function(newM) {
state.kernel.particleModified(this._id, {_m:newM})
})
-
- Node.prototype.__defineGetter__("fixed", function() { return this._fixed; });
- Node.prototype.__defineSetter__("fixed", function(isFixed) {
- this._fixed = isFixed
- state.kernel.particleModified(this._id, {f:isFixed?1:0})
- })
-
+ */
+
+ Object.defineProperty
+ (Node.prototype, "fixed", {
+ add:function() { return this._fixed; },
+ set:function(isFixed) {
+ this._fixed = isFixed
+ state.kernel.particleModified(this._id, {f:isFixed?1:0})
+ }
+ }
+ );
+
return that
}
-
\ No newline at end of file