<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />Story, Part 1
Early in the afternoon, the late October sunshine plays with the boy and his friends. It isn’t scary yet. Halloween is hours away.
( A redesigned JMP User Community is coming! Part 2 and Part 3 will find you there! )
Video and Code
This video sequence shows Simulated Annealing for evenly placing 3200 points around a sphere. Initially the points are placed in a double spiral from the north pole to the south pole. I wasn’t trying for a double spiral; I was hoping for a bit more initial spreading, but it works anyway. The colors are just for the visualization. After the initial points, the annealing process begins. https://en.wikipedia.org/wiki/Annealing_(metallurgy) describes a physical annealing process, where metal is heated to relax a crystalized structure, allowing the atoms to move into a different relationship. Some materials might even be annealed in the warmth of the October sun. The JSL here is tweaked for the video and not optimized for anything else...but good enough. During the annealing phase, a schedule of heating and cooling controls how much the points are allowed to inch away from their nearest neighbor. Because there are a lot of points crowded together at the poles, the schedule needs to keep the system hot for a bit so those points have time to migrate away. At that “high temperature” the points are jumping far enough that they often go too far into another point’s neighborhood. The gradual cooling phase of the schedule lets the points settle down, moving less and less. The video ends before they have completely settled.
https://www.youtube.com/watch?v=8-Fjw1wD-Oc

nnodes = 3200;
xx = -nnodes * 3 / 2;
nodelocs = J( nnodes, 3, 0 ); // x,y,z point for each node
For( i = 1, i <= nnodes, i++,
f = i / nnodes; // 0..1
z = Cos( f * Pi() ); // top to bottom
r = Sin( f * Pi() ); // radius
x = Sin( 10000 * f ) * r; // 10,000 radians
y = Cos( 10000 * f ) * r; // is way too many. but it works...
nodelocs[i, 1] = x;
nodelocs[i, 2] = y;
nodelocs[i, 3] = z;
);
//nodeobj = scene displaylist(); nodeobj<
addnodes = Function( {thisnnodes},
{inode, delta, pos = [0 0 0], result = Scene Display List()},
For( inode = 1, inode <= thisnnodes, inode++,
delta = nodelocs[inode, 0] - pos;
pos = nodelocs[inode, 0];
//show(pos,delta);
result << translate( delta );
rgb = Color To RGB( Heat Color( inode / nnodes, "spectral" ) ) * 1;
//show(inode/nnodes,rgb);
result << color( rgb[1], rgb[2], rgb[3] );
result << sphere( .005, 3, 3 )//calllist( nodeobj );
;
);
result << translate( -pos ); // restore original location
result// return the display list
;
);
//a=addnodes();
// animate the nodes into a sphere of radius 1 about 0,0,0
adjust = Function( {d},
{inode, radius, closest, dist, err, pos, newlocs = J( nnodes, 3, . ), table = KDTable( nodelocs )},
For( inode = 1, inode <= nnodes, inode++,
pos = nodelocs[inode, 0];
// nudge away from nearest neighbor
table << removerows( inode );
{closest, dist} = table << knearestrows( 5, pos );
table << insertrows( inode );
For( iclose = 1, iclose <= 5, iclose++,
err = pos - nodelocs[closest[iclose], 0] + J( 1, 3, Random Normal( 0, .01 ) ); // [dx dy dz]
//pos = pos + .00991/ err; // move away
For( idim = 1, idim <= 3, idim++,
If( err[idim] < 0,
pos[idim] -= d / iclose,
pos[idim] += d / iclose
)
);
);
// nudge radius toward 1.000
radius = Sqrt( Sum( pos
pos ) );
//err = 1.0 - radius; // err is positive if radius too small
pos = pos / radius; // big err moves faster
newlocs[inode, 0] = pos; // temporary copy
);
nodelocs = newlocs; // copy temp to actual
0 // dummy return value
;
);
angle = 30;
scene = Scene Box( 1924 / 2, 1084 / 2 );
build = Function( {thisnnodes},
{},
scene << clear;
scene << ShowArcBall( never/*always*/ /*During Drag*/ );
scene << BackgroundColor( black );
scene << Perspective( 65, .1, 5 );
scene << Translate( 0.0, 0.0, -2.0 );
angle += .1;
scene << rotate( angle * 0.23, 1, 0, 0 );
scene << rotate( angle * 0.11, 0, 1, 0 );
scene << rotate( angle * 2, 0, 0, 1 );
object = addnodes( thisnnodes );
scene << ArcBall( object, 1 );
);
build( 1 );
New Window( "Dot World", scene );
For( i = 1, i <= 3200, i += 4,
build( i );
scene << update;
Wait( 0 );
// scene<
);
For( i = 1, i < 5000, i++, // all but done at 1000
vint = Interpolate( i, 1, .00001, 100, .0005, 200, .001, 4000, .0005, 5000, .00001 );
Show( i, vint );
build( 3200 );
scene << update;
Wait( 0 );
adjust( vint );
//scene<
);
build();
scene << update;
Wait( 0 );