forked from: ボロノイ図

by kawakita forked from ボロノイ図 (diff: 500)
ボロノイ図描画
Quasimondo 先生が 2002 年に書いたコードの写経です。
ステージをクリックしてね。何度も何度もクリックしてね。
オリジナル:http://www.quasimondo.com/archives/000110.php
説明:http://aquioux.blog48.fc2.com/blog-entry-654.html

--- 
処理を色々変えて試みました.
♥0 | Line 258 | Modified 2009-12-04 03:05:31 | MIT License
play

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;
        }
    }
}