forked from: ボロノイ図
forked from ボロノイ図 (diff: 500)
ボロノイ図描画 Quasimondo 先生が 2002 年に書いたコードの写経です。 ステージをクリックしてね。何度も何度もクリックしてね。 オリジナル:http://www.quasimondo.com/archives/000110.php 説明:http://aquioux.blog48.fc2.com/blog-entry-654.html --- 処理を色々変えて試みました.
ActionScript3 source code
/**
* Copyright kawakita ( http://wonderfl.net/user/kawakita )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/qDnM
*/
// forked from Aquioux's ボロノイ図
package {
import flash.display.Graphics;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import net.hires.debug.Stats;
[SWF(width = "465", height = "465", frameRate = "30", backgroundColor = "#FFFFFF")]
/**
* ボロノイ図描画
* Quasimondo 先生が 2002 年に書いたコードの写経です。
* ステージをクリックしてね。何度も何度もクリックしてね。
* オリジナル:http://www.quasimondo.com/archives/000110.php
* 説明:http://aquioux.blog48.fc2.com/blog-entry-654.html
*
* ---
* 処理を色々変えて試みました.
*/
public class Main extends Sprite {
private const STAGE_WIDTH:uint = stage.stageWidth;
private const STAGE_HEIGHT:uint = stage.stageHeight;
private var dots:Array; // Dot の配列
private var points:Array; // VoronoiPoint の配列
private var dotLayer:Sprite; // ドットのコンテナ
private var lineLayer:Sprite; // 線描画用レイヤ
private var textField:TextField;// "Click STAGE"
public function Main():void {
// 配列初期化
dots = [];
points = [];
stage.frameRate = 120;
// コンテナ等
addChild(dotLayer = new Sprite());
addChild(lineLayer = new Sprite());
addChild(new Stats());
/*
textField = new TextField();
textField.defaultTextFormat = new TextFormat("_serif", 20, 0x000000);
textField.text = "Click STAGE";
textField.autoSize = TextFieldAutoSize.LEFT;
textField.selectable = false;
textField.x = (STAGE_WIDTH - textField.width) / 2;
textField.y = (STAGE_HEIGHT - textField.height) / 2;
addChild(textField);
*/
// 最初に Dot を3つ配置
for (var i:uint = 0; i < 50; i++) {
addDot( Math.random() * STAGE_WIDTH, Math.random() * STAGE_HEIGHT );
}
// イベントリスナー
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
//stage.addEventListener(MouseEvent.CLICK, clickHandler);
}
// ENTER_FRAME
private function enterFrameHandler(event:Event):void {
var n:uint = dots.length;
for (var i:uint = 0; i < n; i++) {
var dot:Dot = dots[i];
dot.update();
}
setVoronoi(); // ボロノイ境界線の計算
drawVolonoi(); // ボロノイ境界線の描画
}
// ボロノイ境界線の計算
private function setVoronoi():void {
var idx1:int, i:uint, j:uint, k:uint, a:Number, b:Number, sub_a:Number;
var x:Number, y:Number, x0:Number, y0:Number, x1:Number, y1:Number;
var n:uint = dots.length;
var m:uint = n + 3;
var m1:uint = m + 1;
var sw_1:Number = STAGE_WIDTH - 1;
var sh_1:Number = STAGE_HEIGHT - 1;
for ( i = 0; i < n; i++ ) {
x0 = dots[i].x;
y0 = dots[i].y;
idx1 = i * m1 + 1;
for ( j = i + 1; j < n; j++ ) {
x1 = dots[j].x;
y1 = dots[j].y;
if ( x1 == x0 ) {
a = 0;
} else if ( y1 == y0 ) {
a = 10000;
} else {
a = -1 / ( ( y1 - y0 ) / ( x1 - x0 ) );
}
b = ( ( y0 + y1 ) >> 1 ) - ( ( a * ( x0 + x1 ) ) >> 1 );
if (a > -1 && a <= 1) {
if ( !points[idx1] ) {
points[idx1] = new VoronoiPoint( 0, b, sw_1, a * sw_1 + b );
}else {
points[idx1].setPoints( 0, b, sw_1, a * sw_1 + b );
}
} else {
sub_a = 1 / a;
if ( !points[idx1] ) {
points[idx1] = new VoronoiPoint( - b * sub_a, 0, ( sh_1 - b ) * sub_a, sh_1 );
}else {
points[idx1].setPoints( - b * sub_a, 0, ( sh_1 - b ) * sub_a, sh_1 );
}
}
idx1++;
}
if ( !points[idx1] ) {
points[idx1] = new VoronoiPoint( 0, 0, STAGE_WIDTH, 0 );
}else {
points[idx1].setPoints( 0, 0, STAGE_WIDTH, 0 );
}
if ( !points[++idx1] ) {
points[idx1] = new VoronoiPoint( 0, 0, 0, STAGE_HEIGHT );
}else {
points[idx1].setPoints( 0, 0, 0, STAGE_HEIGHT );
}
if ( !points[++idx1] ) {
points[idx1] = new VoronoiPoint( STAGE_WIDTH, 0, STAGE_WIDTH, STAGE_HEIGHT );
}else {
points[idx1].setPoints( STAGE_WIDTH, 0, STAGE_WIDTH, STAGE_HEIGHT );
}
if ( !points[++idx1] ) {
points[idx1] = new VoronoiPoint( 0, STAGE_HEIGHT, STAGE_WIDTH, STAGE_HEIGHT );
}else {
points[idx1].setPoints( 0, STAGE_HEIGHT, STAGE_WIDTH, STAGE_HEIGHT );
}
}
var aaaa:Number;
var maxVal:Number = -Number.MAX_VALUE;
var p0:VoronoiPoint, p1:VoronoiPoint;
for (i = 0; i < n; i++) {
x0 = dots[i].x;
y0 = dots[i].y;
for (j = 0; j < m1; j++) {
if (j != i) {
if (j > i) {
p0 = points[ i * m + j ];
} else {
p0 = points[ j * m + i ];
}
if ( p0.sx > maxVal ) {
for ( k = i + 1; k < m1; k++) {
if (k != j) {
p1 = points[i * m + k];
if ( p1.sx > maxVal ) {
aaaa = p0.a * x0 + p0.b - y0;
x = - ( p1.b - p0.b ) / ( p1.a - p0.a );
y = p0.a * x + p0.b;
if ( aaaa * ( p0.a * p1.sx + p0.b - p1.sy ) < 0 ) {
p1.setPoints( x, y, p1.ex, p1.ey );
}
if ( aaaa * ( p0.a * p1.ex + p0.b - p1.ey ) < 0) {
if ( p1.sx == x) {
p1.sx = maxVal;
} else {
p1.setPoints( p1.sx, p1.sy, x, y );
}
}
}
}
}
}
}
}
}
}
// ボロノイ境界線の描画
private function drawVolonoi():void {
var idx:uint;
var n:uint = dots.length;
var m:uint = n + 3;
var m1:uint = m + 1;
var p:VoronoiPoint;
var maxVal:Number = -Number.MAX_VALUE;
var graphics:Graphics = lineLayer.graphics;
graphics.clear();
graphics.lineStyle(0, 0x666666);
for ( var i:uint = 0; i < n; i++ ) {
idx = i * m1 + 1;
for ( var j:uint = i + 1; j < m1; j++ ) {
p = points[idx];
if ( p.sx > maxVal ) {
graphics.moveTo( p.sx, p.sy );
graphics.lineTo( p.ex, p.ey );
}
idx++;
}
}
}
// マウスイベント
private function clickHandler(event:MouseEvent):void {
if (textField) {
removeChild(textField);
textField = null;
}
addDot(mouseX, mouseY);
}
// Dot をコンテナ上に追加する
private function addDot(x:Number, y:Number):void {
var dot:Dot = new Dot();
dot.x = x;
dot.y = y;
dots.push(dot);
dotLayer.addChild(dot);
}
}
}
// --------------------------------------------------------------------------------
class VoronoiPoint {
private var _a:Number;
private var _b:Number;
private var _sx:Number;
private var _sy:Number;
private var _ex:Number;
private var _ey:Number;
public function VoronoiPoint( sx:Number, sy:Number, ex:Number, ey:Number ) {
setPoints( sx, sy, ex, ey );
}
public function setPoints( sx:Number, sy:Number, ex:Number, ey:Number ):void {
_sx = sx; _sy = sy; _ex = ex; _ey = ey;
_a = ( _ey - _sy ) / ( _ex - _sx );
_b = _sy - _a * _sx;
}
public function get a():Number { return _a; }
public function get b():Number { return _b; }
public function get sx():Number { return _sx; }
public function set sx(value:Number):void {
_sx = value;
_a = ( _ey - _sy ) / ( _ex - _sx );
_b = _sy - _a * _sx;
}
public function get sy():Number { return _sy; }
public function set sy(value:Number):void {
_sy = value;
_a = ( _ey - _sy ) / ( _ex - _sx );
_b = _sy - _a * _sx;
}
public function get ex():Number { return _ex; }
public function set ex(value:Number):void {
_ex = value;
_a = ( _ey - _sy ) / ( _ex - _sx );
_b = _sy - _a * _sx;
}
public function get ey():Number { return _ey; }
public function set ey(value:Number):void {
_ey = value;
_a = ( _ey - _sy ) / ( _ex - _sx );
_b = _sy - _a * _sx;
}
}
// --------------------------------------------------------------------------------
import flash.display.Shape;
import flash.events.Event;
class Dot extends Shape {
private const RADIUS:uint = 3;
private var vx:Number = 2.0; // X軸
private var vy:Number = 2.0; // Y軸ベロシティ
private var sw:Number; // ステージ
private var sh:Number; // ステージ高
public function Dot():void{
graphics.beginFill(0xCC0033);
graphics.drawCircle(0, 0, RADIUS);
graphics.endFill();
addEventListener(Event.ADDED_TO_STAGE, addHandler);
}
private function addHandler(event:Event):void {
removeEventListener(Event.ADDED_TO_STAGE, addHandler);
sw = stage.stageWidth;
sh = stage.stageHeight;
if (Math.random() < 0.5) vx *= -1;
if (Math.random() < 0.5) vy *= -1;
}
// 移動
public function update():void {
x += vx;
y += vy;
if (x < RADIUS) {
x = RADIUS;
vx *= -1;
}else if (x > sw - RADIUS) {
x = sw - RADIUS;
vx *= -1;
}
if (y < RADIUS) {
y = RADIUS;
vy *= -1;
}else if (y > sh - RADIUS) {
y = sh - RADIUS;
vy *= -1;
}
}
}