From OrganicDesign Wiki
// Clumping Simulation - Jim Bumgardner
kNumberGuys = 26; // Number of balls
kMinRadius = 8;
kMaxRadius = 32; // Radius of balls
kGravWeight = 1; // Controls how strong the force towards the center is
kDamp = 0.98; // Decreasing this makes the balls act like they are in thick oil
kCollisionDamp = 0.8; // Helps reduce jitter from collisions
kSpin = 1;
kSpinR = kSpin*Math.PI/180;
SW = Stage.width;
SH = Stage.height-100;
kRadiansToDegrees = 180/Math.PI;
kDegreesToRadians = Math.PI/180;
gTx = 0;
gTy = 1;
guys = [];
function tintColor(a,v,mix)
{
var r1 = a >> 16;
var g1 = (a >> 8) & 0xFF;
var b1 = a & 0xFF;
r1 = r1*(1-mix)+v*mix;
g1 = g1*(1-mix)+v*mix;
b1 = b1*(1-mix)+v*mix;
return (r1 << 16) | (g1 << 8) | b1;
}
getRainbowColor = function(r) // r is 0 - 1 - returns a bright color
{
var a = r*Math.PI*2;
var r = 128+Math.cos(a)*127;
var b = 128+Math.cos(a+2*Math.PI/3)*127;
var g = 128+Math.cos(a+4*Math.PI/3)*127;
return (r << 16) | (g << 8) | b;
};
getCandyColor = function(r)
{
var v = 128+random(127);
return tintColor(getRainbowColor(r),v,0.5);
};
MovieClip.prototype.drawCircle = function(x,y,radius)
{
var r = radius;
var n = 8;
var theta = 45*kDegreesToRadians;
var cr = radius/Math.cos(theta/2);
var angle = bA*kDegreesToRadians;
var cangle = angle-theta/2;
this.moveTo(x+r, y);
for (var i=0;i < n;i++)
{
angle += theta;
cangle += theta;
var endX = r*Math.cos (angle);
var endY = r*Math.sin (angle);
var cX = cr*Math.cos (cangle);
var cY = cr*Math.sin (cangle);
this.curveTo(x+cX,y+cY, x+endX,y+endY);
}
};
MovieClip.prototype.drawSphere = function(clr,alpha)
{
var alphas = [alpha,alpha,alpha];
var ratios = [0x00,0x44,0xFF];
var colors = [0xFFFFFF,clr,tintColor(clr,0,0.5)];
var matrix = { matrixType:"box", x:-this.radius, y:-this.radius*2, w:this.radius*3, h:this.radius*3, r: 0 };
this.clear();
this.beginGradientFill("radial",colors,alphas,ratios,matrix);
this.drawCircle(0,0,this.radius);
this.endFill();
};
function clump_about()
{
if (this.dragging)
{
this.vx = _root._xmouse - this.lx;
this.vy = _root._ymouse - this.ly;
this.lx = _root._xmouse;
this.ly = _root._ymouse;
return;
}
// damping
this.vx *= kDamp;
this.vy *= kDamp;
// gravity
this.vx += gTx*kGravWeight;
this.vy += gTy*kGravWeight;
// Object collisions
var fx = 0;
var fy = 0;
for (var i = guys.length-1; i >= 0; --i)
{
if (guys[i] != this && this.hitTest(guys[i]))
{
var dx = (this._x+this.vx) - guys[i]._x;
var dy = (this._y+this.vy) - guys[i]._y;
var dist = Math.sqrt(dx*dx+dy*dy);
var dmax = this.radius+guys[i].radius - dist;
if (dmax > 0) {
var mag1 = dmax*kCollisionDamp/dist;
fx += dx*mag1;
fy += dy*mag1;
}
}
}
this.vx += fx;
this.vy += fy;
// Rim Collisions
var dx = 0-(this._x+this.vx);
var dy = 0-(this._y+this.vy);
var dist = Math.sqrt(dx*dx+dy*dy);
var dmax = dist - (SW/2-this.radius);
if (dmax > 0) {
this.vx += dx*dmax/dist;
this.vy += dy*dmax/dist;
}
this._x += this.vx;
this._y += this.vy;
// Use this to make object point in direction of travel
// this._rotation = Math.atan2(this.vx,this.vy)*kRadiansToDegrees;
}
_root.createEmptyMovieClip("objcell_mc", 1);
objcell_mc._x = SW/2;
objcell_mc._y = SH/2;
// Setup critterrs in random positions
for (var i = 0; i < kNumberGuys; ++i)
{
var mc = objcell_mc.createEmptyMovieClip("critter_mc"+i, i+2);
guys.push(mc);
mc._x = random(SW)-SW/2; // critter position (random)
mc._y = random(SH)-SH/2;
mc.vx = mc.vy = 0;
mc.lx = mc.ly = 0;
mc.radius = kMinRadius+Math.random()*(kMaxRadius-kMinRadius);
var v = 128+random(127);
mc.drawSphere(getCandyColor(i/kNumberGuys),100);
mc.onEnterFrame = clump_about;
mc.onPress = function() { this.dragging = true; this.startDrag(true); };
mc.onRelease = mc.onReleaseOutside = function() { this.dragging = false; this.stopDrag(); };
}
// Show guide
objcell_mc.radius = SW/2;
objcell_mc.drawSphere(0x777777, 30);
objcell_mc.onEnterFrame = function()
{
/* Originaly I was spinning the wheel by doing this:
this._rotation += kSpin;
gTx = Math.sin(this._rotation*Math.PI/180);
gTy = Math.cos(this._rotation*Math.PI/180);
*/
/* But I've switched to a non-rotation method which will be easier to port to
a non Flash-app: */
for (var i = guys.length-1; i >= 0; --i)
{
var guy = guys[i];
var dist = Math.sqrt(guy._x*guy._x + guy._y*guy._y);
var ang = Math.atan2(guy._y, guy._x);
guy._x = Math.cos(ang+kSpinR)*dist;
guy._y = Math.sin(ang+kSpinR)*dist;
}
};
MovieClip.prototype.setupSlider = function(lab,min,max,val,changeFunc)
{
this.minv = min;
this.maxv = max;
this.range = max - min;
this.val = val;
this.changeFunc = changeFunc;
this.its_label.text = lab;
this.knob_mc.useHandCursor = true;
this.knob_mc._x = (this.val - this.minv)*this.bar_mc._width/this.range;
this.setTextVal = function()
{
if (this.range < 1)
this.its_value.text = Math.round(this.val*1000)/1000;
else if (this.range < 10)
this.its_value.text = Math.round(this.val*100)/100;
else if (this.range < 50)
this.its_value.text = Math.round(this.val*10)/10;
else
this.its_value.text = Math.round(this.val);
};
this.setTextVal();
this.changeFunc(this.val);
this.onMouseMove = function()
{
if (!this.dragging) return;
x = this.knob_mc._x;
var r = x/this.bar_mc._width;
this.val = this.minv + r*this.range;
this.setTextVal();
this.changeFunc(this.val);
};
this.knob_mc._xscale = this.knob_mc._yscale = 90;
this.knob_mc.onRollOver = function() { this._xscale = this._yscale = 100; };
this.knob_mc.onRollOut = function() { this._xscale = this._yscale = 90; };
this.knob_mc.onPress = function() { this._parent.dragging = true; this.startDrag(false,0,this._y,this._parent.bar_mc._width,this._y); };
this.knob_mc.onReleaseOutside = this.knob_mc.onRelease = function() { this._parent.dragging = false; this.stopDrag(); };
};
MovieClip.prototype.setupSpring = function()
{
this.speed = 0; // current tween velocity
this.tScale = this._xscale; // target scale
this.stiffness = 0.4;
this.damping = 0.8;
this.springIt = function()
{
var ds = this.tScale - this._xscale;
this.speed += ds*this.stiffness;
this.speed *= this.damping;
this._xscale += this.speed;
this._yscale += this.speed;
if (Math.abs(this.speed) < 0.001)
{
this._xscale = this._yscale = this.tScale;
this.onEnterFrame = undefined;
}
};
this.onRollOver = function() { this.tScale = 100; this.onEnterFrame = this.springIt; };
this.onRollOut = function() { this.tScale = 90; this.onEnterFrame = this.springIt; };
};
MovieClip.prototype.setupButton = function(lab, pressFunc, isSpring)
{
this._xscale = this._yscale = 90;
this.label_txt.text = lab;
this.label_txt.textColor = 0x000000;
this.light_mc._alpha = 0;
this.useHandCursor = true;
this.pressFunc = pressFunc;
this.onRollOver = function() { this._xscale = this._yscale = 100; };
this.onRelease = function() { this.pressFunc(); this.label_txt.textColor = 0x000000; };
this.onRollOut = this.onReleaseOutside = function() { this._xscale = this._yscale = 90; };
if (isSpring) this.setupSpring();
};
Movieclip.prototype.setupToggleButton = function(lab, initState, toggleFunc, isSpring)
{
this.label_txt.text = lab;
this.state = initState;
this.useHandCursor = true;
this.toggleFunc = toggleFunc;
this._xscale = this._yscale = 90;
this.setState = function(s)
{
this.state = s;
this.toggleFunc(this.state);
this.showState();
};
this.showState = function() { this.label_txt.textColor = this.state? 0xFF6666 : 0x000000; };
this.onRollOver = function() { this._xscale = this._yscale = 100; };
this.onRelease = function() { this.setState(!this.state); };
this.onRollOut = this.onReleaseOutside = function () { this._xscale = this._yscale = 90; this.showState(); };
this.showState();
if (isSpring) this.setupSpring();
};
Movieclip.prototype.setupRadioButton = function(lab, initState, setmodeFunc, modeNbr, isSpring)
{
this._xscale = this._yscale = 90;
this.label_txt.text = lab;
this.state = initState;
this.useHandCursor = true;
this.setmodeFunc = setmodeFunc;
this.modeNbr = modeNbr;
this.setState = function(s)
{
this.state = s;
this.showState();
};
this.showState = function() { this.label_txt.textColor = this.state? 0x66FF66 : 0x000000; };
this.onRollOver = function() { this._xscale = this._yscale = 100; };
this.onRelease = function() { this.setModeFunc(this.modeNbr); };
this.onRollOut = this.onReleaseOutside = function () { this._xscale = this._yscale = 90; this.showState(); };
this.showState();
if (isSpring) this.setupSpring();
};
slider1_mc.setupSlider('gravity',0,4, kGravWeight, function (t) { kGravWeight = t; });
slider2_mc.setupSlider('spin', 0 ,10, kSpin, function (t) { kSpin = t; kSpinR = kSpin*Math.PI/180; });
slider3_mc.setupSlider('viscosity', 0 ,0.3, 1-kDamp, function (t) { kDamp = 1-t; });
/* Uncomment this line for Performance testing - Displays framerate every 10 seconds
count = 0;
st = getTimer();
_root.onEnterFrame = function()
{
++count;
if (getTimer() - st >= 10*1000) {
trace("fps = " + count/10);
st = getTimer();
count = 0;
}
};
*/