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

WonderWall SSのパチモン

WanderWallのScreenSaverのパチモン
PortAsまわりは、http://wonderfl.net/c/c3Uk/ からごっそりパクりました。
Get Adobe Flash player
by sandmanjp 09 Jun 2011
/**
 * Copyright sandmanjp ( http://wonderfl.net/user/sandmanjp )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/taVD
 */

package
{
    import flash.display.*;
    import flash.events.Event;
    
    [SWF(backgroundColor="0", frameRate="60")]
    
    public class wonderwall extends Sprite {
        public function wonderwall():void {
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT
            
            addChild(new WallText("W"));

        }
    }
}

import flash.display.*;
import flash.events.Event;
import flash.filters.BlurFilter;
import flash.filters.GlowFilter;
import flash.geom.ColorTransform;
import flash.geom.Point;
import flash.system.LoaderContext;
import flash.net.URLLoader;
import flash.net.URLRequest;

internal class WallText extends Sprite {

    private var _cp:ColorPallete;
    private var _pt:Sprite;
    private var _canvas:Sprite;
    private var _beam:Sprite;
    private var _bmp:Bitmap;
    private var _bmd:BitmapData;
    private var _wormLoad:Array = new Array();
    private var _count:int = 0;
    
    public function WallText(str:String):void {
     
        
        _pt = new PotrasText(str).getText();
        _pt.scaleX = _pt.scaleY = 4;
        addChild(_pt);
        
        _cp = new ColorPallete("http://assets.wonderfl.net/images/related_images/f/f9/f95f/f95fcdff878b6771abece3f9d12faa1a733571c3");
        _cp.x = 120;
        _cp.y = 330;
        
        addChild(_cp);
        
        
        _canvas = new Sprite();
        _bmd = new BitmapData(_pt.width * 1.2, _pt.height * 2, true, 0xFF0000);
        _bmp = new Bitmap(_bmd);
        _bmp.blendMode = BlendMode.ADD;
        addChild(_bmp);
        //
        _beam = new Sprite();
        addChild(_beam);
        
        _cp.addEventListener(Event.COMPLETE, startDraw);
    }
    
    
    private function startDraw(e:Event):void {
        removeEventListener(Event.ADDED, startDraw);
        var o:Object = hitPoint(0, 0, _pt.width, _pt.height);
        _wormLoad = new Array({}, o);
        //
        addEventListener(Event.ENTER_FRAME, drawColorWorm);
        
    }
    
    private function drawColorWorm(e:Event):void {
        _canvas.graphics.clear();
        _canvas.graphics.lineStyle(0.01, getPixelColor(), 0.3);
        _canvas.graphics.moveTo(_wormLoad[1].x, _wormLoad[1].y);
        
        var a:Array = [hitPoint(_wormLoad[1].x - 20, _wormLoad[1].y - 20, 40, 40)];
        a.push(hitPoint(a[0].x - 30, a[0].y - 30, 60, 60));
        _canvas.graphics.curveTo(a[0].x, a[0].y, a[1].x, a[1].y);
        _canvas.graphics.endFill();
        _wormLoad = a;
        
        _count++;
        if(_count == 60){
            _bmd.colorTransform(_bmd.rect, new ColorTransform(1, 1, 1, 0.9999999, 0, 0, 0, 0));
            _count = 0
        }
        _bmd.draw(_canvas);
        
        var pos:Point = _beam.globalToLocal(_cp.getPixelPos());
        _beam.graphics.clear();
        _beam.graphics.lineStyle(1, 0xffffff, 0.5);
        _beam.graphics.moveTo(_wormLoad[1].x, _wormLoad[1].y);
        _beam.graphics.lineTo(pos.x, pos.y);
        _beam.graphics.endFill();
    }
    
    private function hitPoint(mX:Number, mY:Number, pX:Number, pY:Number):Point {
        //var obj:Object;
        var gp:Point;
        
        do{
            gp = new Point(mX + Math.random()*pX, mY + Math.random()*pY);
            var lp:Point = localToGlobal(gp);
        //}while(_bmp.width > gp.x && _bmp.height > gp.y);
        }while(!_pt.hitTestPoint(lp.x, lp.y, true));
        
        return gp;
    }
    
    private function getPixelColor():uint {
        
        var x:Number = Math.random() * _cp.width;
        var y:Number = Math.random() * _cp.height;
        var c:uint = _cp.getPixel(x, y);
        
        return c;
    }
        
}

internal class ColorPallete extends Sprite {
    
    private var _bmp:Bitmap;
    private var _pBmp:Bitmap = new Bitmap(new BitmapData(5, 5, false, 0xffffff));
    public var _ldr:Loader;
    
    public function ColorPallete(url:String):void {
        _ldr = new Loader();
        _ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
        _ldr.load(new URLRequest(url), new LoaderContext(true));
        
    }
    
    private function onComplete(e:Event):void {
        var bmd:BitmapData = new BitmapData(_ldr.width, _ldr.height, false, 0x0000ff);
        bmd.draw(_ldr);
        _bmp = new Bitmap(bmd);
        _bmp.width = _bmp.height = 100;
        
        addChild(_bmp);
        addChild(_pBmp);
        
        dispatchEvent(new Event(Event.COMPLETE));
    }
    
    public function getPixel(x:Number, y:Number):uint{
        _pBmp.x = x;
        _pBmp.y = y;
        return _bmp.bitmapData.getPixel(x, y);
    }
    public function getPixelPos():Point{
        return localToGlobal(new Point(_pBmp.x, _pBmp.y));
    }
}

internal class PotrasText {
    
    import flash.text.TextFormat;
    import flash.text.TextField;
    import flash.display.BitmapData;
    import flash.geom.Point
    
    private var _txt:ClosedPathList;
    private const _SIZE:int = 80;
    
    
    public function PotrasText(txt:String):void {
        
        _txt = traceLetter(txt, _SIZE);
        
    }
    
    public function getText():Sprite {
        var sp:Sprite = new Sprite();
        sp.graphics.lineStyle(0.5, 0x333333);
        sp.graphics.beginFill(0x0, 1);
        _txt.draw(sp.graphics);
        sp.graphics.endFill();
        return sp;
    }
    
    /*
     * code from http://wonderfl.net/c/c3Uk/
     */
    public static function traceLetter(letter:String, fontSize:int, font:String="Arial"):ClosedPathList
    {
        var tf:TextFormat = new TextFormat();
        tf.font = font;
        tf.size = fontSize;
        
        var text:TextField = new TextField();
        text.defaultTextFormat = tf;
        text.autoSize = "left";
        text.text = letter;
        
        // We have to use threshold method to binarize, because Mac OS draws antialiased text.
        var bmdtmp:BitmapData = new BitmapData(fontSize * letter.length, fontSize * 1.2, true);
        var bitmapdata:BitmapData = bmdtmp.clone();
        bmdtmp.draw(text);
        bitmapdata.threshold(bmdtmp, bmdtmp.rect, new Point(), "<", 0xffdddddd, 0xff000000);
        
        var pathList:Array = PathList.create(bitmapdata);
        var c:ClosedPathList = ProcessPath.processPath(pathList);
        
        bmdtmp.dispose();
        bitmapdata.dispose();
        return c;
    }
    
}




/** ======================================================================================
 * これ以下はwonderflでPotrAsが使えないようなので無茶貼りです。
 * import com.nitoyon.potras.*; の代替
 * http://www.libspark.org/wiki/nitoyon/PotrAs
 ** ====================================================================================*/

/* Copyright (C) 2001-2007 Peter Selinger and nitoyon.
   Original code(Potrace v1.8) by Peter Selinger.
   Ported to ActionScript 3.0 by nitoyon.
   This file is part of PotrAs. It is free software and it is covered
   by the GNU General Public License. See the file COPYING for details. */

import flash.geom.Point;
import flash.display.Graphics;
class ClosedPath
{
    /**
     *  
     */
    public var $a:Array;

    /**
     *  Constructor.
     *
     *  initialize the members of the given curve structure to size m.
     *  Return 0 on success, 1 on error with errno set.
     */
    public function ClosedPath(array:Array = null):void
    {
        $a = array || [];
    }

    /**
     * draw
     */
    public function draw(g:Graphics):void
    {
        var pt:Point = $a[$a.length - 1].c[2];
        g.moveTo(pt.x, pt.y);

        for(var i:int = 0; i < $a.length; i++)
        {
            var c:Curve = $a[i];

            if(c.tag == ProcessPath.POTRACE_CORNER)
            {
                g.lineTo(c.c[1].x, c.c[1].y);
                g.lineTo(c.c[2].x, c.c[2].y);
            }
            else
            {
                for(var t:Number = 0; t < 1.0; t += 0.02)
                {
                    var p:Point = getBezierPoint(pt, c.c[0], c.c[1], c.c[2], t);
                    g.lineTo(p.x, p.y);
                }
                g.lineTo(c.c[2].x, c.c[2].y);
            }
            pt = c.c[2];
        }
    }

    /**
     * Get quad bezier curve point.
     */
    private function getBezierPoint(p0:Point, p1:Point, p2:Point, p3:Point, t:Number):Point
    {
        return new Point(Math.pow(1 - t, 3)  * p0.x + 3 * t * Math.pow(1 - t, 2) * p1.x
                   + 3 * t * t * (1 - t) * p2.x + t * t * t * p3.x,
                     Math.pow(1 - t, 3)  * p0.y + 3 * t * Math.pow(1 - t, 2) * p1.y
                   + 3 * t * t * (1 - t) * p2.y + t * t * t * p3.y);
    }
}


/* Copyright (C) 2001-2007 Peter Selinger and nitoyon.
   Original code(Potrace v1.8) by Peter Selinger.
   Ported to ActionScript 3.0 by nitoyon.
   This file is part of PotrAs. It is free software and it is covered
   by the GNU General Public License. See the file COPYING for details. */
import flash.display.Graphics;
import flash.display.BitmapData;
class ClosedPathList
{
    /**
     *  ClosePath array.
     */
    public var $a:Array;

    /**
     *  Constructor.
     */
    public function ClosedPathList():void
    {
        $a = [];
    }

    /**
     *  trace bitmap
     */
    public static function trace(bmpdata:BitmapData):ClosedPathList
    {
        var pathList:Array = PathList.create(bmpdata);
        return ProcessPath.processPath(pathList);
    }

    /**
     *  draw.
     */
    public function draw(g:Graphics):void
    {
        for each(var path:* in $a)
        {
            if(path is ClosedPath)
            {
                path.draw(g);
            }
        }
    }
}

/* Copyright (C) 2001-2007 Peter Selinger and nitoyon.
   Original code(Potrace v1.8) by Peter Selinger.
   Ported to ActionScript 3.0 by nitoyon.
   This file is part of PotrAs. It is free software and it is covered
   by the GNU General Public License. See the file COPYING for details. */
import flash.display.BitmapData;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.geom.Matrix;
import flash.filters.ColorMatrixFilter;
class PathList
{
    /**
     * Decompose the given bitmap into paths.
     *
     * @param bitmapData BitmapData to create paths.
     * @return paths.
     */
    public static function create(bitmapData:BitmapData):Array
    {
        var pathList:Array = [];
        var y:int = 0;

        var bmdCopy:BitmapData = bitmapData.clone();
        var param:Object = {turdSize : 3};

        // XOR
        var filter:ColorMatrixFilter = new ColorMatrixFilter([
            -1, 0, 0, 0, 255, 
            -1, 0, 0, 0, 255, 
            -1, 0, 0, 0, 255,
             0, 0, 0, 1, 0
        ]);

        var point:Point = new Point();
        while(findNext(bmdCopy, point))
        {
            // calculate the sign by looking at the original
            var sign:String = bmdCopy.getPixel(point.x, point.y) == 0 ? '+' : '-';

            // calculate the path
            var p:Object = findPath(bmdCopy, new Point(point.x, point.y - 1), sign, param.turnPolicy);
            if(!p)
            {
                pathList = null;
                break;
            }

            // update buffered image
            xorPath(bmdCopy, p, filter);

            // if it's a turd, eliminate it, else append it to the list
            if(p.area > param.turdSize)
            {
                pathList.push(p);
            }
        }

        bmdCopy.dispose();
        return pathList;
    }

    /**
     * find the next set pixel in a row <= y.
     *
     * <p>Pixels are searched first left-to-right, then top-down.</p>
     *
     * <p>If found, return Point object. Else return null.</p>
     */
    private static function findNext(bmd:BitmapData, pt:Point):Boolean
    {
        for(var y:int = pt.y; y < bmd.height; y++)
        {
            for(var x:int = 0; x < bmd.width; x++)
            {
                if(bmd.getPixel(x, y) == 0x000000)
                {
                    pt.x = x;
                    pt.y = y;
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * compute a path in the given pixmap, separating black from white.
     *
     * <p>Start path at the <code>startPoint</code>, which must be an upper
     * left corner of the path.
     * Also compute the area enclosed by the path. Return a
     * new path object. (note that a legitimate path
     * cannot have length 0).</p>
     *
     * @param sign Required for correct interpretation of turnpolicies.
     */
    private static function findPath(bmd:BitmapData, startPoint:Point, sign:String, turnpolicy:int):Object
    {
        var area:int = 0;
        var pointList:Array = [];

        var pt:Point = startPoint.clone();
        var dir:Point = new Point(0, 1);

        var rotateRight:Matrix = new Matrix(0, -1, 1, 0);
        var rotateLeft:Matrix  = new Matrix(0, 1, -1, 0);

        while(true)
        {
            // add point to path
            pointList.push(pt.clone());

            // move to next point
            pt.offset(dir.x, dir.y);
            area += pt.x * -dir.y;

            // path complete?
            if(pt.equals(startPoint))
            {
                break;
            }

            // determine next direction
            var c:Boolean = bmd.getPixel32(pt.x + (dir.x - dir.y - 1) /2, pt.y + (dir.y + dir.x + 1) / 2) == 0xff000000;
            var d:Boolean = bmd.getPixel32(pt.x + (dir.x + dir.y - 1) /2, pt.y + (dir.y - dir.x + 1) / 2) == 0xff000000;

            // c d
            // X _
            if(c && !d) // ambiguous turn
            {
                if(true) // TODO: left turn only by nitoyon
                {
                    dir = rotateLeft.transformPoint(dir);
                }
                else
                {
                    dir = rotateRight.transformPoint(dir);
                }
            }
            else if(c) // left turn
            {
                dir = rotateLeft.transformPoint(dir);
            }
            else if(!d) // right turn
            {
                dir = rotateRight.transformPoint(dir);
            }
        }

        // allocate new path object
        var path:Object = {};
        path.priv = pointList;
        path.area = area;
        path.sign = sign;

        return path;
    }

    /**
     *  xor the given pixmap with the interior of the given path. 
     *  Note: the path must be within the dimensions of the pixmap.
     */
    private static function xorPath(bm:BitmapData, p:Object, filter:ColorMatrixFilter):void
    {
        var priv:Array = p.priv as Array;
        var len:int = priv.length;

        // get minimum x
        var minX:int = 99999;
        for(var i:int = 0; i < len; i++)
        {
            minX = Math.min(minX, priv[i].x);
        }

        var y1:int = priv[len - 1].y;
        var pt:Point = new Point();
        var rect:Rectangle = new Rectangle;
        for(i = 0; i < len; i++)
        {
            var x:int = priv[i].x;
            var y:int = priv[i].y;

            if(y != y1)
            {
                // efficiently invert the rectangle [minX, x] x [y,y1]
                var y2:int = Math.max(y, y1);
                pt.x = minX; pt.y = y2;
                rect.x = minX; rect.y = y2; rect.width = x - minX; rect.height = 1;
                bm.applyFilter(bm, rect, pt, filter);
                y1 = y;
            }
        }
    }
}

/* Copyright (C) 2001-2007 Peter Selinger and nitoyon.
   Original code(Potrace v1.8) by Peter Selinger.
   Ported to ActionScript 3.0 by nitoyon.
   This file is part of PotrAs. It is free software and it is covered
   by the GNU General Public License. See the file COPYING for details. */
import flash.geom.Point;
class Curve
{
    public var tag:int;                 /* tag[n]: POTRACE_CORNER or POTRACE_CURVETO */
    public var c:Array = new Array(3); /* c[n][i]: control points. 
                                             c[n][0] is unused for tag[n]=POTRACE_CORNER */
                                /* the remainder of this structure is special to privcurve, and is
         used in EPS debug output and special EPS "short coding". These
         fields are valid only if "alphacurve" is set. */
    public var vertex:Point; /* for POTRACE_CORNER, this equals c[1] */
    public var alpha:Number;        /* only for POTRACE_CURVETO */
    public var alpha0:Number;     /* "uncropped" alpha parameter - for debug output only */
    public var beta:Number;

    /**
     *  Constructor.
     *
     *  initialize the members of the given curve structure to size m.
     *  Return 0 on success, 1 on error with errno set.
     */
    public function Curve():void
    {
        c[0] = new Point();
        c[1] = new Point();
        c[2] = new Point();
        vertex = new Point();
    }

    public function toString():String
    {
        return "alpha0: " + alpha0 + "\n"
             + "alpha:  " + alpha + "\n"
             + "beta:   " + beta + "\n"
             + "corner: " + (tag == ProcessPath.POTRACE_CORNER) + "\n"
             + "bezier: " + c[0] + "," + c[1] + "," + c[2];
    }
}

/* Copyright (C) 2001-2007 Peter Selinger and nitoyon.
   Original code(Potrace v1.8) by Peter Selinger.
   Ported to ActionScript 3.0 by nitoyon.
   This file is part of PotrAs. It is free software and it is covered
   by the GNU General Public License. See the file COPYING for details. */
import flash.display.BitmapData;
import flash.geom.*;
class ProcessPath
{
    /**
     *  it suffices that this is longer than any
     *  path; it need not be really infinite
     */
    private static const INFTY:int = 10000000;

    public static const POTRACE_CURVETO:int = 1;
    public static const POTRACE_CORNER:int = 2;

    /**
     * On success, returns a Potrace state st with st->status ==
     * POTRACE_STATUS_OK. On failure, returns NULL if no Potrace state
     * could be created (with errno set), or returns an incomplete Potrace
     * state (with st->status == POTRACE_STATUS_INCOMPLETE). Complete or
     * incomplete Potrace state can be freed with potrace_state_free().
     */
    public static function processPath(pathList:Array):ClosedPathList
    {
        var curveList:ClosedPathList = new ClosedPathList();

        for(var i:int = 0; i < pathList.length; i++)
        {
            var sums:Array       = ProcessPath.calcSums(pathList[i].priv as Array) as Array;
            var lons:Array       = ProcessPath.calcLon(pathList[i].priv as Array);
            var po:Array         = ProcessPath.bestPolygon(pathList[i].priv as Array, lons, sums);
            var vertex:Array     = ProcessPath.adjustVertices(pathList[i].priv as Array, sums, po);
            var curve:ClosedPath = ProcessPath.smooth(vertex, pathList[i].sign, .9);

            curveList.$a.push(curve);
        }

        return curveList;
    }

    /**
     * Preparation: fill in the sum* fields of a path (used for later
     * rapid summing). Return 0 on success, 1 with errno set on
     * failure.
     */
    public static function calcSums(pt:Array):Array
    {
        var n:int = pt.length;

        var sums:Array = new Array(n + 1);

        // origin
        var x0:int = pt[0].x;
        var y0:int = pt[0].y;

        // preparatory computation for later fast summing
        sums[0] = new Sums();
        sums[0].x2 = sums[0].xy = sums[0].y2 = sums[0].x = sums[0].y = 0;
        for(var i:int = 0; i < n; i++)
        {
            var x:int = pt[i].x - x0;
            var y:int = pt[i].y - y0;
            sums[i + 1] = new Sums();
            sums[i + 1].x  = sums[i].x  + x;
            sums[i + 1].y  = sums[i].y  + y;
            sums[i + 1].x2 = sums[i].x2 + x*x;
            sums[i + 1].xy = sums[i].xy + x*y;
            sums[i + 1].y2 = sums[i].y2 + y*y;
        }


        return sums;
    }

    /**
     * Stage 1: determine the straight subpaths (Sec. 2.2.1). Fill in the
     * "lon" component of a path object (based on pt/len).        For each i,
     * lon[i] is the furthest index such that a straight line can be drawn
     * from i to lon[i]. Return 1 on error with errno set, else 0.
     */
    public static function calcLon(pt:Array):Array
    {
        var lon:Array = [];
        var n:int = pt.length;
        var j:Number;

        // initialize the nc data structure. Point from each point to the
        // furthest future point to which it is connected by a vertical or
        // horizontal segment. We take advantage of the fact that there is
        // always a direction change at 0 (due to the path decomposition
        // algorithm). But even if this were not so, there is no harm, as
        // in practice, correctness does not depend on the word "furthest"
        // above.
        var nc:Array = [];
        var k:int = 0;
        for(var i:int = n - 1; i >= 0; i--)
        {
            if(pt[i].x != pt[k].x && pt[i].y != pt[k].y)
            {
                k = i + 1; // necessarily i<n-1 in this case
            }
            nc[i] = k;
        }

        // determine pivot points: for each i, let pivk[i] be the furthest k
        // such that all j with i<j<k lie on a line connecting i,k. */
        var pivk:Array = [];
        var ct:Array;
        var dir:int;
        var k1:int;
        var constraint0:Point = new Point();
        var constraint1:Point = new Point();
        var cur:Point = new Point();
        var off:Point = new Point();
        var dk:Point = new Point();
        for(i = n - 1; i >= 0; i--)
        {
            ct = [0, 0, 0, 0];

            // keep track of "directions" that have occurred
            dir = (3 + 3 * (pt[mod(i + 1, n)].x - pt[i].x) + (pt[mod(i + 1, n)].y - pt[i].y)) / 2;
            ct[dir]++;
            constraint0.x = constraint0.y = constraint1.x = constraint1.y = 0;

            // find the next k such that no straight line from i to k
            k = nc[i];
            k1 = i;
            var foundk:Boolean = false;
            while(true)
            {
                dir = (3 + 3 * sign(pt[k].x - pt[k1].x) + sign(pt[k].y - pt[k1].y)) / 2;
                ct[dir]++;

                // if all four "directions" have occurred, cut this path
                if(ct[0] && ct[1] && ct[2] && ct[3])
                {
                    pivk[i] = k1;
                    foundk = true;
                    break;
                }

                cur.x = pt[k].x - pt[i].x;
                cur.y = pt[k].y - pt[i].y;

                // see if current constraint is violated
                if(xprod(constraint0, cur) > 0 || xprod(constraint1, cur) < 0)
                {
                    break;
                }

                // else, update constraint
                if (Math.abs(cur.x) <= 1 && Math.abs(cur.y) <= 1)
                {
                    // no constraint
                }
                else
                {
                    off.x = cur.x + ((-cur.y >= 0 && (-cur.y > 0 || cur.x < 0)) ? 1 : -1);
                    off.y = cur.y + ((-cur.x <= 0 && (-cur.x < 0 || cur.y < 0)) ? 1 : -1);
                    if(xprod(constraint0, off) <= 0)
                    {
                        constraint0.x = off.x;
                        constraint0.y = off.y;
                    }

                    off.x = cur.x + ((-cur.y <= 0 && (-cur.y < 0 || cur.x < 0)) ? 1 : -1);
                    off.y = cur.y + ((-cur.x >= 0 && (-cur.x > 0 || cur.y < 0)) ? 1 : -1);
                    if(xprod(constraint1, off) >= 0)
                    {
                        constraint1.x = off.x;
                        constraint1.y = off.y;
                    }
                }

                k1 = k;
                k = nc[k1];
                if(!cyclic(k, i, k1))
                {
                    break;
                }
            }

            // constraint
            if(!foundk)
            {
                dk.x = sign(pt[k].x-pt[k1].x);
                dk.y = sign(pt[k].y-pt[k1].y);
                cur.x = pt[k1].x - pt[i].x;
                cur.y = pt[k1].y - pt[i].y;

                // find largest integer j such that xprod(constraint0, cur+j*dk) >= 0 
                // and xprod(constraint1, cur+j*dk) <= 0. Use bilinearity of xprod.
                var a:int = xprod(constraint0, cur);
                var b:int = xprod(constraint0, dk);
                var c:int = xprod(constraint1, cur);
                var d:int = xprod(constraint1, dk);

                // find largest integer j such that a+j*b>=0 and c+j*d<=0.
                // This can be solved with integer arithmetic.
                j = INFTY;
                if(b > 0)
                {
                    j = floordiv(-a, b);
                }
                if(d < 0)
                {
                    j = Math.min(j, floordiv(c, -d));
                }
                pivk[i] = mod(k1 + j, n);
            }

            // foundk
        } // for i

        // clean up: for each i, let lon[i] be the largest k such that for
        // all i' with i<=i'<k, i'<k<=pivk[i'].
        j = pivk[n - 1];
        lon[n - 1] = j;
        for(i = n - 2; i >= 0; i--)
        {
            if(cyclic(i + 1, pivk[i], j))
            {
                j = pivk[i];
            }
            lon[i] = j;
        }

        for(i = n - 1; cyclic(mod(i + 1, n), j, lon[i]); i--)
        {
            lon[i] = j;
        }

        return lon;
    }

    /**
     * Stage 2: calculate the optimal polygon (Sec. 2.2.2-2.2.4).
     *
     * find the optimal polygon. Fill in the m and po components. Return 1
     * on failure with errno set, else 0. Non-cyclic version: assumes i=0
     * is in the polygon. Fixme: ### implement cyclic version.
     */
    public static function bestPolygon(pt:Array, lon:Array, sums:Array):Array
    {
        var i:int, j:int, k:int;
        var n:int = pt.length;

        var clip0:Array = new Array(n);        // clip0[n]: longest segment pointer, non-cyclic
        var clip1:Array = new Array(n + 1);    // clip1[n+1]: backwards segment pointer, non-cyclic

        // calculate clipped paths
        for(i = 0; i < n; i++)
        {
            var c:int = mod(lon[mod(i - 1, n)] - 1, n);
            if(c == i)
            {
                c = mod(i + 1, n);
            }
            if(c < i)
            {
                clip0[i] = n;
            }
            else
            {
                clip0[i] = c;
            }
        }

        // calculate backwards path clipping, non-cyclic. j <= clip0[i] iff clip1[j] <= i, for i,j=0..n.
        j = 1;
        for(i = 0; i < n; i++)
        {
            while(j <= clip0[i])
            {
                clip1[j] = i;
                j++;
            }
        }

        var seg0:Array = new Array(n + 1);        // seg0[m+1]: forward segment bounds, m<=n
        var seg1:Array = new Array(n + 1);         // seg1[m+1]: backward segment bounds, m<=n

        // calculate seg0[j] = longest path from 0 with j segments
        i = 0;
        for(j = 0; i < n; j++)
        {
            seg0[j] = i;
            i = clip0[i];
        }
        seg0[j] = n;
        var m:int = j;

        // calculate seg1[j] = longest path to n with m-j segments
        i = n;
        for(j = m; j > 0; j--)
        {
            seg1[j] = i;
            i = clip1[i];
        }
        seg1[0] = 0;

        // now find the shortest path with m segments, based on penalty3
        // note: the outer 2 loops jointly have at most n interations, thus
        // the worst-case behavior here is quadratic. In practice, it is
        // close to linear since the inner loop tends to be short.
        var pen:Array   = new Array(n + 1); // pen[n+1]: penalty vector
        var prev:Array  = new Array(n + 1);    // prev[n+1]: best path pointer vector
        var thispen:Number;
        pen[0] = 0;
        for(j = 1; j <= m; j++)
        {
            for(i = seg1[j]; i <= seg0[j]; i++)
            {
                var best:Number = -1;
                for(k = seg0[j - 1]; k >= clip1[i]; k--)
                {
                    thispen = penalty3(pt, k, i, sums) + pen[k];
                    if(best < 0 || thispen < best)
                    {
                        prev[i] = k;
                        best = thispen;
                    }
                }
                pen[i] = best;
            }
        }

        // read off shortest path
        var po:Array = new Array(m);
        for(i = n, j = m - 1; i > 0; j--)
        {
            po[j] = i = prev[i];
        }

        return po;
    }

    /**
     * Auxiliary function: calculate the penalty of an edge from i to j in
     * the given path. This needs the "lon" and "sum*" data.
     */
    private static function penalty3(pt:Array, i:int, j:int, sums:Array):Number
    {
        var n:int = pt.length;

        // assume 0 <= i < j <= n
        var r:int = 0; // rotations from i to j

        if(j >= n)
        {
            j -= n;
            r += 1;
        }
        
        var x:Number  = sums[j + 1].x  - sums[i].x  + r * sums[n].x;
        var y:Number  = sums[j + 1].y  - sums[i].y  + r * sums[n].y;
        var x2:Number = sums[j + 1].x2 - sums[i].x2 + r * sums[n].x2;
        var xy:Number = sums[j + 1].xy - sums[i].xy + r * sums[n].xy;
        var y2:Number = sums[j + 1].y2 - sums[i].y2 + r * sums[n].y2;
        var k:Number  = j + 1 - i + r * n;

        var px:Number = (pt[i].x + pt[j].x) / 2.0 - pt[0].x;
        var py:Number = (pt[i].y + pt[j].y) / 2.0 - pt[0].y;
        var ey:Number = (pt[j].x - pt[i].x);
        var ex:Number =  -(pt[j].y - pt[i].y);

        var a:Number = ((x2 - 2 * x * px) / k + px * px);
        var b:Number = ((xy - x * py - y * px) / k + px * py);
        var c:Number = ((y2 - 2 * y * py) / k + py * py);
        
        var s:Number = ex * ex * a  +  2 * ex * ey * b  +  ey * ey * c;

        return Math.sqrt(s);
    }

    /**
    /* Stage 3: vertex adjustment (Sec. 2.3.1).
     *
     * Adjust vertices of optimal polygon: calculate the intersection of
     * the two "optimal" line segments, then move it into the unit square
     * if it lies outside. Return 1 with errno set on error; 0 on
     * success.
     */
    public static function adjustVertices(pt:Array, sums:Array, po:Array):Array
    {
        var m:int = po.length;
        var n:int = pt.length;

        var i:int, j:int, k:int, l:int;

        // represent each line segment as a singular quadratic form; the
        // distance of a point (x,y) from the line segment will be
        // (x,y,1)Q(x,y,1)^t, where Q=q[i].
        var q:Array = [];
        var v:Point3D = new Point3D();
        var ctr:Point = new Point();
        var dir:Point = new Point();
        for(i = 0; i < m; i++)
        {
            // calculate "optimal" point-slope representation for each line segment
            j = po[(i + 1) % m];
            j = mod(j - po[i], n) + po[i];
            ctr.x = ctr.y = dir.x = dir.y = 0;
            pointslope(pt, sums, po[i], j, ctr, dir);

            q[i] = new QuadraticForm();
            var d1:Number = dir.x * dir.x + dir.y * dir.y;
            if(d1 != 0.0)
            {
                v[0] = dir.y;
                v[1] = -dir.x;
                v[2] = -v[1] * ctr.y - v[0] * ctr.x
                q[i].fromVectorMultiply(v).scalar(1 / d1);
            }
        }

        // now calculate the "intersections" of consecutive segments.
        // Instead of using the actual intersection, we find the point
        // within a given unit square which minimizes the square distance to
        // the two lines.
        var vertex:Array = [];
        var p0:Point = pt[0].clone();
        var s:Point = new Point();
        var w:Point = new Point();
        var _q:QuadraticForm = new QuadraticForm();
        var minCoord:Point = new Point;
        for(i = 0; i < m; i++)
        {
            var z:int;
            vertex[i] = new Point();

            // let s be the vertex, in coordinates relative to x0/y0
            s.x = pt[po[i]].x - p0.x;
            s.y = pt[po[i]].y - p0.y;

            // intersect segments i-1 and i
            j = mod(i - 1, m);

            // add quadratic forms
            var Q:QuadraticForm = q[j].clone().add(q[i]);

            while(1)
            {
                // minimize the quadratic form Q on the unit square
                // find intersection
                var det:Number = Q[0][0] * Q[1][1] - Q[0][1] * Q[1][0];
                if(det != 0.0)
                {
                    w.x = (-Q[0][2] * Q[1][1] + Q[1][2] * Q[0][1]) / det;
                    w.y = ( Q[0][2] * Q[1][0] - Q[1][2] * Q[0][0]) / det;
                    break;
                }

                // matrix is singular - lines are parallel. Add another,
                // orthogonal axis, through the center of the unit square
                if(Q[0][0] > Q[1][1])
                {
                    v[0] = -Q[0][1];
                    v[1] = Q[0][0];
                }
                else if(Q[1][1])
                {
                    v[0] = -Q[1][1];
                    v[1] = Q[1][0];
                }
                else
                {
                    v[0] = 1;
                    v[1] = 0;
                }

                var d:Number = v[0] * v[0] + v[1] * v[1];
                v[2] = - v[1] * s.y - v[0] * s.x;
                Q.add(_q.fromVectorMultiply(v)).scalar(1 / d);
            }

            var dx:Number = Math.abs(w.x - s.x);
            var dy:Number = Math.abs(w.y - s.y);
            if(dx <= .5 && dy <= .5)
            {
                vertex[i].x = w.x + p0.x;
                vertex[i].y = w.y + p0.y;
                continue;
            }

            // the minimum was not in the unit square; now minimize quadratic
            // on boundary of square
            var min:Number, cand:Number; // minimum and candidate for minimum of quad. form
            minCoord.x = s.x; minCoord.y = s.y; // coordinates of minimum
            min = Q.apply(s);

            if(Q[0][0] != 0.0)
            {
                for(z = 0; z < 2; z++) // value of the y-coordinate
                {
                    w.y = s.y - 0.5 + z;
                    w.x = - (Q[0][1] * w.y + Q[0][2]) / Q[0][0];
                    dx = (w.x - s.x > 0 ? w.x - s.x : s.x - w.x);
                    cand = Q.apply(w);
                    if(dx <= .5 && cand < min)
                    {
                        min = cand;
                        minCoord.x = w.x; minCoord.y = w.y;
                    }
                }
            }

            if(Q[1][1] != 0.0)
            {
                for(z = 0; z < 2; z++)     // value of the x-coordinate
                {
                    w.x = s.x - 0.5 + z;
                    w.y = - (Q[1][0] * w.x + Q[1][2]) / Q[1][1];
                    dy = (w.y - s.y > 0 ? w.y - s.y : s.y - w.y);
                    cand = Q.apply(w);
                    if(dy <= .5 && cand < min)
                    {
                        min = cand;
                        minCoord.x = w.x; minCoord.y = w.y;
                    }
                }
            }

            // check four corners
            for(l = 0; l < 2; l++)
            {
                for(k = 0; k < 2; k++)
                {
                    w.x = s.x - 0.5 + l;
                    w.y = s.y - 0.5 + k;
                    cand = Q.apply(w);
                    if(cand < min)
                    {
                        min = cand;
                        minCoord.x = w.x; minCoord.y = w.y;
                    }
                }
            }

            vertex[i].x = minCoord.x + p0.x;
            vertex[i].y = minCoord.y + p0.y;
            continue;
        }

        return vertex;
    }

    /**
     *  Stage 4: smoothing and corner analysis (Sec. 2.3.3)
     *
     *  Always succeeds and returns 0
     */
    public static function smooth(vertex:Array, sign:String, alphamax:Number = 1.0):ClosedPath
    {
        var m:int = vertex.length;
        var closedPath:ClosedPath = new ClosedPath();

        var i:int;

        if(sign == '-')
        {
            // reverse orientation of negative paths
            var tmp:Point;
            for(i = 0, j = m - 1; i < j; i++, j--)
            {
                tmp = vertex[i];
                vertex[i] = vertex[j];
                vertex[j] = tmp;
            }
        }

        // examine each vertex and find its best fit
        var p2:Point = new Point();
        var p3:Point = new Point();
        var p4:Point = new Point();
        for(i = 0; i < m; i++)
        {
            var j:int = (i + 1) % m;
            var k:int = (i + 2) % m;
            interval(1 / 2.0, vertex[k], vertex[j], p4);

            var curve:Curve = new Curve();
            curve.vertex.x =vertex[j].x;
            curve.vertex.y =vertex[j].y;

            var denom:Number = ddenom(vertex[i], vertex[k]);
            var alpha:Number;
            if(denom != 0.0)
            {
                var dd:Number = dpara(vertex[i], vertex[j], vertex[k]) / denom;
                dd = dd < 0 ? -dd : dd;
                alpha = dd > 1 ? (1 - 1.0 / dd) / 0.75 : 0;
            }
            else
            {
                alpha = 4 / 3.0;
            }
            curve.alpha0 = alpha;   // remember "original" value of alpha

            if(alpha > alphamax)  // pointed corner
            {
                curve.tag  = POTRACE_CORNER;
                curve.c[0].x = 0; curve.c[0].y = 0;
                curve.c[1].x = vertex[j].x; curve.c[1].y = vertex[j].y;
                curve.c[2].x = p4.x; curve.c[2].y = p4.y;
            }
            else
            {
                if(alpha < 0.55)
                {
                    alpha = 0.55;
                }
                else if(alpha > 1)
                {
                    alpha = 1;
                }
                interval(.5 + .5 * alpha, vertex[i], vertex[j], p2);
                interval(.5 + .5 * alpha, vertex[k], vertex[j], p3);
                curve.tag = POTRACE_CURVETO;
                curve.c[0].x = p2.x; curve.c[0].y = p2.y;
                curve.c[1].x = p3.x; curve.c[1].y = p3.y;
                curve.c[2].x = p4.x; curve.c[2].y = p4.y;
            }
            curve.alpha = alpha; // store the "cropped" value of alpha
            curve.beta  = 0.5;

            closedPath.$a[j] = curve;
        }

        return closedPath;
    }

    /**
     *  determine the center and slope of the line i..j. Assume i<j. Needs
     *    "sum" components of p to be set.
     */
    public static function pointslope(pt:Array, sums:Array, i:int, j:int, ctr:Point, dir:Point):void
    {
        // assume i<j

        var n:int = pt.length;

        var x:Number, y:Number, x2:Number, xy:Number, y2:Number;
        var k:Number;
        var a:Number, b:Number, c:Number, lambda2:Number, l:Number;
        var r:int = 0; // rotations from i to j

        while (j >= n)
        {
            j -= n;
            r += 1;
        }
        while(i >= n)
        {
            i -= n;
            r -= 1;
        }
        while(j < 0)
        {
            j += n;
            r -= 1;
        }
        while(i < 0)
        {
            i += n;
            r += 1;
        }
        
        x  = sums[j + 1].x  - sums[i].x  + r * sums[n].x;
        y  = sums[j + 1].y  - sums[i].y  + r * sums[n].y;
        x2 = sums[j + 1].x2 - sums[i].x2 + r * sums[n].x2;
        xy = sums[j + 1].xy - sums[i].xy + r * sums[n].xy;
        y2 = sums[j + 1].y2 - sums[i].y2 + r * sums[n].y2;
        k = j + 1 - i + r * n;
        
        ctr.x = x / k;
        ctr.y = y / k;

        a = (x2 - x * x / k) / k;
        b = (xy - x * y / k) / k;
        c = (y2 - y * y / k) / k;
        
        lambda2 = (a + c + Math.sqrt((a - c)*(a - c) + 4 * b * b)) / 2; // larger e.value 

        // now find e.vector for lambda2
        a -= lambda2;
        c -= lambda2;

        if(Math.abs(a) >= Math.abs(c))
        {
            l = Math.sqrt(a * a + b * b);
            if(l != 0)
            {
                dir.x = -b / l;
                dir.y =  a / l;
            }
        }
        else
        {
            l = Math.sqrt(c * c + b * b);
            if (l != 0)
            {
                dir.x = -c / l;
                dir.y =  b / l;
            }
        }
        if(l == 0)
        {
            dir.x = dir.y = 0;  // sometimes this can happen when k=4:
                                  // the two eigenvalues coincide
        }
    }

    /**
     *  range over the straight line segment [a,b] when lambda ranges over [0,1]
     */
    public static function interval(lambda:Number, a:Point, b:Point, ret:Point):void
    {
        ret.x = a.x + lambda * (b.x - a.x);
        ret.y = a.y + lambda * (b.y - a.y);
    }

    /**
     *  some useful macros. Note: the "mod" macro works correctly for
     *  negative a. Also note that the test for a>=n, while redundant,
     *  speeds up the mod function by 70% in the average case (significant
     *  since the program spends about 16% of its time here - or 40%
     *  without the test). The "floordiv" macro returns the largest integer
     *  <= a/n, and again this works correctly for negative a, as long as
     *  a,n are integers and n>0.
     */
    private static function mod(a:int, n:int):int
    {
        return a >= n ? a % n : a >= 0 ? a : n - 1 - (-1 - a) % n;
    }

    private static function floordiv(a:int, n:int):int
    {
        return a >= 0 ? a / n : -1 - (-1 - a) / n;
    }

    /**
     *  calculate p1 x p2
     */
    private static function xprod(p1:Point, p2:Point):int
    {
        return p1.x * p2.y - p1.y * p2.x;
    }

    /**
     *  sign
     */
    private static function sign(x:Number):int
    {
        return (x > 0 ? 1 : x < 0 ? -1 : 0);
    }

    /**
     *  return a direction that is 90 degrees counterclockwise from p2-p0,
     *  but then restricted to one of the major wind directions (n, nw, w, etc)
     */
    public static function dorth_infty(p0:Point, p2:Point):Point
    {
        return new Point(
            sign(p2.x  - p0.x), 
            -sign(p2.y - p0.y)
            );
    }

    /**
     *  return (p1-p0)x(p2-p0), the area of the parallelogram
     */
    static public function dpara(p0:Point, p1:Point, p2:Point):Number
    {
        var x1:Number = p1.x - p0.x;
        var y1:Number = p1.y - p0.y;
        var x2:Number = p2.x - p0.x;
        var y2:Number = p2.y - p0.y;

        return x1 * y2 - x2 * y1;
    }

    /**
     *  ddenom/dpara have the property that the square of radius 1 centered
     *  at p1 intersects the line p0p2 iff |dpara(p0,p1,p2)| <= ddenom(p0,p2)
     */
    public static function ddenom(p0:Point, p2:Point):Number
    {
        var r:Point = dorth_infty(p0, p2);
        return r.y * (p2.x - p0.x) - r.x * (p2.y - p0.y);
    }

    /**
     *  return 1 if a <= b < c < a, in a cyclic sense (mod n)
     */
    private static function cyclic(a:int, b:int, c:int):Boolean
    {
        if (a <= c)
        {
            return (a <= b) && (b < c);
        }
        else
        {
            return (a <= b) || (b < c);
        }
    }
}

internal class Sums
{
    public var x:Number;
    public var y:Number;
    public var x2:Number;
    public var xy:Number;
    public var y2:Number;
}

import flash.geom.Point;
import flash.utils.Proxy;
import flash.utils.flash_proxy;
import flash.errors.IllegalOperationError;

internal class Point3D extends Proxy
{
    private var $v:Array;

    public function Point3D(x:Number = 0.0, y:Number = 0.0, z:Number = 0.0):void
    {
        $v = [x, y, z];
    }

    flash_proxy override function getProperty(name:*):*
    {
        if(name == 0 || name == "x") return $v[0];
        if(name == 1 || name == "y") return $v[1];
        if(name == 2 || name == "z") return $v[2];
        return undefined;
    }

    flash_proxy override function hasProperty(name:*):Boolean
    {
        return flash_proxy.getProperty(name) != undefined;
    }

    flash_proxy override function setProperty(name:*, value:*):void
    {
        if(name == 0 || name == "x"){$v[0] = Number(value); return;}
        if(name == 1 || name == "y"){$v[1] = Number(value); return;}
        if(name == 2 || name == "z"){$v[2] = Number(value); return;}
        throw new IllegalOperationError();
    }

    public function toString():String
    {
        return "(" + $v[0] + "," + $v[1] + "," + $v[2] + ")";
    }
}

/**
 *  the type of (affine) quadratic forms, represented as symmetric 3x3
 *  matrices.  The value of the quadratic form at a vector (x,y) is v^t
 *  Q v, where v = (x,y,1)^t.
 */
internal class QuadraticForm extends Proxy
{
    private var $m:Array;

    public function QuadraticForm():void
    {
        $m = [];
        $m[0] = new Point3D();
        $m[1] = new Point3D();
        $m[2] = new Point3D();
    }

    flash_proxy override function getProperty(name:*):*
    {
        if(name == 0) return $m[0];
        if(name == 1) return $m[1];
        if(name == 2) return $m[2];
        return undefined;
    }

    flash_proxy override function hasProperty(name:*):Boolean
    {
        return flash_proxy.getProperty(name) != undefined;
    }

    flash_proxy override function setProperty(name:*, value:*):void
    {
        throw new IllegalOperationError();
    }

    /**
     *  Apply quadratic form Q to vector w = (w.x,w.y)
     */
    public function apply(w:Point):Number
    {
        var v:Array = [w.x, w.y, 1];
        var sum:Number = 0.0;

        for(var i:int = 0; i < 3; i++)
        {
            for(var j:int = 0; j < 3; j++)
            {
                sum += v[i] * $m[i][j] * v[j];
            }
        }
        return sum;
    }

    public function clone():QuadraticForm
    {
        var ret:QuadraticForm = new QuadraticForm();
        for(var i:int = 0; i < 3; i++)
        {
            for(var j:int = 0; j < 3; j++)
            {
                ret[i][j] = $m[i][j];
            }
        }
        return ret;
    }

    public function add(m2:QuadraticForm):QuadraticForm
    {
        for(var i:int = 0; i < 3; i++)
        {
            for(var j:int = 0; j < 3; j++)
            {
                $m[i][j] += m2[i][j];
            }
        }
        return this;
    }

    public function scalar(s:Number):QuadraticForm
    {
        for(var i:int = 0; i < 3; i++)
        {
            for(var j:int = 0; j < 3; j++)
            {
                $m[i][j] *= s;
            }
        }
        return this;
    }

    public function fromVectorMultiply(v:Point3D):QuadraticForm
    {
        for(var i:int = 0; i < 3; i++)
        {
            for(var j:int = 0; j < 3; j++)
            {
                $m[i][j] = v[i] * v[j];
            }
        }
        return this;
    }

    public function toString():String
    {
        return "[" + 
            $m[0][0] + "," + $m[0][1] + "," + $m[0][2] + "," + 
            $m[1][0] + "," + $m[1][1] + "," + $m[1][2] + "," + 
            $m[2][0] + "," + $m[2][1] + "," + $m[2][2] + "]";
    }
}