In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

forked from: ボロノイ図を軽く解説

import net.hires.debug.Stats;

ボロノイ図描画
Quasimondo 先生が 2002 年に書いたコードの写経です。
ステージをクリックしてね。何度も何度もクリックしてね。
オリジナル:http://www.quasimondo.com/archives/000110.php
説明:http://aquioux.blog48.fc2.com/blog-entry-654.html
/**
 * Copyright soh335 ( http://wonderfl.net/user/soh335 )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/1Odq
 */

// forked from uwi's ボロノイ図を軽く解説
// forked from Aquioux's ボロノイ図
package {
    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 sx:Array;        // 線開始座標(X座標)
        private var sy:Array;        // 線開始座標(Y座標)
        private var ex:Array;        // 線終了座標(X座標)
        private var ey:Array;        // 線終了座標(Y座標)
        
        private var dotLayer:Sprite;        // ドットのコンテナ
        private var lineLayer:Sprite;        // 線描画用レイヤ
        private var textField:TextField;    // "Click STAGE"
        
        
        public function Main():void {
            // 配列初期化
            dots = [];
            sx = [];
            sy = [];
            ex = [];
            ey = [];
            
            // コンテナ等
            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:int = 0; i < 4; 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 {
            moveDot();        // Dot の移動
            setVoronoi();    // ボロノイ境界線の計算
            drawVolonoi();    // ボロノイ境界線の描画
        }
        
        // Dot の移動
        private function moveDot():void {
            var n:uint = dots.length;
            for (var i:int = 0; i < n; i++) {
                var dot:Dot = dots[i];
                dot.update();
            }
        }
        
        // ボロノイ境界線の計算
        // 長い線分をつくってから、それぞれの交点を計算してトリミングしていきます。
        private function setVoronoi():void {
            var a:Number;
            var b:Number;
            var a0:Number;
            var b0:Number;
            var a1:Number;
            var b1:Number;
            var x:Number;
            var y:Number;
            var x0:Number;
            var y0:Number;
            var x1:Number;
            var y1:Number;
            
            var n:uint = dots.length;
            var m:uint = n + 3;
                // 各dotについて、dot同士と画面境界の線分をつくる
                // sx,syは線分の開始点, ex,eyは終端点とでも
            for (var i:int = 0; i < n; i++) {
                x0 = dots[i].x;
                y0 = dots[i].y;
                var idx1:int = i * m + i + 1; // 以降の線分は[i][i+1]~[i][m-1]の間に格納する
                        // 相手をするdot
                        for (var j:int = i + 1; j < n; j++) {
                    x1 = dots[j].x;
                    y1 = dots[j].y;
                    
                                // 垂直二等分線を求める。
                                // aは傾き、bは切片
                    if (x1 == x0) {
                        a = 0;
                    } else if (y1 == y0) {
                        a = 10000;
                    } else {
                        a = -1 / ((y1 - y0) / (x1 - x0));
                    }
                    
                    b = (y0 + y1) / 2 - a * (x0 + x1) / 2;
                    
                                // 45度以下の傾きなら画面境界のx座標でぶったぎる
                                // それ以上の傾きならy座標でぶったぎる
                    if (a > -1 && a <= 1) {
                        sx[idx1] = 0;
                        sy[idx1] = a * sx[idx1] + b;
                        ex[idx1] = STAGE_WIDTH - 1;
                        ey[idx1] = a * ex[idx1] + b;
                    } else {
                        sy[idx1] = 0;
                        sx[idx1] = (sy[idx1] - b) / a;
                        ey[idx1] = STAGE_HEIGHT - 1;
                        ex[idx1] = (ey[idx1] - b) / a;
                    }
                    idx1++;
                }
                        // 画面境界
                sx[idx1] = 0;
                sy[idx1] = 0;
                ex[idx1] = STAGE_WIDTH;
                ey[idx1] = 0;
                idx1++;
                sx[idx1] = 0;
                sy[idx1] = 0;
                ex[idx1] = 0;
                ey[idx1] = STAGE_HEIGHT;
                idx1++;
                sx[idx1] = STAGE_WIDTH;
                sy[idx1] = 0;
                ex[idx1] = STAGE_WIDTH;
                ey[idx1] = STAGE_HEIGHT;
                idx1++;
                sx[idx1] = 0;
                sy[idx1] = STAGE_HEIGHT;
                ex[idx1] = STAGE_WIDTH;
                ey[idx1] = STAGE_HEIGHT;
            }
            
                // それぞれの線分たちを見てトリミングしていく
                // 各dotについて
            for (i = 0; i < n; i++) {
                x0 = dots[i].x;
                y0 = dots[i].y;
                        // 各線分(S1とする)について(jは線分をつくった相手のdotのインデックス)
                for (j = 0; j < m + 1; j++) {
                    if (j != i) {
                        if (j > i) {
                            idx1 = i * m + j;
                        } else {
                            idx1 = j * m + i;
                        }
                                        // -Number.MAX_VALUEだったらトリミング対象にしない(後述)
                                if (sx[idx1] > -Number.MAX_VALUE) {
                                                // S1の傾き・切片をとる
                            a0 = (ey[idx1] - sy[idx1]) / (ex[idx1] - sx[idx1]);
                            b0 = sy[idx1] - a0 * sx[idx1];

                                                // dotについてできているもう一個の線分(S2)をとってくる。
                                                // S1-S2間でS2をトリミングする。S1は、iとjが逆になっているときトリミングされる。
                            for (var k:int = i + 1; k < m + 1; k++) {
                                if (k != j) {
                                    var idx2:int = i * m + k;
                                    if (sx[idx2] > -Number.MAX_VALUE) {
                                                                        // S2の傾き・切片をとる
                                        a1 = (ey[idx2] - sy[idx2]) / (ex[idx2] - sx[idx2]);
                                        b1 = sy[idx2] - a1 * sx[idx2];
                                                                        // S1とS2の交点を求める
                                        x = -(b1 - b0) / (a1 - a0);
                                                y = a0 * x + b0;

                                                                        // S1からみて、dotとS2の開始点が反対方向にあれば
                                        if ((a0 * x0 + b0 - y0) * (a0 * sx[idx2] + b0 - sy[idx2]) < 0) {
                                                                        // S2の開始点をS1とS2の交点にする(トリミング) 
                                            sx[idx2] = x;
                                            sy[idx2] = y;
                                        }
                                                                        // S1からみて、dotとS2の終端点が反対方向にあれば 
                                        if ((a0 * x0 + b0 - y0) * (a0 * ex[idx2] + b0 - ey[idx2]) < 0) {
                                                                                // この上のトリミングが行われている場合、
                                                                                // S1からみて、S2の全体がdotと逆方向にあるため、
                                                                                // S2はもう表示する必要がない。
                                                                                // よってトリミング対象から外す。
                                                                                        if(sx[idx2] == x) {
                                                sx[idx2] = -Number.MAX_VALUE;
                                            } else {
                                                                                        // S2の開始点をS1とS2の交点にする(トリミング) 
                                                ex[idx2] = x;
                                                ey[idx2] = y;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        
        // ボロノイ境界線の描画
        private function drawVolonoi():void {
            lineLayer.graphics.clear();
            lineLayer.graphics.lineStyle(0, 0x666666);

            var n:uint = dots.length;
            var m:uint = n + 3;
            for (var i:int = 0; i < n; i++) {
                var idx:uint = i * m + i + 1;
                for (var j:int = i + 1; j < m + 1; j++) {
                    if (sx[idx] > -Number.MAX_VALUE) {
                        lineLayer.graphics.moveTo(sx[idx], sy[idx]);
                        lineLayer.graphics.lineTo(ex[idx], ey[idx]);
                    }
                    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);
        }
    }
}


    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() {
            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;
            }
            if (x > sw - RADIUS) {
                x = sw - RADIUS;
                vx *= -1;
            }
            if (y < RADIUS) {
                y = RADIUS;
                vy *= -1;
            }
            if (y > sh - RADIUS) {
                y = sh - RADIUS;
                vy *= -1;
            }
        }
    }