Worked on this some more…
[kml_flashembed publishmethod=”static” fversion=”10.0.0″ movie=”http://www.bit-101.com/blog/wp-content/uploads/2009/08/Blobs2.swf” width=”600″ height=”600″ targetclass=”flashmovie”]
[/kml_flashembed]
[as]package
{
import com.bit101.components.CheckBox;
import com.bit101.components.Component;
import com.bit101.components.HUISlider;
import com.bit101.components.Knob;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Point;
[SWF(width=600, height=600, backgroundColor=0xdddddd, frameRate=31)]
public class Blobs extends Sprite
{
private var firm:Number;
private var firmSlider:HUISlider;
private var gravity:Number;
private var gravitySlider:HUISlider;
private var radius:Number;
private var radiusSlider:HUISlider;
private var damp:Number;
private var dampSlider:HUISlider;
private var rotate:Number;
private var rotationAmount:Number;
private var rotateKnob:Knob;
private var points:Array;
private var numPoints:int;
private var renderLinesCB:CheckBox;
private var renderLines:Boolean;
private var renderPointsCB:CheckBox;
private var renderPoints:Boolean;
private var renderOutlineSegments:Boolean;
private var renderOutlineSegmentsCB:CheckBox;
private var renderOutlineCurves:Boolean;
private var renderOutlineCurvesCB:CheckBox;
private var ball:Point;
public function Blobs()
{
Component.initStage(stage);
ball = new Point(150, 590);
points = new Array();
numPoints = 40;
firm = 0.05;
firmSlider = new HUISlider(this, 10, 10, “Firmness”, onFirmChange);
firmSlider.width = 580;
firmSlider.setSliderParams(0, .5, firm);
firmSlider.labelPrecision = 3;
gravity = 0.5;
gravitySlider = new HUISlider(this, 10, 22, “Gravity”, onGravityChange);
gravitySlider.width = 580;
gravitySlider.setSliderParams(0, 5, gravity);
gravitySlider.labelPrecision = 2;
radius = 40;
radiusSlider = new HUISlider(this, 10, 34, “Radius”, onRadiusChange);
radiusSlider.width = 580;
radiusSlider.setSliderParams(1, 250, radius);
radiusSlider.labelPrecision = 0;
damp = .99;
dampSlider = new HUISlider(this, 10, 46, “Damp”, onDampChange);
dampSlider.width = 580;
dampSlider.setSliderParams(.9, 1.0, damp);
dampSlider.labelPrecision = 3;
renderPoints = false
renderPointsCB = new CheckBox(this, 10, 65, “Render Points”, onRenderPoints);
renderPointsCB.selected = false;
renderLines = false;
renderLinesCB = new CheckBox(this, 10, 80, “Render Lines”, onRenderLines);
renderLinesCB.selected = false;
renderOutlineSegments = false;
renderOutlineSegmentsCB = new CheckBox(this, 10, 95, “Render Outline Segments”, onRenderOutlineSegments);
renderOutlineSegmentsCB.selected = false;
renderOutlineCurves = true;
renderOutlineCurvesCB = new CheckBox(this, 10, 110, “Render Outline Curves”, onRenderOutlineCurves);
renderOutlineCurvesCB.selected = true;
rotate = 0;
rotationAmount = 0;
rotateKnob = new Knob(this, 250, 60, “Rotate”, onRotateChange);
rotateKnob.minimum = -.1;
rotateKnob.maximum = .1;
rotateKnob.value = 0;
rotateKnob.labelPrecision = 3;
for(var i:int = 0; i < numPoints; i++)
{
var angle:Number = Math.PI * 2 / numPoints * i;
points.push(new Dot(400 + Math.cos(angle) * radius, 400 + Math.sin(angle) * radius));
}
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
private function onFirmChange(event:Event):void
{
firm = firmSlider.value;
}
private function onGravityChange(event:Event):void
{
gravity = gravitySlider.value;
}
private function onRadiusChange(event:Event):void
{
radius = radiusSlider.value;
}
private function onDampChange(event:Event):void
{
damp = dampSlider.value;
}
private function onRotateChange(event:Event):void
{
rotationAmount = rotateKnob.value;
}
private function onRenderPoints(event:Event):void
{
renderPoints = renderPointsCB.selected;
}
private function onRenderLines(event:Event):void
{
renderLines = renderLinesCB.selected;
}
private function onRenderOutlineSegments(event:Event):void
{
renderOutlineSegments = renderOutlineSegmentsCB.selected;
}
private function onRenderOutlineCurves(event:Event):void
{
renderOutlineCurves = renderOutlineCurvesCB.selected;
}
private function enterFrameHandler(event:Event):void
{
graphics.clear();
var cx:Number = 0;
var cy:Number = 0;
for(var i:int = 0; i < numPoints; i++)
{
var point:Dot = points[i];
point.update(gravity);
point.vx *= damp;
point.vy *= damp;
checkBall(point);
cx += point.x;
cy += point.y;
if(renderPoints)
{
graphics.beginFill(0);
graphics.drawCircle(point.x, point.y, 2);
graphics.endFill();
}
}
cx /= numPoints;
cy /= numPoints;
if(renderPoints)
{
graphics.beginFill(0xff0000);
graphics.drawCircle(cx, cy, 5);
graphics.endFill();
}
for(i = 0; i < numPoints; i++)
{
var angle:Number = Math.PI * 2 / numPoints * i + rotate;
var tx:Number = cx + Math.cos(angle) * radius;
var ty:Number = cy + Math.sin(angle) * radius;
point = points[i];
point.vx += (tx - point.x) * firm;
point.vy += (ty - point.y) * firm;
if(renderLines)
{
graphics.lineStyle(0, 0, .25);
graphics.moveTo(cx, cy);
graphics.lineTo(point.x, point.y);
}
if(renderOutlineSegments && i > 0)
{
graphics.lineStyle(0, 0x0000ff, 0.5);
graphics.moveTo(points[i – 1].x, points[i – 1].y);
graphics.lineTo(point.x, point.y);
}
}
if(renderOutlineSegments)
{
graphics.lineStyle(0, 0x0000ff, 0.25);
graphics.moveTo(points[i – 1].x, points[i – 1].y);
graphics.lineTo(points[0].x, points[0].y);
}
if(renderOutlineCurves)
{
renderCurves();
}
graphics.lineStyle(0, 0, .5);
graphics.drawCircle(ball.x, ball.y, 40);
rotate += rotationAmount;
}
private function checkBall(point:Dot):void
{
var dx:Number = point.x – ball.x;
var dy:Number = point.y – ball.y;
var dist:Number = Math.sqrt(dx * dx + dy * dy);
if(dist < 40)
{
point.x = ball.x + dx / dist * 40;
point.y = ball.y + dy / dist * 40;
point.vx = 0;
point.vy = 0;
}
}
private function renderCurves():void
{
var mids:Array = new Array();
for(var i:int = 0; i < points.length - 1; i++)
{
mids[i] = new Point((points[i].x + points[i + 1].x) / 2, (points[i].y + points[i + 1].y) / 2);
}
mids[i] = new Point((points[i].x + points[0].x) / 2, (points[i].y + points[0].y) / 2);
graphics.lineStyle(0, 0xff0000, .5);
graphics.beginFill(0xff0000);
graphics.moveTo(mids[0].x, mids[0].y);
for(i = 1; i < points.length; i++)
{
graphics.curveTo(points[i].x, points[i].y, mids[i].x, mids[i].y);
}
graphics.curveTo(points[0].x, points[0].y, mids[0].x, mids[0].y);
graphics.endFill();
}
}
}[/as]
[as]package
{
public class Dot
{
public var x:Number;
public var y:Number;
public var vx:Number;
public var vy:Number;
public function Dot(x:Number, y:Number)
{
this.x = x;
this.y = y;
vx = 0;
vy = 0;
}
public function update(gravity:Number):void
{
x += vx;
y += vy;
vy += gravity;
if(x < 0)
{
x = 0;
vx = 0;
vy = 0;
}
if(x > 600)
{
x = 600;
vx = 0;
vy = 0;
}
if(y > 600)
{
y = 600;
vy = 0;
vx = 0;
}
}
}
}[/as]
Nice! Reminds me of an old (AS1) experiment of mine – http://rmd.com.au/files/labs/jellytri05.swf
I think you’ve inspired me to update it to AS3.
i think you should add one sublayer for more stable results
like here http://cowboyprogramming.com/2007/01/05/blob-physics/