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: navMeshAstarPath

...
http://wonderfl.net/c/lDKs
http://www.cnblogs.com/neoragex2002/archive/2007/09/09/887556.html
@author sliz http://game-develop.net/
Get Adobe Flash player
by jmbyh521 27 Nov 2015
/**
 * Copyright jmbyh521 ( http://wonderfl.net/user/jmbyh521 )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/0lmR
 */

// forked from lizhi's navMeshAstarPath
package {
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.utils.getTimer;

    /**
     * ...
     * http://wonderfl.net/c/lDKs
     * http://www.cnblogs.com/neoragex2002/archive/2007/09/09/887556.html
     * @author sliz http://game-develop.net/
     */
    public class Main extends Sprite {
        private var d:Delaunay;
        private var astar:AStar;
        private var g:Grid;

        public function Main():void {
            if (stage)
                init();
            else
                addEventListener(Event.ADDED_TO_STAGE, init);
        }

        private function init(e:Event = null):void {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            d = new Delaunay(400, 400);
            var c:Number = 300;
            while (c-- > 0){
                d.add(400 * Math.random(), 400 * Math.random());
            }
            for each (var t:Triangle in d.triangles){
                if (Math.random() < 0.2){
                    t.walkAble = false;
                }
            }

            g = new Grid();
            g.delaunay = d;
            g.calculateLinks();
            astar = new AStar(g);
            g.startNode = d.triangles[int(d.triangles.length * Math.random())];
            g.endNode = d.triangles[int(d.triangles.length * Math.random())];

            stage.addEventListener(MouseEvent.CLICK, onclick);
            astar.findPath();
            draw();
        }

        private function onclick(e:MouseEvent):void {
            g.startNode = g.endNode;
            for each (var t:Triangle in d.triangles){
                if (isPointInTriangle(t.node0.point.x, t.node0.point.y, t.node1.point.x, t.node1.point.y, t.node2.point.x, t.node2.point.y, mouseX, mouseY)){
                    g.endNode = t;
                    break;
                }
            }
            if (g.startNode && g.endNode){
                trace(astar.findPath(), astar.path);
                draw();
            }
        }

        private function draw():void {
            graphics.lineStyle(0);
            for each (var t:Triangle in d.triangles){
                if (t.walkAble)
                    graphics.beginFill(0xffffff);
                else
                    graphics.beginFill(0xff0000);
                graphics.moveTo(t.node0.point.x, t.node0.point.y);
                graphics.lineTo(t.node1.point.x, t.node1.point.y);
                graphics.lineTo(t.node2.point.x, t.node2.point.y);
                graphics.lineTo(t.node0.point.x, t.node0.point.y);
            }
            if (astar.path){

                for (var i:int = 0; i < astar.path.length; i++){
                    t = astar.path[i];
                    if (i == 0)
                        graphics.beginFill(0xffff00);
                    else if (i == astar.path.length - 1)
                        graphics.beginFill(0x00ffff);
                    else
                        graphics.beginFill(0xff00ff);
                    graphics.moveTo(t.node0.point.x, t.node0.point.y);
                    graphics.lineTo(t.node1.point.x, t.node1.point.y);
                    graphics.lineTo(t.node2.point.x, t.node2.point.y);
                    graphics.lineTo(t.node0.point.x, t.node0.point.y);
                }
            }
        }

        public static function multiply(px1:Number, py1:Number, px2:Number, py2:Number, px3:Number, py3:Number):int {
            return (px1 - px3) * (py2 - py3) - (px2 - px3) * (py1 - py3);
        }

        public static function isPointInTriangle(px1:Number, py1:Number, px2:Number, py2:Number, px3:Number, py3:Number, px:Number, py:Number):Boolean {
            var v:int = multiply(px1, py1, px2, py2, px3, py3);
            if (v * multiply(px1, py1, px2, py2, px, py) <= 0){
                return false;
            }
            if (v * multiply(px2, py2, px3, py3, px, py) <= 0){
                return false;
            }
            if (v * multiply(px3, py3, px1, py1, px, py) <= 0){
                return false;
            }
            return true;
        }
    }

}

import flash.geom.Point;

class AStar {
    private var open:BinaryHeap;
    private var grid:Grid;
    private var endNode:Triangle;
    private var startNode:Triangle;
    private var _path:Array;
    private var nowversion:int = 1;
    public var heuristic:Function;

    public function AStar(grid:Grid){
        this.grid = grid;
        heuristic = distance;
    }

    private function justMin(x:Object, y:Object):Boolean {
        return x.f < y.f;
    }

    public function findPath():Boolean {
        endNode = grid.endNode;
        nowversion++;
        startNode = grid.startNode;
        open = new BinaryHeap(justMin);
        startNode.g = 0;
        return search();
    }

    private function distance(t1:Triangle):Number {
        var t2:Triangle = endNode;
        var c:Number = Math.sqrt((t1.cx - t2.cx) * (t1.cx - t2.cx) + (t1.cy - t2.cy) * (t1.cy - t2.cy));
        return c;
    }

    public function search():Boolean {
        var node:Triangle = startNode;
        node.version = nowversion;
        while (node != endNode){
            var len:int = node.links.length;
            for (var i:int = 0; i < len; i++){
                var test:Triangle = node.links[i].node;
                var cost:Number = node.links[i].cost;
                var g:Number = node.g + cost;
                var h:Number = heuristic(test);
                var f:Number = g + h;
                if (test.version == nowversion){
                    if (test.f > f){
                        test.f = f;
                        test.g = g;
                        test.h = h;
                        test.parent = node;
                    }
                } else {
                    test.f = f;
                    test.g = g;
                    test.h = h;
                    test.parent = node;
                    open.ins(test);
                    test.version = nowversion;
                }
            }
            if (open.a.length == 1){
                return false;
            }
            node = open.pop() as Triangle;
        }
        buildPath();
        return true;
    }

    private function buildPath():void {
        _path = [];
        var node:Triangle = endNode;
        path.push(node);
        while (node != startNode){
            node = node.parent;
            path.unshift(node);
        }
    }

    public function get path():Array {
        return _path;
    }
}

class BinaryHeap {
    public var a:Array = [];
    public var justMinFun:Function = function(x:Object, y:Object):Boolean {
        return x < y;
    };

    public function BinaryHeap(justMinFun:Function = null){
        a.push(-1);
        if (justMinFun != null)
            this.justMinFun = justMinFun;
    }

    public function ins(value:Object):void {
        var p:int = a.length;
        a[p] = value;
        var pp:int = p >> 1;
        while (p > 1 && justMinFun(a[p], a[pp])){
            var temp:Object = a[p];
            a[p] = a[pp];
            a[pp] = temp;
            p = pp;
            pp = p >> 1;
        }
    }

    public function pop():Object {
        var min:Object = a[1];
        a[1] = a[a.length - 1];
        a.pop();
        var p:int = 1;
        var l:int = a.length;
        var sp1:int = p << 1;
        var sp2:int = sp1 + 1;
        while (sp1 < l){
            if (sp2 < l){
                var minp:int = justMinFun(a[sp2], a[sp1]) ? sp2 : sp1;
            } else {
                minp = sp1;
            }
            if (justMinFun(a[minp], a[p])){
                var temp:Object = a[p];
                a[p] = a[minp];
                a[minp] = temp;
                p = minp;
                sp1 = p << 1;
                sp2 = sp1 + 1;
            } else {
                break;
            }
        }
        return min;
    }
}

class Delaunay {

    //----------------------------------------
    //VARIABLES

    //点群
    private var _points:Array;
    //三角形の集まり
    private var _triangles:Array;
    private var stageWidth:Number;
    private var stageHeight:Number;

    /*
     * コンストラクタ
     */
    public function Delaunay(w:Number, h:Number){
        stageWidth = w;
        stageHeight = h;
        //Security.allowDomain("*");
        //Security.allowDomain("planet-ape.net");
        //Security.allowDomain("wonderfl.net");
        //Security.allowDomain("swf.wonderfl.net");
        //初期化
        _initialize();
    }

    /*
     * 初期化
     */
    private function _initialize():void {

        _points = new Array();
        _triangles = new Array();

        //ステージの大きさの三角形2つを用意
        _points.push(new Node(0, 0, 0));
        _points.push(new Node(1, stageWidth, 0));
        _points.push(new Node(2, stageWidth, stageHeight));
        _points.push(new Node(3, 0, stageHeight));
        _triangles.push(new Triangle(_points[0], _points[1], _points[2]));
        _triangles.push(new Triangle(_points[0], _points[2], _points[3]));
    }

    /*
     * アップデート
     */ /*private function _updateHandler(event : Event) : void {
       _interaction();
       // _draw();
     }*/

    /*
     * インタラクション
     */
    private function _interaction():void {
        //一時保持の三角形群
        var localTriangles:Array = new Array();
        //辺
        var edges:Array;
        //多角形
        var polygon:Array;
        //ポイント群ループ
        for (var k:int = 4; k < _points.length; k++){
            var node:Node = _points[k];
            localTriangles = new Array();
            edges = new Array();

            for (var i:String in _triangles){
                //点が外接円
                var tri:Triangle = _triangles[i];
                if (inOuterCircle(node.point.x, node.point.y, tri)){
                    edges.push(new Edge(tri.node0, tri.node1));
                    edges.push(new Edge(tri.node1, tri.node2));
                    edges.push(new Edge(tri.node2, tri.node0));
                } else {
                    localTriangles.push(tri);
                }
            }
            //edgesからpolygonを作る(重複辺の削除
            polygon = new Array();
            for (i in edges){
                var edge0:Edge = edges[i];
                //重複チェック
                var flg:Boolean = false;
                for (var j:String in polygon){
                    var edge1:Edge = polygon[j];
                    if (judgeEdges(edge0, edge1)){
                        flg = true;
                        polygon.splice(j, 1);
                        break;
                    }
                }
                //データが存在しない場合は追加
                if (!flg)
                    polygon.push(edges[i]);
            }
            //polygonから三角形を作って挿入
            for (i in polygon){
                var tri1:Triangle = new Triangle(polygon[i].node0, polygon[i].node1, node);
                localTriangles.push(tri1);
            }
        }
        if (localTriangles.length > 1)
            _triangles = localTriangles;
    }

    /*
     * 同じ辺かどうかの判定
     */
    private function judgeEdges(edge:Edge, edge0:Edge):Boolean {
        if (edge.node0.id == edge0.node0.id && edge.node1.id == edge0.node1.id){
            return true;
        }
        if (edge.node1.id == edge0.node0.id && edge.node0.id == edge0.node1.id){
            return true;
        }
        return false;
    }



    /*
     * 外接円の内か外か
     */
    public function inOuterCircle(x:Number, y:Number, tri:Triangle):Boolean {
        var node0:Node = tri.node0;
        var node1:Node = tri.node1;
        var node2:Node = tri.node2;

        var d:Number = (node0.point.x * node0.point.x + node0.point.y * node0.point.y - x * x - y * y) * ((node1.point.x - x) * (node2.point.y - y) - (node2.point.x - x) * (node1.point.y - y)) + (node1.point.x * node1.point.x + node1.point.y * node1.point.y - x * x - y * y) * ((node2.point.x - x) * (node0.point.y - y) - (node2.point.y - y) * (node0.point.x - x)) + (node2.point.x * node2.point.x + node2.point.y * node2.point.y - x * x - y * y) * ((node0.point.x - x) * (node1.point.y - y) - (node0.point.y - y) * (node1.point.x - x));
        return ((node1.point.x - node0.point.x) * (node2.point.y - node0.point.y) - (node1.point.y - node0.point.y) * (node2.point.x - node0.point.x) > 0) ? d > 0 : d <= 0;
    }

    /*
     * マウスクリック時
     */
    public function add(x:Number, y:Number):void {
        _points.push(new Node(_points.length, x, y));
        _interaction();
    }

    public function get triangles():Array {
        return _triangles;
    }

    public function get points():Array {
        return _points;
    }
}

class Edge {
    private var _node0:Node;
    private var _node1:Node;

    public function Edge(node0:Node, node1:Node):void {
        _node0 = node0;
        _node1 = node1;
    }

    public function get node0():Node {
        return _node0;
    }

    public function get node1():Node {
        return _node1;
    }
}

class Grid {
    public var startNode:Triangle;
    public var endNode:Triangle;
    public var delaunay:Delaunay;

    public function Grid(){

    }

    public function calculateLinks():void {
        var ts:Array = delaunay.triangles;
        for each (var t:Triangle in ts){
            t.links = [];
        }
        for (var i:int = 0; i < ts.length; i++){
            var t1:Triangle = ts[i];
            if (!t1.walkAble)
                continue;
            for (var j:int = i + 1; j < ts.length; j++){
                var t2:Triangle = ts[j];
                if (!t2.walkAble)
                    continue;
                var t1ns:Array = [t1.node0, t1.node1, t1.node2];
                var t2ns:Array = [t2.node0, t2.node1, t2.node2];
                var counter:int = 0;
                for (var m:int = 0; m < t1ns.length; m++){
                    for (var n:int = 0; n < t2ns.length; n++){
                        if (t1ns[m] == t2ns[n]){
                            counter++;
                        }
                    }
                }
                if (counter > 1){
                    var c:Number = Math.sqrt((t1.cx - t2.cx) * (t1.cx - t2.cx) + (t1.cy - t2.cy) * (t1.cy - t2.cy));
                    t1.links.push(new Link(t2, c));
                    t2.links.push(new Link(t1, c));
                }
            }
        }
    }

}

class Link {
    public var node:Triangle;
    public var cost:Number;

    public function Link(node:Triangle, cost:Number){
        this.node = node;
        this.cost = cost;
    }

}

class Node {
    private var _id:int;
    private var _point:Point;

    public function Node(id:int, x:Number, y:Number){
        _id = id;
        _point = new Point(x, y);
    }

    public function get id():int {
        return _id;
    }

    public function get point():Point {
        return _point;
    }
}

class Triangle {


    public var walkAble:Boolean = true;
    public var version:int = 1;
    public var links:Array;
    public var f:Number;
    public var g:Number;
    public var h:Number;
    public var parent:Triangle;

    private var _node0:Node;
    private var _node1:Node;
    private var _node2:Node;
    private var edge0:Edge;
    private var edge1:Edge;
    private var edge2:Edge;

    public var cx:Number;
    public var cy:Number;

    public function Triangle(node0:Node, node1:Node, node2:Node):void {
        _node0 = node0;
        _node1 = node1;
        _node2 = node2;
        cx = (_node0.point.x + _node1.point.x + _node2.point.x) / 3;
        cy = (_node0.point.y + _node1.point.y + _node2.point.y) / 3;
    }

    public function get node0():Node {
        return _node0;
    }

    public function get node1():Node {
        return _node1;
    }

    public function get node2():Node {
        return _node2;
    }
}