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); */ } } Jelly Ball 2