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

Delaunay Letter

Delaunay Letter
* Usase : key down "Alphabet" or "Number" or "Number of tenkey", and move mouse on stage.
*         if no reaction, click stage and key down.
* 使い方 : アルファベットキー、数字キー、テンキーの数字キー、いずれかを押してください。そしてマウスを動かしてください。
*         反応しないときは、一度ステージをクリックしてからキーを押してください。
*
* Delaunay triangulation Algorithm by nicoptere
* ドロネー三角形分割のアルゴリズムは nicoptere さんによるクラスで実現されています。
* Delaunay.as, DelaunayPoint.as, DelaunayEdge.as, DelaunayTriangle.as
* @see http://en.nicoptere.net/?p=10
* 
* 参考: 「.fla 2」 より "YuruYurer"
* http://www.amazon.co.jp/gp/product/4862670717?ie=UTF8&tag=laxcomplex-22
* 
* 解説:http://aquioux.net/blog/2010/07/23/delaunay-letter/
* @author Aquioux(Yoshida, Akio)
/**
 * Copyright Aquioux ( http://wonderfl.net/user/Aquioux )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/9aZG
 */

/**
 * Delaunay Letter
 * Usase : key down "Alphabet" or "Number" or "Number of tenkey", and move mouse on stage.
 *         if no reaction, click stage and key down.
 * 使い方 : アルファベットキー、数字キー、テンキーの数字キー、いずれかを押してください。そしてマウスを動かしてください。
 *         反応しないときは、一度ステージをクリックしてからキーを押してください。
 *
 * Delaunay triangulation Algorithm by nicoptere
 * ドロネー三角形分割のアルゴリズムは nicoptere さんによるクラスで実現されています。
 * Delaunay.as, DelaunayPoint.as, DelaunayEdge.as, DelaunayTriangle.as
 * @see http://en.nicoptere.net/?p=10
 * 
 * 参考: 「.fla 2」 より "YuruYurer"
 * http://www.amazon.co.jp/gp/product/4862670717?ie=UTF8&tag=laxcomplex-22
 * 
 * 解説:http://aquioux.net/blog/2010/07/23/delaunay-letter/
 * @author Aquioux(Yoshida, Akio)
 */
package {
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Graphics;
    import flash.display.GraphicsEndFill;
    import flash.display.GraphicsSolidFill;
    import flash.display.GraphicsStroke;
    import flash.display.GraphicsTrianglePath;
    import flash.display.IGraphicsData;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.geom.Matrix;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    import flash.text.TextFormat;
    [SWF(width = "465", height = "465", frameRate = "30", backgroundColor = "#FFFFFF")]
    
    public class Main extends Sprite {
        
        // 母点生成用定数
        private const FONT_SIZE:uint  = 30;
        private const INTERVAL_X:Number = 14;
        private const INTERVAL_Y:Number = 14;
        private const W:Number = stage.stageWidth / INTERVAL_X;
        private const H:Number = stage.stageHeight / INTERVAL_Y;
        private const CX:Number = stage.stageWidth / 2;
        private const CY:Number = stage.stageHeight / 2;
        
        // 母点動作用定数
        private const SPRING:Number   = 0.05;            // バネ係数
        private const FRICTION:Number = 0.79;            // 摩擦係数
        private const DISTANCE_OF_REACTION:uint = 150;    // マウスに反応する距離
        private const PI2:Number = Math.PI * 2;
        private const SCALE:uint   = 25;
        private const OFFSET:uint  = 2;
        private const OFFSET2:uint = OFFSET * 2 + 1;
        
        // 文字を転写する BitmapData
        private var bmd_:BitmapData;
        
        // 母点格納
        private var generators_:Vector.<Point>;

        // キャンバス
        private var canvas_:Sprite;
        // GraphicsData
        private var graphicsData:Vector.<IGraphicsData>;
        private var path:GraphicsTrianglePath;
        
        
        // コンストラクタ
        public function Main() {
            // ドロネー辺描画用 GraphicsData 生成
            createGraphicsData();
            // キャンバス生成
            canvas_ = new Sprite();
            addChild(canvas_);
            
            // 母点生成
            createGenerators("A");
            
            addEventListener(Event.ENTER_FRAME, enterFrameHandler);
            stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
        }
        
        // 母点生成
        private function createGenerators(letter:String):void {
            createGenerator(letter);
            //fortune_.points = generators_;
        }
        private function createGenerator(letter:String):void {
            const TRANSPARENT:uint = 0x00000000;
            const COLOR:uint       = 0xFF000000;

            // TextField の生成
            var tFormat:TextFormat = new TextFormat("_sans", FONT_SIZE, COLOR);
            var tField:TextField = new TextField();
            tField.defaultTextFormat = tFormat;
            tField.text = letter;
            tField.autoSize = TextFieldAutoSize.LEFT;
            
            // TextField を BitmapData へ転写
            var bmd1:BitmapData = new BitmapData(tField.textWidth, tField.textHeight, true, TRANSPARENT);
            bmd1.draw(tField, new Matrix(1, 0, 0, 1, -2, -2));
            
            // 上記 BitmapData から 文字部分のみを抜き出す
            var rect:Rectangle = bmd1.getColorBoundsRect(0xFF000000, TRANSPARENT, false);
            
            // 文字部分のサイズの BitmapData へ転写
            var bmd2:BitmapData = new BitmapData(rect.width, rect.height, true, TRANSPARENT);
            bmd2.draw(bmd1, new Matrix(1, 0, 0, 1, -rect.x, -rect.y));
            
            // 上記 BitmapData をスキャン対象サイズの BitmapData へ転写
            bmd_ = new BitmapData(W, H, true, TRANSPARENT);
            bmd_.draw(bmd2, new Matrix(1, 0, 0, 1, (W - bmd2.width) / 2, (H - bmd2.height) / 2));
            
            // 使用済みの BitmapData を解放
            bmd1.dispose();
            bmd2.dispose();

            // BitmapData をスキャン
            var flg:Boolean = false;
            generators_ = new Vector.<Point>();
            for (var y:int = 0; y < H; y++) {
                for (var x:int = 0; x < W; x++) {
                    var color:uint = bmd_.getPixel32(x, y);
                    var alpha:uint = (color >> 24) & 0xFF;
                    if (alpha > 0x79) {
                        flg = true;
                    } else {
                        //if (Math.random() < 0.005) flg = true;
                    }
                    if (flg) {
                        var generator:ExPoint = new ExPoint();
                        generator.localX = x * INTERVAL_X + int(Math.random() * OFFSET2) - OFFSET;
                        generator.localY = y * INTERVAL_Y + int(Math.random() * OFFSET2) - OFFSET;
                        var radian:Number = Math.random() * PI2;
                        generator.x = Math.cos(radian) * 300 + CX;
                        generator.y = Math.sin(radian) * 300 + CY;
                        generators_.push(generator as Point);
                        flg = false;
                    }
                }
            }

            // 使用済みの BitmapData を解放
            bmd_.dispose();
        }
        
        // ドロネー辺描画用 GraphicsData 生成
        private function createGraphicsData():void {
            // 線の定義
            var stroke:GraphicsStroke = new GraphicsStroke();
            stroke.thickness = 0;
            stroke.fill = new GraphicsSolidFill(0x000000);

            // パスの定義
            path = new GraphicsTrianglePath();

            // 描画データをパッケージング
            graphicsData = new Vector.<IGraphicsData>();
            graphicsData.push(stroke);
            graphicsData.push(path);
            graphicsData.push(new GraphicsEndFill());
        }
        
        
        private function enterFrameHandler(event:Event):void {
            // 母点位置更新
            var mousePoint:Point = new Point(mouseX, mouseY);
            var n:uint = generators_.length;
            for (var i:int = 0; i < n; i++) {
                generatorUpdate(generators_[i] as ExPoint, mousePoint);
            }

            var vertices:Vector.<Number> = new Vector.<Number>();
            for each (var t:DelaunayTriangle in Delaunay.Triangulate(generators_)) {
                vertices.push(t.p1.x, t.p1.y, t.p2.x, t.p2.y, t.p3.x, t.p3.y);
            }
            path.vertices = vertices;
            
            // 線の描画
            var g:Graphics = canvas_.graphics;
            g.clear();
            g.drawGraphicsData(graphicsData);
        }
        
        // 各母点の位置更新
        private function generatorUpdate(generator:ExPoint, mousePoint:Point):void {
            var localX:Number = generator.localX;
            var localY:Number = generator.localY;
            var vx:Number     = generator.vx;
            var vy:Number     = generator.vy;
            var x:Number      = generator.x;
            var y:Number      = generator.y;
            var distance:Number = Point.distance(mousePoint, new Point(localX, localY));

            var dx:Number;        // 到達値X座標
            var dy:Number;        // 到達値Y座標
            var diff:Number;
            var radian:Number;
            
            if (distance < DISTANCE_OF_REACTION) {
                diff  = -distance * (DISTANCE_OF_REACTION - distance) / DISTANCE_OF_REACTION;
                var offset:Number = DISTANCE_OF_REACTION / distance * SCALE;
                diff += Math.floor(Math.random() * offset) - offset / 2;
                radian = Math.atan2(mousePoint.y - localY, mousePoint.x - localX);
            } else {
                diff   = 0;
                radian = 0;
            }
            var diffPoint:Point = Point.polar(diff, radian);
            dx = localX + diffPoint.x;
            dy = localY + diffPoint.y;
            
            vx += (dx - x) * SPRING;
            vy += (dy - y) * SPRING;
            vx *= FRICTION;
            vy *= FRICTION;
            x += vx;
            y += vy;
            
            generator.x  =  x;
            generator.y  =  y;
            generator.vx = vx;
            generator.vy = vy;
        }
        
        private function keyDownHandler(e:KeyboardEvent):void {
            var flg:Boolean = false;
            var keyCode:int = e.keyCode;
            if ((keyCode >= 48) && (keyCode <= 57))  flg = true;    // 数字キー(キーボード)
            if ((keyCode >= 96) && (keyCode <= 105)) flg = true;    // 数字キー(テンキー)
            if ((keyCode >= 65) && (keyCode <= 91))  flg = true;    // アルファベットキー(キーボード)

            if (flg) createGenerators(String.fromCharCode(e.charCode));
        }
    }
}


//package {
    import flash.geom.Point;
    /**
     * Expand Point
     * @author YOSHIDA, Akio (Aquioux)
     */
    /*public*/ class ExPoint extends Point {
        
        // 既定座標
        public function get localX():Number { return _localX; }
        public function set localX(value:Number):void { _localX = value; }
        private var _localX:Number = 0;

        public function get localY():Number { return _localY; }
        public function set localY(value:Number):void { _localY = value; }
        private var _localY:Number = 0;
        
        // 速度
        public function get vx():Number { return _vx; }
        public function set vx(value:Number):void { _vx = value; }
        private var _vx:Number = 0;
        
        public function get vy():Number { return _vy; }
        public function set vy(value:Number):void { _vy = value; }
        private var _vy:Number = 0;
        

        public function ExPoint() {
        }
    }
//}

/**
 * Delaunay triangulation Algorithm by nicoptere
 * Delaunay.as, DelaunayEdge.as, DelaunayPoint.as, DelaunayTriangle.as
 * @see http://en.nicoptere.net/?p=10
 */
//package net.nicoptere.delaunay
//{
    import flash.geom.Point;
    //import net.nicoptere.delaunay.DelaunayTriangle;
    //import net.nicoptere.delaunay.DelaunayEdge;
    
    
    //Credit to Paul Bourke (pbourke@swin.edu.au) for the original Fortran 77 Program :))
    //Converted to a standalone C# 2.0 library by Morten Nielsen (www.iter.dk)
    //Check out: http://astronomy.swin.edu.au/~pbourke/terrain/triangulate/
    //You can use this code however you like providing the above credits remain in tact

    /**
     * @author nicoptere
     * http://en.nicoptere.net/
     */

    /*public*/ class Delaunay    
    {
        
        /**
         * performs a Delaunay triangulation on a  set of points 
         * @param points the original Vector of points
         * @return a Vector.<DelaunAyTriangle> of delaunay triangles
         * 
         */
        public static function Triangulate( points:Vector.<Point> ):Vector.<DelaunayTriangle>
        {

            //those will be used quite everywhere so I am storing them here not to declare them x times
            var i:int;
            var j:int;
            var nv:int = points.length;

            if (nv < 3) return null;

            var trimax:int = 4 * nv;

            
            
            // Find the maximum and minimum vertex bounds.
            // This is to allow calculation of the bounding supertriangle

            var xmin:Number = points[0].x;
            var ymin:Number = points[0].y;
            var xmax:Number = xmin;
            var ymax:Number = ymin;
            
            var vertex:Vector.<DelaunayPoint> = new Vector.<DelaunayPoint>( nv );
            
            for ( i = 0; i < nv; i++)
            {
                
                vertex[ i ] = new DelaunayPoint( points[i].x, points[i].y, i );
                
                if (vertex[i].x < xmin) xmin = vertex[i].x;

                if (vertex[i].x > xmax) xmax = vertex[i].x;

                if (vertex[i].y < ymin) ymin = vertex[i].y;

                if (vertex[i].y > ymax) ymax = vertex[i].y;

            }
            


            var dx:Number = xmax - xmin;
            var dy:Number = ymax - ymin;
            var dmax:Number = (dx > dy) ? dx : dy;

            var xmid:Number = (xmax + xmin) * 0.5;
            var ymid:Number = (ymax + ymin) * 0.5;


            // Set up the supertriangle
            // This is a triangle which encompasses all the sample points.
            // The supertriangle coordinates are added to the end of the
            // vertex list. The supertriangle is the first triangle in
            // the triangle list.

            vertex.push(new DelaunayPoint( (xmid - 2 * dmax), (ymid - dmax), nv + 1 ) );            
            vertex.push(new DelaunayPoint( xmid, (ymid + 2 * dmax), nv + 2 ) );            
            vertex.push(new DelaunayPoint((xmid + 2 * dmax), (ymid - dmax), nv + 3));
            

            var triangles:Vector.<DelaunayTriangle> = new Vector.<DelaunayTriangle>();

            triangles.push( new DelaunayTriangle( vertex[ nv ], vertex[ nv + 1 ], vertex[ nv + 2 ] ) ); //SuperTriangle placed at index 0

            

            // Include each point one at a time into the existing mesh
            for ( i = 0; i < nv; i++)
            {
                
                var DelaunayEdges:Vector.<DelaunayEdge> = new Vector.<DelaunayEdge>(); 

                // Set up the DelaunayEdge buffer.
                // If the point (vertex(i).x,vertex(i).y) lies inside the circumcircle then the
                // three DelaunayEdges of that triangle are added to the DelaunayEdge buffer and the triangle is removed from list.
                for ( j = 0; j < triangles.length; j++ )
                {
                    
                    if ( InCircle( vertex[ i ], triangles[ j ].p1, triangles[ j ].p2, triangles[ j ].p3 ) )
                    {

                        DelaunayEdges.push(new DelaunayEdge(triangles[j].p1, triangles[j].p2) );

                        DelaunayEdges.push(new DelaunayEdge(triangles[j].p2, triangles[j].p3) );

                        DelaunayEdges.push(new DelaunayEdge(triangles[j].p3, triangles[j].p1) );

                        triangles.splice( j,1 );

                        j--;

                    }

                }

                if ( i >= nv) continue; //In case we the last duplicate point we removed was the last in the array



                // Remove duplicate DelaunayEdges
                // Note: if all triangles are specified anticlockwise then all
                // interior DelaunayEdges are opposite pointing in direction.

                for ( j = DelaunayEdges.length - 2; j >= 0; j--)
                {

                    for (var k:int = DelaunayEdges.length - 1; k >= j + 1; k--)
                    {

                        if ( DelaunayEdges[ j ].equals( DelaunayEdges[ k ] ) )
                        {

                            DelaunayEdges.splice( k, 1 );
                            DelaunayEdges.splice( j, 1 );
                            k--;
                            continue;

                        }

                    }

                }

                // Form new triangles for the current point
                // Skipping over any tagged DelaunayEdges.
                // All DelaunayEdges are arranged in clockwise order.
                for ( j = 0; j < DelaunayEdges.length; j++)
                {

                    if (triangles.length >= trimax )
                    {
                        //    throw new ApplicationException("Exceeded maximum DelaunayEdges");
                        trace("Exceeded maximum DelaunayEdges");
                        
                    }
                    triangles.push( new DelaunayTriangle( DelaunayEdges[ j ].p1, DelaunayEdges[ j ].p2, vertex[ i ] ));

                }

                DelaunayEdges = null;
                
            }
            
            // Remove triangles with supertriangle vertices
            // These are triangles which have a vertex number greater than nv

            for ( i = triangles.length - 1; i >= 0; i--)
            {
                
                if ( triangles[ i ].p1.id >= nv || triangles[ i ].p2.id >= nv || triangles[ i ].p3.id >= nv)
                {
                    triangles.splice(i, 1);
                }
                
            }

            //Remove SuperTriangle vertices
            /*
            vertex.splice(vertex.length - 1, 1);
            vertex.splice(vertex.length - 1, 1);
            vertex.splice(vertex.length - 1, 1);
            */
            return triangles;

        }



        /// <summary>
        /// Returns true if the point (p) lies inside the circumcircle made up by points (p1,p2,p3)
        /// </summary>
        /// <remarks>
        /// NOTE: A point on the DelaunayEdge is inside the circumcircle
        /// </remarks>
        /// <param name="p">Point to check</param>
        /// <param name="p1">First point on circle</param>
        /// <param name="p2">Second point on circle</param>
        /// <param name="p3">Third point on circle</param>
        /// <returns>true if p is inside circle</returns>
        static private const Epsilon:Number = Number.MIN_VALUE;
        private static function InCircle( p:DelaunayPoint, p1:DelaunayPoint, p2:DelaunayPoint, p3:DelaunayPoint ):Boolean

        {

            //Return TRUE if the point (xp,yp) lies inside the circumcircle
            //made up by points (x1,y1) (x2,y2) (x3,y3)
            //NOTE: A point on the DelaunayEdge is inside the circumcircle

            if ( Math.abs( p1.y - p2.y ) < Epsilon && Math.abs( p2.y - p3.y) < Epsilon)
            {

                //INCIRCUM - F - Points are coincident !!
                return false;

            }



            var m1:Number;
            var m2:Number;

            var mx1:Number;
            var mx2:Number;

            var my1:Number;
            var my2:Number;

            var xc:Number;
            var yc:Number;

                        

            if ( Math.abs(p2.y - p1.y) < Epsilon)
            {

                m2 = -(p3.x - p2.x) / (p3.y - p2.y);

                mx2 = (p2.x + p3.x) * 0.5;

                my2 = (p2.y + p3.y) * 0.5;

                //Calculate CircumCircle center (xc,yc)
                
                xc = (p2.x + p1.x) * 0.5;

                yc = m2 * (xc - mx2) + my2;

            }
            else if ( Math.abs(p3.y - p2.y) < Epsilon)
            {

                m1 = -(p2.x - p1.x) / (p2.y - p1.y);

                mx1 = (p1.x + p2.x) * 0.5;

                my1 = (p1.y + p2.y) * 0.5;

                //Calculate CircumCircle center (xc,yc)

                xc = (p3.x + p2.x) * 0.5;

                yc = m1 * (xc - mx1) + my1;

            }
            else
            {

                m1 = -(p2.x - p1.x) / (p2.y - p1.y);

                m2 = -(p3.x - p2.x) / (p3.y - p2.y);

                mx1 = (p1.x + p2.x) * 0.5;

                mx2 = (p2.x + p3.x) * 0.5;

                my1 = (p1.y + p2.y) * 0.5;

                my2 = (p2.y + p3.y) * 0.5;

                //Calculate CircumCircle center (xc,yc)
                xc = (m1 * mx1 - m2 * mx2 + my2 - my1) / (m1 - m2);

                yc = m1 * (xc - mx1) + my1;

            }



            var dx:Number = p2.x - xc;

            var dy:Number = p2.y - yc;

            var rsqr:Number = dx * dx + dy * dy;

            dx = p.x - xc;

            dy = p.y - yc;

            var drsqr:Number = dx * dx + dy * dy;

            return ( drsqr <= rsqr );

        }

    }

//}


//package net.nicoptere.delaunay
//{

    /**
     * @author nicoptere
     */
    /*public*/ class DelaunayPoint
    {
        
        public var id:int;
        public var x:Number;
        public var y:Number;
        
        public function DelaunayPoint( x:Number, y:Number, id:int = -1 )
        {
            this.x = x;
            this.y = y;
            if( id != -1 ) this.id = id;
        }
        
        public function equals( other:DelaunayPoint ):Boolean
        {
            return ( x == other.x && y == other.y );
        }
    
    }
//}


//package net.nicoptere.delaunay
//{

    //Credit to Paul Bourke (pbourke@swin.edu.au) for the original Fortran 77 Program :))
    //Converted to a standalone C# 2.0 library by Morten Nielsen (www.iter.dk)
    //Check out: http://astronomy.swin.edu.au/~pbourke/terrain/triangulate/
    //You can use this code however you like providing the above credits remain in tact

    /**
     * @author nicoptere
     * http://en.nicoptere.net/
     */
    /*public*/ class DelaunayEdge 
    {
        
        public var p1:DelaunayPoint;
        public var p2:DelaunayPoint;

        /**
        * Initializes a new DelaunayEdge instance
        * @param point1 Start DelaunayEdge vertex index
        * @param point1 Start DelaunayEdge vertex index
        */
        public function DelaunayEdge( point1:DelaunayPoint, point2:DelaunayPoint )
        {

            p1 = point1; 
            p2 = point2;
            
        }

        /**
         * Checks whether two DelaunayEdges are equal disregarding the direction of the DelaunayEdges
         * @param other the DelaunayEdge to compare
         */
        public function equals( other:DelaunayEdge ):Boolean
        {

            return ((this.p1 == other.p2) && (this.p2 == other.p1)) || ((this.p1 == other.p1) && (this.p2 == other.p2));

        }

    }
//}


//package net.nicoptere.delaunay
//{


    //Credit to Paul Bourke (pbourke@swin.edu.au) for the original Fortran 77 Program :))
    //Converted to a standalone C# 2.0 library by Morten Nielsen (www.iter.dk)
    //Check out: http://astronomy.swin.edu.au/~pbourke/terrain/triangulate/
    //You can use this code however you like providing the above credits remain in tact

    /**
     * @author nicoptere
     * http://en.nicoptere.net/
     */
    //import net.nicoptere.delaunay.DelaunayPoint;
    import flash.display.Graphics;
    
    /*public*/ class DelaunayTriangle 
    {
        //points of the DelaunayTriangle
        public var p1:DelaunayPoint;
        public var p2:DelaunayPoint;
        public var p3:DelaunayPoint;
        
        //gravity center
        public var center:DelaunayPoint;
        
        //middle of the sides
        public var mid0:DelaunayPoint;//p0 > p1
        public var mid1:DelaunayPoint;//p1 > p2
        public var mid2:DelaunayPoint;//p2 > p0
        
        public function DelaunayTriangle( p1:DelaunayPoint, p2:DelaunayPoint, p3:DelaunayPoint )
        {
        
            this.p1 = p1;
            this.p2 = p2;
            this.p3 = p3;
            
        }
        
        /**
         * retrieves the gravity center of the DelaunayTriangle
         */
        public function getCenter():void
        {
            
            if( center == null ) center = new DelaunayPoint( 0,0 );
            center.x = ( p1.x + p2.x + p3.x ) / 3;
            center.y = ( p1.y + p2.y + p3.y ) / 3;
            
        }
        
        /**
         * retrieves the midPoint of the DelaunayTriangle's sides. might be useful in some cases
        */
        public function getSidesCenters():void
        {
            if( mid0 == null || mid1 == null || mid2 == null )
            {
                mid0 = new DelaunayPoint( 0, 0 );
                mid1 = new DelaunayPoint( 0, 0 );
                mid2 = new DelaunayPoint( 0, 0 );
            } 
            
            mid0.x = p1.x + ( p2.x - p1.x )/2;
            mid0.y = p1.y + ( p2.y - p1.y )/2;
            
            mid1.x = p2.x + ( p3.x - p2.x )/2;
            mid1.y = p2.y + ( p3.y - p2.y )/2;    
            
            mid2.x = p3.x + ( p1.x - p3.x )/2;
            mid2.y = p3.y + ( p1.y - p3.y )/2;
        }
        
        public function draw( g:Graphics ):void
        {
            g.moveTo( p1.x, p1.y );
            g.lineTo( p2.x, p2.y );
            g.lineTo( p3.x, p3.y );
            g.lineTo( p1.x, p1.y );
        }
    }
//}