forked from: jump floodingアルゴリズムを用いたボロノイ図の生成
jump floodingアルゴリズムを用いたボロノイ図の生成
制御点をドラッグで移動
http://www.comp.nus.edu.sg/~tants/jfa/rong-guodong-phd-thesis.pdf
点の数が増えても処理負荷が変わらず、GPU実装が容易なアルゴリズム
ソフトシャドウにも応用可能
オリジナルソースのJFA部を修正。エッジもきれいに出るように修正。
/**
* Copyright guttyon ( http://wonderfl.net/user/guttyon )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/j3eb
*/
// forked from Nao_u's jump floodingアルゴリズムを用いたボロノイ図の生成
//
// jump floodingアルゴリズムを用いたボロノイ図の生成
//
// 制御点をドラッグで移動
//
// http://www.comp.nus.edu.sg/~tants/jfa/rong-guodong-phd-thesis.pdf
// 点の数が増えても処理負荷が変わらず、GPU実装が容易なアルゴリズム
// ソフトシャドウにも応用可能
//
// オリジナルソースのJFA部を修正。エッジもきれいに出るように修正。
//
//
package {
import flash.display.Sprite;
import flash.events.*;
[SWF(width="256", height="256", backgroundColor="0xFFFFFF", frameRate="15")]
public class FlashTest extends Sprite {
public function FlashTest() {
Main = this;
initialize();
stage.addEventListener(Event.ENTER_FRAME,update);
}
}
}
import flash.display.Sprite;
import flash.events.*
import flash.text.TextField;
import flash.geom.*;
import flash.utils.getTimer;
var Main:Sprite;
var SCREEN_W:Number = 256;
var SCREEN_H:Number = 256;
var Text:TextField
var Text2:TextField
var Tex:ProcTex;
var BITMAP_W:int = 256;
var BITMAP_H:int = 256;
var Pnt:Vector.<ControlPoint> = new Vector.<ControlPoint>;
// 初期化
function initialize():void{
Tex = new ProcTex( BITMAP_W, BITMAP_H );
Tex.Bmp.x = 0;
Tex.Bmp.y = 0;
Text = new TextField();
Text.text = "生成中...";
Text.autoSize = "left";
Main.addChild(Text);
Text2 = new TextField();
Text2.text = "";
Text2.autoSize = "left";
Text2.y = 16;
Main.addChild(Text2);
// 制御点の設置
Pnt[0] = new ControlPoint( new Point(SCREEN_W/2-70, SCREEN_H/2+40) );
Pnt[1] = new ControlPoint( new Point(SCREEN_W/2-50, SCREEN_H/2-120) );
Pnt[2] = new ControlPoint( new Point(SCREEN_W/2+0, SCREEN_H/2-50) );
Pnt[3] = new ControlPoint( new Point(SCREEN_W/2+40, SCREEN_H/2-30) );
Pnt[4] = new ControlPoint( new Point(SCREEN_W/2+100, SCREEN_H/2+120) );
Pnt[5] = new ControlPoint( new Point(SCREEN_W/2-40, SCREEN_H/2+40) );
Pnt[6] = new ControlPoint( new Point(SCREEN_W/2+10, SCREEN_H/2+8) );
Pnt[7] = new ControlPoint( new Point(SCREEN_W/2-40, SCREEN_H/2-8) );
Pnt[8] = new ControlPoint( new Point(SCREEN_W/2-40, SCREEN_H/2-10) );
Pnt[9] = new ControlPoint( new Point(SCREEN_W/2+40, SCREEN_H/2-10) );
}
// 更新
function update(e :Event):void{
var time:int = getTimer();
Tex.draw();
var endTime:int = getTimer() - time;
Text.text = " 生成時間:" + endTime + "[ms]";
}
import flash.display.Bitmap;
import flash.display.BitmapData;
// テクスチャ生成クラス
class ProcTex{
public var BmpData:BitmapData;
public var TmpBmpData:BitmapData;
public var Bmp:Bitmap;
public var Width:int;
public var Height:int;
public function ProcTex( w:int, h:int ){
Width = w;
Height = h;
BmpData = new BitmapData(Width, Height, false, 0xffffff);
TmpBmpData = new BitmapData(Width, Height, false, 0xffffff);
Bmp = new Bitmap(BmpData);
Bmp.x = 0.0;
Bmp.y = 0.0;
Bmp.scaleX = 1.0;
Bmp.scaleY = 1.0;
Main.addChild(Bmp);
}
public function draw():void{
drawBmpData( BmpData );
}
public function drawBmpData( bmpData:BitmapData ):void{
var col:uint;
bmpData.lock();
// 全体を 距離 = 0xff でクリア
for( var x:int=0; x<Width; x++ ){
for( var y:int=0; y<Height; y++ ){
col = 0; // 青成分に距離を入れておく
bmpData.setPixel(x, y, col);
}
}
// 基準点を描画
bmpData.setPixel( Pnt[0].Sp.x, Pnt[0].Sp.y, (uint(Pnt[0].Sp.x) << 8) + (uint(Pnt[0].Sp.y) << 16)+0xff000010);
bmpData.setPixel( Pnt[1].Sp.x, Pnt[1].Sp.y, (uint(Pnt[1].Sp.x) << 8) + (uint(Pnt[1].Sp.y) << 16)+0xff000020);
bmpData.setPixel( Pnt[2].Sp.x, Pnt[2].Sp.y, (uint(Pnt[2].Sp.x) << 8) + (uint(Pnt[2].Sp.y) << 16)+0xff000030);
bmpData.setPixel( Pnt[3].Sp.x, Pnt[3].Sp.y, (uint(Pnt[3].Sp.x) << 8) + (uint(Pnt[3].Sp.y) << 16)+0xff000040);
bmpData.setPixel( Pnt[4].Sp.x, Pnt[4].Sp.y, (uint(Pnt[4].Sp.x) << 8) + (uint(Pnt[4].Sp.y) << 16)+0xff000060);
bmpData.setPixel( Pnt[5].Sp.x, Pnt[5].Sp.y, (uint(Pnt[5].Sp.x) << 8) + (uint(Pnt[5].Sp.y) << 16)+0xff000080);
bmpData.setPixel( Pnt[6].Sp.x, Pnt[6].Sp.y, (uint(Pnt[6].Sp.x) << 8) + (uint(Pnt[6].Sp.y) << 16)+0xff0000a0);
bmpData.setPixel( Pnt[7].Sp.x, Pnt[7].Sp.y, (uint(Pnt[7].Sp.x) << 8) + (uint(Pnt[7].Sp.y) << 16)+0xff0000c0);
bmpData.setPixel( Pnt[8].Sp.x, Pnt[8].Sp.y, (uint(Pnt[8].Sp.x) << 8) + (uint(Pnt[8].Sp.y) << 16)+0xff0000d0);
bmpData.setPixel( Pnt[9].Sp.x, Pnt[9].Sp.y, (uint(Pnt[9].Sp.x) << 8) + (uint(Pnt[9].Sp.y) << 16)+0xff0000ff);
bmpData.unlock();
// jumpFlood処理を大きいほうから順番に行う
jumpFlood( bmpData, 1 ); // 1+JFA
jumpFlood( bmpData, 128 );
jumpFlood( bmpData, 64 );
jumpFlood( bmpData, 32 );
jumpFlood( bmpData, 16 );
jumpFlood( bmpData, 8 );
jumpFlood( bmpData, 4 );
jumpFlood( bmpData, 2 );
jumpFlood( bmpData, 1 );
//jumpFlood( bmpData, 1 ); // 最後にもう一回、幅1で実行するとちょっと綺麗になる?
}
public function dist(x:int, y:int, p:uint):int{
var dx:int, dy:int;
dx=x-((p>>8)&0xff);
dy=y-((p>>16)&0xff);
return dx*dx+dy*dy; // dx^2+dy^2はダメなの?
}
public function jumpFlood( bmpData:BitmapData, w:int ):void{
TmpBmpData = bmpData.clone();
var p0:uint, p1:uint, p2:uint, p3:uint, p5:uint, p6:uint, p7:uint, p8:uint;
var col:uint;
var d0:int, d1:int;
bmpData.lock();
for( var x:int=0; x<Width; x++ ){
for( var y:int=0; y<Height; y++ ){
col = TmpBmpData.getPixel( x, y );
// 周囲8方向の指定された距離だけ離れたピクセルを拾ってくる
p0 = TmpBmpData.getPixel( x-w, y-w );
p1 = TmpBmpData.getPixel( x , y-w );
p2 = TmpBmpData.getPixel( x+w, y-w );
p3 = TmpBmpData.getPixel( x-w, y );
p5 = TmpBmpData.getPixel( x+w, y );
p6 = TmpBmpData.getPixel( x-w, y+w );
p7 = TmpBmpData.getPixel( x , y+w );
p8 = TmpBmpData.getPixel( x+w, y+w );
if(col!=0)d0=dist(x,y,col);
else d0 = 600*600;;
// 拾った中でもっとも基準点からの距離が小さいピクセルを自分のピクセルにコピー
d1=dist(x,y,p0);
if( p0 != 0 && d1 < d0 ) { col = p0; d0 = d1;}
d1=dist(x,y,p1);
if( p1 != 0 && d1 < d0 ) { col = p1; d0 = d1;}
d1=dist(x,y,p2);
if( p2 != 0 && d1 < d0 ) { col = p2; d0 = d1;}
d1=dist(x,y,p3);
if( p3 != 0 && d1 < d0 ) { col = p3; d0 = d1;}
d1=dist(x,y,p5);
if( p5 != 0 && d1 < d0 ) { col = p5; d0 = d1;}
d1=dist(x,y,p6);
if( p6 != 0 && d1 < d0 ) { col = p6; d0 = d1;}
d1=dist(x,y,p7);
if( p7 != 0 && d1 < d0 ) { col = p7; d0 = d1;}
d1=dist(x,y,p8);
if( p8 != 0 && d1 < d0 ) { col = p8; d0 = d1;}
bmpData.setPixel(x, y, col);
}
}
}
}
// 制御点マーカークラス
class ControlPoint{
public var Sp:Sprite;
public var isEnable:Boolean = false;
public var Pos:Point;
public function ControlPoint( p:Point ){
Sp=new Sprite();
Pos = p;
Sp.x = Pos.x;
Sp.y = Pos.y;
setEnable( true );
Main.stage.addChild(Sp);
Sp.addEventListener(MouseEvent.MOUSE_UP, function (event:MouseEvent):void{ Sp.stopDrag(); });
Sp.addEventListener(MouseEvent.MOUSE_DOWN, function (event:MouseEvent):void{ if( isEnable ) Sp.startDrag(); });
}
public function setEnable( flg:Boolean ):void{
if( flg == true && isEnable == false ){
Sp.graphics.clear();
Sp.graphics.lineStyle(1.4,0x000000);
Sp.graphics.beginFill(0xe0d000,1);
Sp.graphics.drawCircle(0,0,8.0);
Sp.graphics.endFill();
}else if( flg == false && isEnable == true ){
Sp.graphics.clear();
}
isEnable = flg;
}
}