eval("F=.95;D=40;K=2e-5;T=255;A=Math.abs;P=2*Math.PI;C=document.body.children[0];C.width=C.height=W=H=600;X=Y=300;N=[I=0];N[n=1]={x:X,y:Y_7M=[0];onkeydown_0N[i=++n]={x:X,y:Y_7_8j=n;k=N[j];j--)if(!k.m&&!M[j]){N[++n]={x:(X-k.x)/2,y:(Y-k.y)/2_7M[n]=[i,j]_1down_0i=G.getImageData(X,Y,1,1).data[2];if(k=N[I=_4?0:i]){k.m=!k.m;k.v=k.w=0_1move_0X_5X;Y_5Y;if(k=N[I]){k.x=X;k.y=Y;k.m=0_1up_0I=0};G=C.getContext('2d');setInterval(Q_0with(G){stroke_6_9a(80,80,80,.4)';fill_6_8i=n;k=N[i];i--)with(k){if(m&&i!=I){_8j=n;o=N[j];j--)if((t=A_2+A_3)<2*D&&i!=j){t=t?t*t:1;v+=_2/t;w+=_3/t}if(_4)_8j=0;o=N[_4[j]];j++){t=A_2+A_3;v-=r=K*_2*t;w-=s=K*_3*t;o.v+=r;o.w+=s}x+=v*=F;y+=w*=F;x=x>W?W:x<0?0:x;y=y>H?H:y<0?0:y}beginPath();if(_4){o=N[_4[0]];p=N[_4[1]];moveTo(o.x,o.y);quadraticCurveTo(x,y,p.x,p.y)}else{arc(x,y,D/3,0,P,0);_9('+!m*T+','+m*T+','+i+')';fill()}stroke()}}},9)".replace(/_(.)/g,function(m,i){return"=function(e){_}};C.onmouse_(x-o.x)_(y-o.y)_M[i]_=e.client_Rect(0,0,W,H);_,v:0,w:0,m:1};_for(_fillStyle='rgb".split("_")[i]}))
F=.95; // 5% friction
D=40; // derived as (W+H)/30
K=2e-5; // derived as 1/D/D/D
T=255; // max color value
A=Math.abs;
P=2*Math.PI;
C=document.body.children[0];
C.width=C.height=W=H=600;
X=Y=300;
// N, set of nodes, zeroth position is a dummy
// I, index of selected node
N=[I=0];
// n, number of nodes
N[n=1]={x:X,y:Y,v:0,w:0,m:1};
M=[0]; // edges, zeroth position is a dummy
onkeydown=function(e){
// add a node
N[i=++n]={x:X,y:Y,v:0,w:0,m:1};
// add edges to selected nodes
for(j=n;k=N[j];j--)
if(!k.m&&!M[j]){
N[++n]={x:(X-k.x)/2,y:(Y-k.y)/2,v:0,w:0,m:1};
M[n]=[i,j]
}
};
C.onmousedown=function(e){
// the blue part of the pixel encodes the node index
i=G.getImageData(X,Y,1,1).data[2];
// toggle moving/fixed state
if(k=N[I=M[i]?0:i]){
k.m=!k.m;
k.v=k.w=0
}
};
C.onmousemove=function(e){
X=e.clientX;
Y=e.clientY;
// if we are dragging a node, update its coordinates
if(k=N[I]){
k.x=X;
k.y=Y;
k.m=0
}
};
C.onmouseup=function(e){
I=0
};
G=C.getContext('2d');
setInterval(Q=function(e){
with(G){
strokeRect(0,0,W,H);
// poor mans motion blur
fillStyle='rgba(80,80,80,.4)';
fillRect(0,0,W,H);
// loop over all nodes
for(i=n;k=N[i];i--)
with(k){
if(m&&i!=I){
// calculate the repelling forces from nearby nodes
for(j=n;o=N[j];j--)
if((t=A(x-o.x)+A(y-o.y))<2*D&&i!=j){
t=t?t*t:1;
v+=(x-o.x)/t;
w+=(y-o.y)/t
}
// apply attracting forces to connected nodes if this is an edge
if(M[i])
for(j=0;o=N[M[i][j]];j++){
t=A(x-o.x)+A(y-o.y);
v-=r=K*(x-o.x)*t;
w-=s=K*(y-o.y)*t;
o.v+=r;
o.w+=s
}
// apply friction and update coordinates
x+=v*=F;
y+=w*=F;
// do not cross borders of the canvas
x=x>W?W:x<0?0:x;
y=y>H?H:y<0?0:y
}
// draw the edge or node on the canvas
beginPath();
if(M[i]){
o=N[M[i][0]];
p=N[M[i][1]];
moveTo(o.x,o.y);
quadraticCurveTo(x,y,p.x,p.y)
}else{
arc(x,y,D/3,0,P,0);
fillStyle='rgb('+!m*T+','+m*T+','+i+')';
fill()
}
stroke()
}
}
},9)