シャボン玉 / Soap bubble
クリックすると何かが起こるかも。
※つぶれなくなりました
・大きさや解像度を最適化。
・かなり高速化。よりつぶれにくく。
・輪郭を滑らかに。回転しにくく!←New!
/**
* Copyright saharan ( http://wonderfl.net/user/saharan )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/pnIc
*/
/*
* シャボン玉/Soap bubble
*
* クリックすると何かが起こるかも。
* ※つぶれなくなりました
*
* ・大きさや解像度を最適化。
* ・かなり高速化。よりつぶれにくく。
* ・輪郭を滑らかに。回転しにくく!←New!
*/
package {
import flash.filters.BlurFilter;
import flash.geom.*;
import flash.display.*;
import flash.events.*;
import flash.events.*;
import flash.display.*;
[SWF(frameRate = "60")]
public class SorpBubble extends Sprite {
private var bubbles:Vector.<Bubble>;
private var press:Boolean;
private var bitmap:BitmapData;
public function SorpBubble() {
initialize();
}
private function initialize():void {
bubbles = new Vector.<Bubble>();
for(var i:int = 0; i < 60; i++)
bubbles[i] = new Bubble(50 + Math.random() * 365,
50 + Math.random() * 365, 10 + Math.random() * 30);
bitmap = new BitmapData(465, 465, false, 0);
addChild(new Bitmap(bitmap));
addEventListener(Event.ENTER_FRAME, frame);
stage.addEventListener(MouseEvent.MOUSE_DOWN, function():void { press = true; });
stage.addEventListener(MouseEvent.MOUSE_UP, function():void { press = false; });
}
private function frame(e:Event):void {
var b:Bubble;
if(press) {
for each(b in bubbles) {
b.collision(mouseX, mouseY);
}
}
var sp:Sprite = new Sprite();
for each(b in bubbles) {
b.move();
b.draw(sp.graphics);
}
bitmap.colorTransform(bitmap.rect, new ColorTransform(1, 1, 1, 0.85));
bitmap.draw(sp);
}
}
}
import flash.display.*;
class Bubble {
public var springs:Vector.<Spring>;
public var vertices:Vector.<Vertex>;
public var windAngle:Number;
private var wind:Number;
private var targetArea:Number;
private var numVertices:int;
public function Bubble(x:Number, y:Number, size:Number) {
wind = 0;
springs = new Vector.<Spring>();
vertices = new Vector.<Vertex>();
var i:int;
var j:int;
numVertices = size / 2 + 2;
var ang:Number = 0;
var dAng:Number = Math.PI * 2 / numVertices;
for(i = 0; i < numVertices; i++) {
vertices[i] = new Vertex(x + Math.cos(ang) * size, y + Math.sin(ang) * size);
ang += dAng;
}
targetArea = calcArea();
for(i = 0; i < numVertices; i++)
springs[i] = new Spring(vertices[i], vertices[(i + 1) % numVertices]);
windAngle = Math.random() * Math.PI * 2;
}
public function move():void {
for each(var s:Spring in springs)
s.move();
var i:int;
var v:Vertex;
var outer:Number = 0; // 外周
for(i = 0; i < numVertices; i++) {
v = vertices[i];
var next:Vertex = vertices[(i + 1) % numVertices];
var dx:Number = next.x - v.x;
var dy:Number = next.y - v.y;
var invDistance:Number = Math.sqrt(dx * dx + dy * dy);
outer += invDistance;
invDistance = 1 / invDistance;
v.nx = dy * invDistance; // 頂点法線ベクトルを計算
v.ny = -dx * invDistance;
}
var k:Number = (targetArea - calcArea()) / outer * 2;
for each(v in vertices) {
v.vx += v.nx * k;
v.vy += v.ny * k;
v.vx += wind * Math.cos(windAngle) * 3;
v.vy += wind * Math.sin(windAngle) * 3;
v.move();
}
wind += Math.random() * 0.005 - 0.0025;
wind *= 0.99;
windAngle += Math.random() * 0.05 - 0.025;
}
public function draw(graphics:Graphics):void {
for(var i:int = 0; i < numVertices; i++) {
const v:Vertex = vertices[i];
const prev:Vertex = vertices[(i + numVertices - 1) % numVertices];
const next:Vertex = vertices[(i + 1) % numVertices];
graphics.lineStyle(1, v.color);
graphics.moveTo((prev.x + v.x) * 0.5, (prev.y + v.y) * 0.5);
graphics.curveTo(v.x, v.y, (v.x + next.x) * 0.5, (v.y + next.y) * 0.5);
}
}
public function collision(x:Number, y:Number):void {
for each(var v:Vertex in vertices) {
var dx:Number = x - v.x;
var dy:Number = y - v.y;
if(dx * dx + dy * dy < 10000) {
var dist:Number = Math.sqrt(dx * dx + dy * dy);
dx /= dist;
dy /= dist;
var overlap:Number = 100 - dist;
v.vx -= dx * overlap * 0.0125;
v.vy -= dy * overlap * 0.0125;
v.x -= dx * overlap * 0.00625;
v.y -= dy * overlap * 0.00625;
}
}
}
public function calcArea():Number { // 外積から大体の面積を求める
var area:Number = 0;
for(var i:int = 0; i < numVertices; i++) {
var next:int = (i + 1) % numVertices;
area += vertices[i].x * vertices[next].y - vertices[i].y * vertices[next].x;
}
area *= 0.5;
return area;
}
}
class Spring {
public var v1:Vertex;
public var v2:Vertex;
public var targetDistance:Number;
public function Spring(v1:Vertex, v2:Vertex) {
this.v1 = v1;
this.v2 = v2;
targetDistance = calcDistance();
}
public function move():void {
var dx:Number = v2.x - v1.x;
var dy:Number = v2.y - v1.y;
var rx:Number = v2.vx - v1.vx;
var ry:Number = v2.vy - v1.vy;
var distance:Number = calcDistance();
var invDistance:Number = 1 / distance;
dx *= invDistance;
dy *= invDistance;
var k:Number = (distance - targetDistance) * 0.5;
var dot:Number = (dx * rx + dy * ry) * 0.125;
dx *= k + dot;
dy *= k + dot;
v1.vx += dx;
v1.vy += dy;
v2.vx -= dx;
v2.vy -= dy;
}
public function calcDistance():Number {
const dx:Number = v2.x - v1.x;
const dy:Number = v2.y - v1.y;
return Math.sqrt(dx * dx + dy * dy);
}
}
class Vertex {
public var x:Number;
public var y:Number;
public var vx:Number;
public var vy:Number;
public var nx:Number; // 頂点法線ベクトル
public var ny:Number;
public var color:uint;
public function Vertex(x:Number, y:Number) {
this.x = x;
this.y = y;
vx = vy = 0;
nx = ny = 0;
}
public function move():void {
color = (Math.random() * 96 + 160) << 16 | (Math.random() * 96 + 160) << 8 | (Math.random() * 96 + 160);
x += vx;
y += vy;
if(x < 10) vx += 10 - x;
if(x > 455) vx += 455 - x;
if(y < 10) vy += 10 - y;
if(y > 455) vy += 455 - y;
vx *= 0.95; // damping
vy *= 0.95;
}
}