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

3D pointcloud

Pointcloud of a 3D geometric shapes, using 2d flash api.

It's a basic way to draw 3D shapes in isometric projection. 

FP 10+ required.
package {
    import flash.text.TextFormat;
    /**
     * by Ke.Kurono. 2014. All rights reserved.
     *
     */ 
    import flash.events.TimerEvent;
    import flash.utils.Timer;
    import flash.display.Shape;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.display.Sprite;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.geom.Point;
    import flash.geom.Vector3D;
    import flash.text.TextField;
    import flash.display.StageQuality;
    
    public class FlashTest extends Sprite {
        private var _points:Vector.<Vector3D>;
        private var _transformedPoints:Vector.<Vector3D>;
        private var _scale:Number = 2;
        private var _color:uint = 0x000000;
        // euler angles
        private var _ex:Number = 0;
        private var _ey:Number = 0;
        private var _ez:Number = 0;
        // canvas
        private var _w:Number = 500;
        private var _h:Number = 500;
        private var _clearScreen:BitmapData = new BitmapData(_w, _h, true, 0xff000000);
        private var _fadeScreen:BitmapData = new BitmapData(_w, _h, true, 0x11000000);
        private var _canvas:BitmapData = new BitmapData(_w, _h, true, 0x00000000);
        // formulas
        private var _currentFunction:Function;
        private var _functions:Array;
        private var _i:uint;
        // button's height
        private var _buttonDim:Number = 20;
        // caption textfield
        private var _caption:TextField;
        
        public function FlashTest() {
            // write as3 code here..
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);            
        }
        
        /**
         * Entry point
         * @param    e Event.ADDED_TO_STAGE
         */
        private function init(e:Event = null):void 
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            // entry point
            
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;
            stage.quality = StageQuality.LOW;
            
            // add functions
            _i = 0;
            _functions = [
                [formulaY1, 'hyperbolic paraboloid'], 
                [formulaY2, 'ring wave'],
                [formulaY3, 'sphere'], 
                [formulaY4, 'plane waves'],
                [formulaY5, 'cube'],
                [formulaY6, 'cylinder'],
                [formulaY7, 'cone']
            ];
            
            // add canvas
            var bmp:Bitmap = new Bitmap(_canvas, "auto", true);
            addChild(bmp);
            
            _currentFunction = formulaY2;
            initDataPoints();
            
            // caption text
            _caption = new TextField();
            _caption.defaultTextFormat = new TextFormat('Verdana', 14, 0xffffff, null, null, null, null, null, 'center');
            _caption.text = '';
            _caption.y = 10;
            _caption.width = stage.stageWidth;
            addChild(_caption);
            
            // start rendering
            addEventListener(Event.ENTER_FRAME, render);
            stage.addEventListener(Event.RESIZE, resize);
            
            resize();
            
            createButtons();
            
            initFormulaChanging();
        }
        
        /**
         * Build a set of points in 3d space specified by _currentFunction
         * @param    e
         */
        private function initDataPoints(e:Event = null):void {
            // build a set of points
            _points = buildPointCloud(_currentFunction, 100, false);
        }
        
        /**
         * Projects 3d vector to 2d plane specified by euler angles
         * @param    pos initial 3d vector
         * @param    rot euler angles
         * @return    transformed 3d vector
         */
        private function projectTo2D(pos:Vector3D, rot:Vector3D):Vector3D {    
            var v2:Vector3D = new Vector3D(
                pos.x * Math.cos(rot.x) - pos.y * Math.sin(rot.x),
                pos.x * Math.sin(rot.x) + pos.y * Math.cos(rot.x),
                pos.z
            );
            var v1:Vector3D = new Vector3D(
                v2.x,
                v2.y * Math.cos(rot.y) - v2.z * Math.sin(rot.y)
            );
            var proj:Vector3D = new Vector3D(
                v1.x * Math.cos(rot.z) - v1.y * Math.sin(rot.z),
                v1.x * Math.sin(rot.z) + v1.y * Math.cos(rot.z),
                0,
                pos.w
            );
            return proj;
        }
        
        /**
         * Render loop
         * @param    e Event.Enter_FRAME
         */
        private function render(e:Event = null):void {
            // do nothing if data is void
            if (_points.length == 0)  return;
            
            // project
            _transformedPoints = new Vector.<Vector3D>();
            var i:uint;
            var v:Vector3D;
            for (i = 0; i < _points.length; i++) {
                v = projectTo2D(_points[i], new Vector3D(_ex, _ey, _ez));
                //v.scaleBy(_scale);
                v.x += _w / 2;
                v.y += _h / 2;
                _transformedPoints.push(v);
            }
            
            // rotate
            _ex += .01;
            _ey += .01;
            _ez += .01;
            
            _ex = wrap2Pi(_ex);
            _ey = wrap2Pi(_ey);
            _ez = wrap2Pi(_ez);
            
            // draw
            rasterize(_transformedPoints, true);
        }
        
        /**
         * Builds a pointcloud specified by formula relations
         * @param    formula y = f(x, z)
         * @param    zoneSize a grid domian size
         * @return
         */
        private function buildPointCloud(formula:Function, zoneSize:Number = 100, regularGrid:Boolean = true):Vector.<Vector3D> {
            var points:Vector.<Vector3D> = new Vector.<Vector3D>();
            var x:Number = - zoneSize;
            var z:Number = - zoneSize;
            var y:Number;
            var step:Number = zoneSize / 30;
            
            if (regularGrid) {
                while (z < zoneSize) {
                    x = - zoneSize;
                    while (x < zoneSize) {
                        y = formula(x, z);
                        x += step;
                        // store
                        points.push(new Vector3D(x, y, z, 0xffffff * (y/zoneSize)));
                    }
                    z += step;
                }
            } else {
                var i:uint;
                for (i = 0; i < zoneSize * zoneSize * .5; i++) {
                    x = zoneSize * (1 - 2 * Math.random());
                    z = zoneSize * (1 - 2 * Math.random());
                    y = formula(x, z);
                    // store
                    points.push(new Vector3D(x, y, z, 0xffffff * (y/zoneSize)));
                }
            }
            
            return points;
        }
        
        /**
         * A relation between x,y,z: y = f(x, z)
         * @param    x
         * @param    z
         * @return    y
         */
        private function formulaY1(x:Number, z:Number):Number {
            return 30 * z / x;
        }
        
        private function formulaY2(x:Number, z:Number):Number {
            var arg:Number = .0015 * x * x + .0015 * z * z;
            return 50 * Math.cos(arg) * 1 / (arg + 1);
        }
        
        private function formulaY3(x:Number, z:Number):Number {
            return sign(1 - 2 * Math.random()) * 100 * Math.sqrt(1 - .0001 * x * x - .0001 * z * z);
        }
        
        private function formulaY4(x:Number, z:Number):Number {
            return 10 * (Math.sin(.1 * x) + Math.cos(.1 * z));
        }
        
        private function formulaY5(x:Number, z:Number):Number {
            return 100 * (1 - 2 * Math.random());
        }
        
        private function formulaY6(x:Number, z:Number):Number {
            return sign(1 - 2 * Math.random()) * 100 * Math.sqrt(1 - .0001 * z * z);
        }
        
        private function formulaY7(x:Number, z:Number):Number {
            return sign(1 - 2 * Math.random()) * 100 * Math.sqrt(.0001 * x * x + .0001 * z * z);
        }
        
        /**
         * Draw a set of points to canvas
         * @param    points Pointcloud
         * @param fade Allow fade effect instead of direct cleaning of screen
         */
        private function rasterize(points:Vector.<Vector3D>, fade:Boolean = false):void {
            // clear all
            if (fade) {
                _canvas.draw(_fadeScreen);
            } else {
                _canvas.copyPixels(_clearScreen, _clearScreen.rect, new Point());
            }
            
            // draw pointcloud
            var p:Vector3D;
            _canvas.lock();
            for each (p in points) {
                _canvas.setPixel(numberToPixelCoord(p.x), numberToPixelCoord(p.y), uint(p.w));
            }
            _canvas.unlock();
        }
        
        private function numberToPixelCoord(x:Number):int {
            var xi:int = int(x);
            return xi;
        }
        
        private function resize(e:Event = null):void {
            var bmp:Bitmap = getChildAt(0) as Bitmap;
            bmp.scaleX = bmp.scaleY = stage.stageHeight / _h;
            bmp.x = (stage.stageWidth - _w * bmp.scaleX) / 2;
            _caption.width = stage.stageWidth;
        }
        
        /**
         * Get a sign (positive or negative) of a numeric value
         * @param    x
         * @return
         */
        private function sign(x:Number):int {
            return (x >= 0) ? 1 : -1;
        }
        
        /**
         * If x > 2*Pi : x = 0
         * @param    x
         * @return
         */
        private function wrap2Pi(x:Number):Number {
            if (x > 2 * Math.PI) x = 0;
            return x;
        }
        
        /**
         * Create a clickable rectangle with a handler function
         * @param    px
         * @param    py
         * @param    parent
         * @param    handler
         */
        private function addButton(px:Number, py:Number, parent:Sprite, id:String, handler:Function):void {
            var bd:BitmapData = new BitmapData(_buttonDim, _buttonDim, false, 0xffffff * Math.random());
            var btn:Bitmap = new Bitmap(bd);
            var s:Sprite = new Sprite();
            s.addChild(btn);
            s.addEventListener(MouseEvent.CLICK, handler);
            parent.addChild(s);
            s.name = id;
            s.x = px;
            s.y = py;
        }
        
        private function buttonClickHandler(e:Event):void {
            var id:int = int((e.currentTarget as Sprite).name);
            _currentFunction = _functions[id][0];
            _caption.text = _functions[id][1];
            initDataPoints();
        }

        
        /**
         * Add some controls
         */
        private function createButtons():void {
            var i:uint;
            var spacing:Number = (stage.stageHeight - _buttonDim * _functions.length) / (_functions.length - 1);
            for (i=0; i<_functions.length; i++) {
                addButton(0, i * (_buttonDim + spacing), this, String(i), buttonClickHandler);
            }
        }
        
        /**
         * Init formula changing time
         */
        private function initFormulaChanging():void {
            var tmr:Timer = new Timer(10000);
            tmr.addEventListener(TimerEvent.TIMER, onTimer);
            tmr.start();
        }
        
        private function onTimer(e:TimerEvent):void {
            _i++;
            _i = (_i == _functions.length) ? 0 : _i;
            _currentFunction = _functions[_i][0];
            _caption.text = _functions[_i][1];
            initDataPoints();
        }
    }
    
}