06
// forked from gyuque's Jelly Ball 2
package
{
import flash.display.*;
import flash.geom.*;
import flash.events.*;
[SWF(width="465", height="465", backgroundColor="0xffffff", frameRate="31")]
public class JBall extends Sprite
{
private static const N:int = 61;
private static const R:int = 60;
public static const JGRAD_C:Array = [0x002255, 0x3377cc];
public static const JGRAD_A:Array = [0.4, 0.7];
public static const JGRAD_R:Array = [0, 255];
private var mJGradTrans:Matrix = new Matrix();
public static const HGRAD_C:Array = [0xaaeeff, 0xaaeeff, 0xaaeeff];
public static const HGRAD_A:Array = [0.3, 0.02, 0];
public static const HGRAD_R:Array = [0, 190, 191];
private var mNodes:Array = [];
private var mCenter:Point = new Point();
private var mGVec:Point = new Point();
private var mField:Sprite = new Sprite();
private var mBallLayer:Sprite = new Sprite();
private var mBoxHW:int = 152;
private var mBoxHH:int = 168;
private var mFAngle:Number = 0;
function JBall()
{
generateNodes(N);
addChild(mField);
mField.addChild(mBallLayer);
mField.x = 232;
mField.y = 232;
var g:Graphics = mField.graphics;
g.beginFill(0);
g.drawRect(-mBoxHW, -mBoxHH, 2*mBoxHW, 2*mBoxHH);
mCenter.x = 0;
mCenter.y = 0;
var i:int, nd:JNode;
var pt:Point = new Point();
for (i = 0;i < N;i++)
{
nd = JNode(mNodes[i]);
generateRoundPos(nd.theta, pt);
nd.x = pt.x;
nd.y = pt.y;
}
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function onEnterFrame(e:Event):void
{
if (!(isNaN(mouseX) || mouseX < -100 || mouseX > 600)) {
var mx:Number = (mouseX - 232) * 0.0003;
const DPI:Number = Math.PI*2;
mFAngle += mx;
if (mFAngle < 0) mFAngle += DPI;
else if (mFAngle > DPI) mFAngle -= DPI;
setGVec(mFAngle);
}
tick();
move();
draw();
}
private function setGVec(a:Number):void
{
mGVec.y = Math.cos(a) * .7;
mGVec.x = Math.sin(a) * .7;
mField.rotation = a * 180 / Math.PI;
}
private function generateNodes(n:int):void
{
var i:int, a:Number, nd:JNode;
for (i = 0;i < n;i++)
{
nd = new JNode();
a = Math.PI * 2.0 * Number(i)/Number(n);
nd.theta = a;
mField.addChild(nd);
mNodes.push(nd);
}
}
private function generateRoundPos(a:Number, outpt:Point, r:Number = R):void
{
outpt.x = mCenter.x + Math.sin(a) * r;
outpt.y = mCenter.y - Math.cos(a) * r;
}
private static const be:Number = 0.4;
private function tick():void
{
var i:int, nd:JNode;
var nxt:Number;
calcCenter();
for (i = 0;i < N;i++)
{
nd = mNodes[i];
nd.v.x *= 0.98;
nd.v.y *= 0.98;
}
var prs:Number = calcPressure();
for (i = 0;i < N;i++)
{
nd = mNodes[i];
nd.F.x *= -0.2;
nd.F.y *= -0.2;
addVec(nd.v, nd.F);
nd.F.x = 0;
nd.F.y = 0;
addVec(nd.v, mGVec);
applyInternalForce(nd, prs);
addVec(nd.v, nd.F);
if (nd.v.y > 0) {
nxt = nd.y + nd.v.y;
if (nxt > mBoxHH) {
nd.nextV.y = -nd.v.y * be;
nd.v.y = mBoxHH - nd.y;
}
} else {
nxt = nd.y + nd.v.y;
if (nxt < -mBoxHH) {
nd.nextV.y = -nd.v.y * be;
nd.v.y = -mBoxHH - nd.y;
}
}
if (nd.v.x > 0) {
nxt = nd.x + nd.v.x;
if (nxt > mBoxHW) {
nd.nextV.x = -nd.v.x * be;
nd.v.x = mBoxHW - nd.x;
}
} else {
nxt = nd.x + nd.v.x;
if (nxt < -mBoxHW) {
nd.nextV.x = -nd.v.x * be;
nd.v.x = -mBoxHW - nd.x;
}
}
}
}
private function move():void
{
var i:int, nd:JNode;
for (i = 0;i < N;i++)
{
nd = mNodes[i];
nd.x += nd.v.x;
nd.y += nd.v.y;
if (!isNaN(nd.nextV.x)) {
nd.v.x = nd.nextV.x;
nd.nextV.x = NaN;
}
if (!isNaN(nd.nextV.y)) {
nd.v.y = nd.nextV.y;
nd.nextV.y = NaN;
}
// applyInternalForce(nd, 0);
}
}
private function draw():void
{
var i:int, nd:JNode;
var g:Graphics = mBallLayer.graphics;
g.clear();
var ar:Number = 0, rx:Number, ry:Number;
for (i = 0;i < N;i++)
{
nd = mNodes[i];
rx = mCenter.x - nd.x;
ry = mCenter.y - nd.y;
ar += Math.sqrt(rx*rx + ry*ry);
}
g.beginFill(0x0011aa, 0.4);
drawBallShape(g, 0);
g.endFill();
ar /= Number(R);
var sx:Number, sy:Number;
var r:Number = 0.06;
mJGradTrans.createGradientBox(ar*4, ar*4, 0, mCenter.x + Math.sin(mFAngle-2.4)*40 - ar*2, mCenter.y + Math.cos(mFAngle-2.4)*40 - ar*2);
g.beginGradientFill(GradientType.RADIAL, JGRAD_C, JGRAD_A, JGRAD_R, mJGradTrans);
drawBallShape(g, r);
g.endFill();
r = 0.13;
mJGradTrans.createGradientBox(ar*2.2, ar*2.2, 0, mCenter.x + Math.sin(mFAngle-2.8)*40 - ar, mCenter.y + Math.cos(mFAngle-2.8)*40 - ar);
g.beginGradientFill(GradientType.RADIAL, HGRAD_C, HGRAD_A, HGRAD_R, mJGradTrans);
drawBallShape(g, r);
g.endFill();
}
private function drawBallShape(g:Graphics, r:Number):void
{
var nd:JNode;
var nd2:JNode;
var sx:Number, sy:Number;
var sx2:Number, sy2:Number;
for (var i:int = 0;i < N;i+=2)
{
nd = mNodes[i];
nd2 = mNodes[i+1];
sx = nd.x*(1-r*0.9) + mCenter.x*r*0.9;
sy = nd.y*(1-r*0.9) + mCenter.y*r*0.9;
sx2 = nd2.x*(1-r) + mCenter.x*r;
sy2 = nd2.y*(1-r) + mCenter.y*r;
if (i == 0) {
g.moveTo(sx, sy);
i--;
}
else {
g.curveTo(sx, sy, sx2, sy2);
}
}
nd = mNodes[N-1];
nd2 = mNodes[0];
sx = nd.x*(1-r) + mCenter.x*r;
sy = nd.y*(1-r) + mCenter.y*r;
sx2 = nd2.x*(1-r) + mCenter.x*r;
sy2 = nd2.y*(1-r) + mCenter.y*r;
g.curveTo(sx2, sy2, sx, sy);
}
private static function addVec(v0:Point, v:Point):void
{
v0.x += v.x;
v0.y += v.y;
}
private function calcPressure():Number
{
var p:Number = 0;
var k:Number = N * R;
var vx:Number, vy:Number;
for (var i:int = 0;i < N;i++) {
var nd:JNode = mNodes[i];
vx = nd.x - mCenter.x;
vy = nd.y - mCenter.y;
p += Math.sqrt(vx*vx + vy*vy);
}
p = (k - p) * 0.3;
if (p < 0) p = 0;
if (p > 20) p = 20;
return p;
}
private function applyInternalForce(nd:JNode, prs:Number):void
{
var p:Point = new Point();
generateRoundPos(nd.theta, p, R+prs);
var vx:Number = (p.x-(nd.x+nd.v.x*0.1));
var vy:Number = (p.y-(nd.y+nd.v.y*0.1));
// var e:Number = 1.0 / (Math.sqrt(vx*vx + vy*vy)*0.01 + 1.0);
// if (e > 1) e = 1;
var e:Number = 0.13;
vx *= e;
vy *= e;
var nrm:Number = Math.sqrt(vx*vx + vy*vy);
if (nrm > 5) {
vx *= 5 / nrm;
vy *= 5 / nrm;
}
nd.F.x += vx;
nd.F.y += vy;
}
private function calcCenter():void
{
var cx:Number = 0;
var cy:Number = 0;
var i:int, nd:JNode;
for (i = 0;i < N;i++)
{
nd = mNodes[i];
cx += nd.x;
cy += nd.y;
}
mCenter.x = cx / Number(N);
mCenter.y = cy / Number(N);
}
}
}
class JNode extends flash.display.Sprite
{
import flash.display.*;
import flash.geom.*;
public var theta:Number;
public var v:Point = new Point(0, 0);
public var F:Point = new Point(0, 0);
public var nextV:Point = new Point(NaN, NaN);
function JNode()
{
/*
var g:Graphics = graphics;
g.beginFill(0xff9933);
g.drawCircle(0, 0, 3);
*/
}
}