flash on 2010-1-15
読み込んだ画像をポヨンポヨンさせる。
クリックでdrawTrianglesのラインを表示/非表示。
ドラッグして別の場所をポヨンポヨン。
半径が小さいと振動が発散してしまうのを何とかしたい。
画像 http://www.flickr.com/photos/iwao_kobayashi/2694620804/
package
{
import flash.utils.ByteArray;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Graphics;
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.net.URLRequest;
import flash.system.LoaderContext;
/**
* 読み込んだ画像をポヨンポヨンさせる。
* クリックでdrawTrianglesのラインを表示/非表示。
* ドラッグして別の場所をポヨンポヨン。
* 半径が小さいと振動が発散してしまうのを何とかしたい。
*
* 画像 http://www.flickr.com/photos/iwao_kobayashi/2694620804/
*/
[SWF(width="465",height="465",backgroundColor="0x0",frameRate="30")]
public class FlashTest extends Sprite
{
private var url:String;
private var loader:Loader;
private var domeData:BitmapData;
private var r:Number;
private var h:Number;
private var layer:Sprite;
public function FlashTest()
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
url = "http://farm4.static.flickr.com/3195/2694620804_ab0fc9137f.jpg";
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, comp)
loader.load(new URLRequest(url), new LoaderContext(true));
}
private function comp(e:Event):void
{
domeData = new BitmapData(loader.width, loader.height);
domeData.draw(loader);
addChild(loader);
layer = new Sprite();
addChild(layer);
createDome(168, 290, 113);
stage.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
}
private var circleX:Number;
private var circleY:Number;
private var circleR:Number;
private function onDown(e:MouseEvent):void
{
stage.removeEventListener(MouseEvent.MOUSE_DOWN, onDown);
circleX = mouseX;
circleY = mouseY;
circleR = 0;
stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMove);
stage.addEventListener(MouseEvent.MOUSE_UP, mouseUp);
}
private function mouseMove(e:MouseEvent):void
{
var dx:Number = mouseX - circleX;
var dy:Number = mouseY - circleY;
circleR = Math.sqrt(dx * dx + dy * dy);
var g:Graphics = layer.graphics;
g.clear();
g.lineStyle(1, 0xffffff, 0.5);
g.drawCircle(circleX, circleY, circleR);
}
private function mouseUp(e:MouseEvent):void
{
layer.graphics.clear();
stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMove);
stage.removeEventListener(MouseEvent.MOUSE_UP, mouseUp);
stage.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
if (circleR == 0) return;
createDome(circleX, circleY, circleR);
}
private var dm:Dome;
private function createDome(x:Number, y:Number, r:Number):void
{
if (dm) removeChild(dm);
dm = new Dome(domeData, x, y, r, r, 16, 8);
dm.x = x;
dm.y = y;
addChild(dm);
}
}
}
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.display.TriangleCulling;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Matrix;
class Dome extends Sprite
{
private var _bitmapData :BitmapData;
private var _x:Number;
private var _y:Number;
private var _r:Number;
private var _h:Number;
private var _n:uint;
private var disks:Vector.<Disk>;
private var startIndices:Vector.<uint>;
private var func:DomeFunction;
private var vertices:Vector.<Number>;
private var indices:Vector.<int>;
private var uvtData:Vector.<Number>;
private var mat:Matrix;
private var oscillators:Vector.<Oscillator>;
private var showsLines:Boolean;
private var isMouseOn:Boolean;
/**
*
* @param bitmapData ターゲットのBitmapData
* @param x BitmapData上の位置
* @param y
* @param r 半径
* @param h 高さ
* @param polygon 円をN角形で近似
* @param diskNumber Diskを何枚重ねてDomeとするか
*/
public function Dome(bitmapData:BitmapData,
x:Number, y:Number, r:Number, h:Number,
polygon:uint, diskNumber:uint )
{
_bitmapData = bitmapData;
_x = x;
_y = y;
_r = r;
_h = h;
_n = polygon;
mat = new Matrix(1, 0, 0, 1, -x, -y);
disks = new Vector.<Disk>();
startIndices = new Vector.<uint>();
oscillators = new Vector.<Oscillator>();
func = new CircFunction(_r, _h, 0.1 * _h);
vertices = new Vector.<Number>();
vertices.push(0, 0);
uvtData = new Vector.<Number>();
uvtData.push(x / _bitmapData.width, y / _bitmapData.height);
var i:int;
var height:Number;
var radius:Number;
var disk:Disk;
var osc:Oscillator;
var thickness: Number = _h / diskNumber;
for (i = diskNumber - 1; i > -1; i--)
{
height = i * thickness;
radius = func.domeRadius(height);
disk = new Disk(bitmapData.rect, x, y, radius, polygon)
disks.push(disk);
startIndices.push(vertices.length);
disk.mergeVertices(vertices, vertices.length);
disk.mergeUVData(uvtData, uvtData.length);
osc = new Oscillator();
osc.mass = thickness * radius * radius;
osc.z = height;
osc.spring = _h * _r * _r;
oscillators.push(osc);
}
buildIndices();
redrawDome();
addEventListener(Event.ENTER_FRAME, update);
addEventListener(MouseEvent.CLICK, onClick);
addEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
addEventListener(MouseEvent.MOUSE_OUT, onMouseOut);
}
private function onMouseOut(e:MouseEvent):void
{
isMouseOn = false;
}
private function onMouseOver(e:MouseEvent):void
{
isMouseOn = true;
}
private function onClick(e:MouseEvent):void
{
showsLines = !showsLines;
}
private function buildIndices():void
{
indices = new Vector.<int>();
var i:int;
for (i = 1; i < _n; i++)
{
indices.push(0, i, i + 1);
}
indices.push(0, _n, 1);
var j: int;
var length:uint = disks.length;
for (j = 1; j < length; j++)
{
var jn:uint = j * _n;
var j1n:uint = (j - 1) * _n;
for (i = 1; i < _n; i++)
{
var i1:uint = i + 1;
indices.push(j1n + i, jn + i, jn + i1);
indices.push(j1n + i, jn + i1, j1n + i1);
}
indices.push(jn, jn + _n, jn + 1);
indices.push(jn, jn + 1, j1n + 1);
}
indices = indices.reverse();
}
private function update(e:Event):void
{
if (isMouseOn)
{
var f0:Number = 0.001 * _h * _r * _r;
var dist:Number = Math.sqrt(mouseX * mouseX + mouseY * mouseY);
var fx:Number = - f0 * mouseX / dist;
var fy:Number = - f0 * mouseY / dist;
}
var i:int;
var length:uint = disks.length;
var disk:Disk;
var disk1:Disk;
var osc:Oscillator;
for (i = length - 2; i > -1; i--)
{
disk = disks[i];
disk1 = disks[i + 1]
osc = oscillators[i];
if (isMouseOn) osc.addForce(fx, fy);
osc.update();
disk.moveTo(disk1.x + osc.x, disk1.y + osc.y);
disk.mergeVertices(vertices, startIndices[i]);
}
disk = disks[0];
disk1 = disks[1]
vertices[0] = 2 * disk.x - disk1.x;
vertices[1] = 2 * disk.y - disk1.y;
redrawDome();
}
private function redrawDome():void
{
graphics.clear();
if(showsLines)graphics.lineStyle(0, 0xffffff, 0.5);
graphics.beginBitmapFill(_bitmapData, mat, false, true);
graphics.drawTriangles(vertices, indices, uvtData, TriangleCulling.POSITIVE);
}
public function get bitmapData():BitmapData { return _bitmapData; }
public function get r():Number { return _r; }
public function get h():Number { return _h; }
}
import flash.geom.Rectangle;
class Disk
{
private var _x:Number;
private var _y:Number;
private var _n:uint;
private var _uvData:Vector.<Number>;
private var _dRad:Number;
private var _r:Number;
private var _vertices:Vector.<Number>;
private var _rotation:Number;
public function Disk(rect:Rectangle, trimX:Number, trimY:Number, trimR:Number, n:uint)
{
_x = 0;
_y = 0;
_r = trimR;
_n = n;
_vertices = new Vector.<Number>();
_uvData = new Vector.<Number>();
_dRad = 2 * Math.PI / _n;
_rotation = 0;
initializeUV(rect.width, rect.height, trimX, trimY, trimR);
updateVertices();
}
private function initializeUV(w:Number, h:Number, x:Number, y:Number, r:Number):void
{
for (var i:uint = 0; i < _n; i++)
{
var cos:Number = Math.cos(i * _dRad);
var sin:Number = Math.sin(i * _dRad);
var _2i:uint = i << 1;
_uvData[_2i] = (x + r * cos) / w;
_uvData[_2i + 1] = (y + r * sin) / h;
}
}
private function updateVertices():void
{
var rad:Number = _rotation;
for (var i:uint = 0; i < _n; i++)
{
var cos:Number = Math.cos(rad);
var sin:Number = Math.sin(rad);
var _2i:uint = i << 1;
_vertices[_2i] = _x + _r * cos;
_vertices[_2i + 1] = _y + _r * sin;
rad += _dRad;
}
}
public function move(dx:Number, dy:Number):void
{
for (var i:uint = 0; i < _n; i++)
{
var _2i:uint = i << 1;
_vertices[_2i] += dx;
_vertices[_2i + 1] += dy;
}
_x += dx;
_y += dy;
}
public function moveTo(x:Number, y:Number):void
{
move(x - _x, y - _y);
}
public function mergeVertices(vertices:Vector.<Number>, startIndex:uint):void
{
mergeVector(vertices, startIndex, _vertices);
}
public function mergeUVData(uvData:Vector.<Number>, startIndex:uint):void
{
mergeVector(uvData, startIndex, _uvData);
}
private function mergeVector(into:Vector.<Number>, start:uint, from:Vector.<Number>):void
{
var last:uint = (_n << 1) + start;
var i:uint = start;
var j:uint = 0;
for (; i < last;)
{
into[i++] = from[j++]
}
}
public function get x():Number { return _x; }
public function set x(value:Number):void
{
move(value - _x, 0);
}
public function get y():Number { return _y; }
public function set y(value:Number):void
{
move(0, value - _y);
}
public function get r():Number { return _r; }
public function set r(value:Number):void
{
_r = value;
updateVertices();
}
public function get rotation():Number { return _rotation; }
public function set rotation(value:Number):void
{
_rotation = value;
updateVertices();
}
}
class Oscillator
{
public static const G:Number = 1.0; //振動子にはたらくy方向の重力
public static const K:Number = 1.1; //ばねの強さ
public static const R:Number = 0.9; //減衰の大きさ
public var x:Number;
public var y:Number;
private var vx:Number;
private var vy:Number;
private var ax:Number;
private var ay:Number;
public var mass:Number;
public var z:Number;
public var spring:Number;
public function Oscillator()
{
x = 0;
y = 0;
vx = 0;
vy = 0;
ax = 0;
ay = 0;
}
public function update():void
{
var param:Number = K * spring / z / mass;
ax = - param * x;
ay = - param * y + G;
vx += ax;
vy += ay;
vx *= R;
vy *= R;
x += vx;
y += vy;
}
public function addForce(fx:Number, fy:Number):void
{
var param:Number = z / mass;
vx += param * fx;
vy += param * fy;
}
}
class DomeFunction
{
private var alpha:Number;
private var beta:Number;
private var gamma:Number;
protected function basicFunction(x:Number):Number
{
throw "override basicFunction()";
}
protected function basicInverse(y:Number):Number
{
throw "override basicInverse()";
}
public function DomeFunction(radius:Number, height:Number, depth:Number)
{
gamma = depth;
beta = height + gamma;
alpha = radius / basicInverse(gamma / beta);
}
public final function domeHeight(radius:Number):Number
{
return beta * basicFunction(radius / alpha) - gamma;
}
public final function domeRadius(height:Number):Number
{
return alpha * basicInverse((height + gamma) / beta);
}
}
class CircFunction extends DomeFunction
{
override protected function basicFunction(x:Number):Number
{
return Math.sqrt(1 - x * x);
}
override protected function basicInverse(y:Number):Number
{
return Math.sqrt(1 - y * y);
}
public function CircFunction(radius:Number, height:Number, depth:Number)
{
super(radius, height, depth);
}
}