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: Draw worm by Webcam Motion Tracking (ジェスチャーでお絵描き)

Webcam Motion Tracking
http://blog.soulwire.co.uk/flash/actionscript-3/webcam-motion-detection-tracking/

LOVE MATRIX.
a study for drawing curl curve.
license under the GNU Lesser General Public License.
Get Adobe Flash player
by mapache 22 Aug 2010
// forked from TX_298's Draw worm by Webcam Motion Tracking (ジェスチャーでお絵描き)
// forked from nutsu's Draw worm by mouse gesture.
// forked from nutsu's Worm matrix based.
/**
Webcam Motion Tracking
http://blog.soulwire.co.uk/flash/actionscript-3/webcam-motion-detection-tracking/

LOVE MATRIX.
a study for drawing curl curve.
license under the GNU Lesser General Public License.
*/
package {
    import frocessing.display.F5MovieClip2D;
    import frocessing.geom.FMatrix2D;
    import frocessing.math.FMath;
    import frocessing.color.ColorHSV;
    import flash.geom.Matrix;
    import flash.display.*;
    import flash.filters.ColorMatrixFilter;
    import flash.media.Camera;
    import flash.media.Video;
    import flash.events.*;
    
    [SWF(width = "465", height = "465", frameRate = "30", backgroundColor = "0xffff")]
    public class WormMatrix extends Sprite {
        private var vms:Array;
        private var MAX_NUM:int = 100; 
        private var N:Number = 80;
        private var px:Number;
        private var py:Number;
        private var oldx:Number;
        private var oldy:Number;
        private var _motionTracker:MotionTracker;
        private var hsv:ColorHSV;
        private var _target:Shape;
        private var _bounds:Shape;
        private var _output:Bitmap;
        private var _source:Bitmap;
        private var _video:BitmapData;
        private var _view:BitmapData;
        private var _matrix:ColourMatrix;
        private var _mtx:Matrix;
        private var cv:Sprite = new Sprite();
        private var cam:Camera;
        public function WormMatrix () {
            //super();
            stage.frameRate = 60;
            vms = [];
            hsv = new ColorHSV(0, 1, 1, 1);
            var camW:int = stage.stageWidth;
            var camH:int = stage.stageHeight;
            _mtx = new Matrix();
            _mtx.translate( -camW, 0 ); _mtx.scale( -1, 1 );
            
            // Create the camera
            cam = Camera.getCamera();
            if (cam == null) return;

            cam.setMode( camW, camH, stage.frameRate );
            cam.addEventListener(ActivityEvent.ACTIVITY, activityHandler);
            // Create a video
            var vid:Video = new Video( camW, camH );
            vid.attachCamera( cam );

            // Create the Motion Tracker
            _motionTracker = new MotionTracker( vid );

            // We flip the input as we want a mirror image
            _motionTracker.flipInput = true;
        
            /*** Create a few things to help us visualise what the MotionTracker is doing... ***/
            _matrix = new ColourMatrix();
            
            _motionTracker.blur = 20;
            _motionTracker.brightness = _matrix.brightness = 20;
            _motionTracker.contrast = _matrix.contrast = 150;
            _motionTracker.minArea = 10;
                    
            // Display the camera input with the same filters (minus the blur) as the MotionTracker is using
            _video = new BitmapData( camW, camH, false, 0 );
            _view = _video.clone();
            _source = new Bitmap( _video );

            _source.filters = [ new ColorMatrixFilter( _matrix.getMatrix() ) ];
            addChild(new Bitmap(_view) );
            
            // Show the image the MotionTracker is processing and using to track
            _output = new Bitmap( _motionTracker.trackingImage );

            // A shape to represent the tracking point
            _target = new Shape();
            _target.graphics.beginFill(0xffffff, 0.5);
            _target.graphics.drawCircle( 0, 0, 7 );
            _target.graphics.endFill();
            addChild( _target );
            
            // A box to represent the activity area
            _bounds = new Shape();
            _bounds.x = _output.x;
            _bounds.y = _output.y;
            addChild( _bounds );
            addChild( cv );
            _target.x = px = oldx = camW / 2; _target.y = py = oldy = camH / 2;
        }
        public function activityHandler( event:ActivityEvent ):void {
                if( event.activating == true )addEventListener( Event.ENTER_FRAME, track );
        }
        private function track( e:Event ):void
        {
            _view.draw(_video,_mtx);
            cv.graphics.lineStyle();
            
            var len:int = vms.length;
            for( var i:int=0; i<len; i++ )
            {
                var o:WormObject = vms[i];
                if( o.count<N ){
                    drawWorm( o );
                    o.count++;
                }else {
                    len--;
                    vms.splice( i, 1 );
                    i--;
                }
            }

            // Tell the MotionTracker to update itself
            _motionTracker.track();
            
            // Move the target with some easing
            _target.x += ((_motionTracker.x + _bounds.x) - _target.x) / 10;
            _target.y += ((_motionTracker.y + _bounds.y) - _target.y) / 10;

            _video.draw( _motionTracker.input );
            if (_target.x == oldx && _target.y == oldy) { cv.graphics.clear(); return; }
            oldx = _target.x; oldy = _target.y;
            // If there is enough movement (see the MotionTracker's minArea property) then continue
            if ( !_motionTracker.hasMovement ) {   return; }
            
            // Draw the motion bounds so we can see what the MotionTracker is doing
            _bounds.graphics.clear();
            _bounds.graphics.lineStyle( 0, 0xFFFFFF,0.3 );
            _bounds.graphics.drawRect( _motionTracker.motionArea.x,
                                       _motionTracker.motionArea.y,
                                       _motionTracker.motionArea.width,
                                       _motionTracker.motionArea.height
                                        );
            check();
        }
        public function check():void
        {
            var x0:Number = _target.x;
            var y0:Number = _target.y;
            var vx:Number = x0 - px;
            var vy:Number = y0 - py;
            var len:Number = Math.min( FMath.mag( vx, vy ), 50 );

            if( len<10 ) return;
            hsv.h = Math.random() * 360;
            var mtx:FMatrix2D = new FMatrix2D();
            mtx.rotate( Math.atan2( vy, vx ) );
            mtx.translate( x0, y0 );
            
            createObj( mtx, len );
            cv.graphics.lineStyle(1,hsv.value);

            cv.graphics.moveTo(px, py);cv.graphics.lineTo(x0, y0 );
            px = x0;
            py = y0;
        }
        public function createObj( mtx:FMatrix2D, len:Number ):void
        {
            var angle:Number = FMath.random(Math.PI/64,Math.PI/6);
            if( Math.random()>0.5 ) angle *= -1;
            var tmt:FMatrix2D = new FMatrix2D();
            tmt.scale( 0.95, 0.95 );
            tmt.rotate( angle );
            tmt.translate( len, 0 );
            var w:Number = 0.5;
               
            var obj:WormObject = new WormObject(hsv.value);
            obj.c1x = obj.p1x = -w * mtx.c + mtx.tx;
            obj.c1y = obj.p1y = -w * mtx.d + mtx.ty;
            obj.c2x = obj.p2x =  w * mtx.c + mtx.tx;
            obj.c2y = obj.p2y =  w * mtx.d + mtx.ty;
            obj.vmt = mtx;
            obj.tmt = tmt;
            obj.r   = angle;
            obj.w   = len/20;
            obj.count = 0;
                
            vms.push( obj );
            if( vms.length > MAX_NUM )
                vms.shift();
        }
        
        public function drawWorm( obj:WormObject ):void
        {
            if( Math.random()>0.9 ){
                obj.tmt.rotate( -obj.r*2 );
                obj.r *= -1;
            }
            obj.vmt.prepend( obj.tmt );
            var cc1x:Number = -obj.w*obj.vmt.c + obj.vmt.tx;
            var cc1y:Number = -obj.w*obj.vmt.d + obj.vmt.ty;
            var pp1x:Number = (obj.c1x+cc1x)/2;
            var pp1y:Number = (obj.c1y+cc1y)/2;
            var cc2x:Number = obj.w*obj.vmt.c + obj.vmt.tx;
            var cc2y:Number = obj.w*obj.vmt.d + obj.vmt.ty;
            var pp2x:Number = (obj.c2x+cc2x)/2;
            var pp2y:Number = (obj.c2y+cc2y)/2;
            cv.graphics.beginFill( obj.col );
            cv.graphics.moveTo( obj.p1x, obj.p1y );
            cv.graphics.curveTo( obj.c1x, obj.c1y, pp1x, pp1y );
            cv.graphics.lineTo( pp2x, pp2y );
            cv.graphics.curveTo( obj.c2x, obj.c2y, obj.p2x, obj.p2y );
            cv.graphics.endFill();
            obj.c1x = cc1x;
            obj.c1y = cc1y;
            obj.p1x = pp1x;
            obj.p1y = pp1y;
            obj.c2x = cc2x;
            obj.c2y = cc2y;
            obj.p2x = pp2x;
            obj.p2y = pp2y;
        }
    }
}

import frocessing.geom.FMatrix2D;
class WormObject{
    public var c1x:Number;
    public var c1y:Number;
    public var c2x:Number;
    public var c2y:Number;
    public var p1x:Number;
    public var p1y:Number;
    public var p2x:Number;
    public var p2y:Number;
    public var w:Number;
    public var r:Number;
    public var count:int;
    public var vmt:FMatrix2D;
    public var tmt:FMatrix2D;
    public var col:uint;
    public function WormObject(c:uint){col = c;}
}

class ColourMatrix 
{
    
    /*
    ========================================================
    | Private Variables                         | Data Type  
    ========================================================
    */
    
    protected const LUMINANCE_R:                Number = 0.212671;
    protected const LUMINANCE_G:                Number = 0.715160;
    protected const LUMINANCE_B:                Number = 0.072169;
    protected const IDENTITY:                    Array  = [1, 0, 0, 0, 0,
                                                          0, 1, 0, 0, 0,
                                                          0, 0, 1, 0, 0,
                                                            0, 0, 0, 1, 0];
                                          
    protected var _matrix:                        Array;
    protected var _hue:                            Number;
    protected var _saturation:                    Number;
    protected var _brightness:                    Number;
    protected var _contrast:                    Number;
    protected var _alpha:                        Number;
    
    /*
    ========================================================
    | Constructor
    ========================================================
    */
    
    public function ColourMatrix( matrix:Array = null ) 
    {
        init(matrix == null ? IDENTITY.concat() : matrix.concat());
    }
    
    /*
    ========================================================
    | Private Methods
    ========================================================
    */
    
    protected function init( matrix:Array ):void
    {
        _matrix = matrix;
        setDefaultValues();
    }

    protected function setDefaultValues():void
    {
        _alpha      = 100;
        _brightness = 0;
        _contrast   = 0;
        _hue        = 0;
        _saturation = 0;
    }

    protected function multiply(matrix:Array):void
    {
        var aBuffer:Array = new Array();
        var n:int = 0;
        
        for(var i:int = 0; i < 4; i++)
        {
            for(var j:int = 0; j < 5; j++)
            {
                aBuffer[n + j] = matrix[n]        * _matrix[j]      + 
                                 matrix[n + 1] * _matrix[j + 5]  + 
                                 matrix[n + 2] * _matrix[j + 10] + 
                                 matrix[n + 3] * _matrix[j + 15] + 
                                 (j == 4 ? matrix[n + 4] : 0);
            }
            n += 5;
        }
        _matrix = aBuffer.concat();
    }
    
    /*
    ========================================================
    | Public Methods
    ========================================================
    */
    
    public function getMatrix():Array
    {
        return _matrix.concat();
    }
    public function clone():ColourMatrix
    {
        return new ColourMatrix( getMatrix() );
    }
    
    public function reset():void
    {
        init( IDENTITY.concat() );
    }
    
    /*
    ========================================================
    | Getters + Setters
    ========================================================
    */
    
    /* ALPHA */
    
    public function get alpha():Number { return _alpha; }
    
    /**
     * Sets the alpha.
     * @param    alpha    A value between 0 and 100 (0 being 0% alpha, 100 being 100% alpha).
     */
    
    public function set alpha( a:Number ):void
    {
        var old:Number = _alpha / 100;
        
        _alpha = a;
        a /= 100;
        a /= old;
        
        var matrix:Array = [1, 0, 0, 0, 0,
                            0, 1, 0, 0, 0,
                            0, 0, 1, 0, 0,
                            0, 0, 0, a, 0];
        
        multiply(matrix);
    }
    
    /* BRIGHTNESS */
    
    public function get brightness():Number { return _brightness; }
    
    /**
     * Sets the brightness.
     * @param    brightness        A value between -100 and 100 (0 being 'no changes').
     */
    
    public function set brightness( b:Number ):void
    {
        var old:Number = _brightness * (255 / 100);
        
        _brightness = b;
        b *= (255 / 100);
        b -= old;
        
        var matrix:Array = [1, 0, 0, 0, b,
                            0, 1, 0, 0, b,
                            0, 0, 1, 0, b,
                            0, 0, 0, 1, 0];
        multiply(matrix);
    }
    
    /* CONTRAST */
    
    public function get contrast():Number { return _contrast; }
    
    /**
     * Sets the contrast.
     * @param    contrast    A value between -100 and 100 (0 being 'no changes').
     */
    
    public function set contrast( c:Number ):void
    {
        var old:Number = _contrast / 100 + 1;
        
        _contrast = c;
        c = c / 100 + 1;
        c /= old;
        
        var matrix:Array = [c, 0, 0, 0, 128 * (1 - c),
                            0, c, 0, 0, 128 * (1 - c),
                            0, 0, c, 0, 128 * (1 - c),
                            0, 0, 0, 1, 0 ];
        multiply(matrix);
    }
    
    /* HUE */
    
    public function get hue():Number { return _hue; }
    
    /**
     * Sets the hue.
     * @param    angle    A value between -180 and 180 (0 being 'no changes').
     */
    
    public function set hue( a:Number ):void
    {
        var old:Number = _hue * (Math.PI / 180);
        
        _hue = a;
        a *= Math.PI / 180;
        a -= old;
        
        var c:Number = Math.cos(a);
        var s:Number = Math.sin(a);
        var matrix:Array = [(LUMINANCE_R + (c * (1 - LUMINANCE_R))) + (s * (-LUMINANCE_R)),       (LUMINANCE_G + (c * (-LUMINANCE_G)))    + (s * (-LUMINANCE_G)), (LUMINANCE_B + (c * (-LUMINANCE_B)))    + (s * (1 - LUMINANCE_B)), 0, 0,
                            (LUMINANCE_R + (c * (-LUMINANCE_R)))    + (s * 0.143),                (LUMINANCE_G + (c * (1 - LUMINANCE_G))) + (s * 0.14),           (LUMINANCE_B + (c * (-LUMINANCE_B)))    + (s * -0.283),            0, 0,
                            (LUMINANCE_R + (c * (-LUMINANCE_R)))    + (s * (-(1 - LUMINANCE_R))), (LUMINANCE_G + (c * (-LUMINANCE_G)))    + (s * LUMINANCE_G),    (LUMINANCE_B + (c * (1 - LUMINANCE_B))) + (s * LUMINANCE_B),       0, 0,
                            0,                                                                    0,                                                              0,                                                                 1, 0];
        multiply(matrix);
    }
    
    /* SATURATION */
    
    public function get saturation():Number { return _saturation; }
    
    /**
     * Sets the saturation.
     * @param    saturation        A value between -100 and 100 (0 being 'no changes').
     */
    
    public function set saturation( s:Number ):void
    {
        var old:Number = (_saturation + 100) / 100;
        
        _saturation = Math.min(100, Math.max(-99.99, s));
        s = (_saturation + 100) / 100;
        s /= old;
        
        var r:Number  = (1 - s) * LUMINANCE_R;
        var g:Number  = (1 - s) * LUMINANCE_G;
        var b:Number  = (1 - s) * LUMINANCE_B;
        var matrix:Array = [r + s, g, b, 0, 0, 
                            r, g + s, b, 0, 0,
                            r, g, b + s, 0, 0,
                            0, 0, 0, 1, 0];
    
        multiply(matrix);
    }
}
    
import flash.display.BitmapData;
import flash.display.BlendMode;
import flash.filters.BlurFilter;
import flash.filters.ColorMatrixFilter;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.media.Video;

class MotionTracker extends Point
{
    
    /*
    ========================================================
    | Private Variables                         | Data Type  
    ========================================================
    */
    
    private static const DEFAULT_AREA:            int = 10;
    private static const DEFAULT_BLUR:            int = 20;
    private static const DEFAULT_BRIGHTNESS:    int = 20;
    private static const DEFAULT_CONTRAST:        int = 150;
    
    private var _src:                            Video;
    private var _now:                            BitmapData;
    private var _old:                            BitmapData;
    
    private var _blr:                            BlurFilter;
    private var _cmx:                            ColourMatrix;
    private var _col:                            ColorMatrixFilter;
    private var _box:                            Rectangle;
    private var _act:                            Boolean;
    private var _mtx:                            Matrix;
    private var _min:                            Number;
    
    /*
    ========================================================
    | Constructor
    ========================================================
    */
    
    /**
     * The MotionTracker class will track the movement within video data
     * 
     * @param    source    A video object which will be used to track motion
     */
    
    public function MotionTracker( source:Video ) 
    {
        super();
        
        input = source;
        
        _cmx = new ColourMatrix();
        _blr = new BlurFilter();
        
        blur = DEFAULT_BLUR;
        minArea = DEFAULT_AREA;
        contrast = DEFAULT_CONTRAST;
        brightness = DEFAULT_BRIGHTNESS;
    }
    
    /*
    ========================================================
    | Public Methods
    ========================================================
    */
    
    /**
     * Track movement within the source Video object.
     */
    
    public function track():void
    {
        _now.draw( _src, _mtx );
        _now.draw( _old, null, null, BlendMode.DIFFERENCE );
        
        _now.applyFilter( _now, _now.rect, new Point(), _col );
        _now.applyFilter( _now, _now.rect, new Point(), _blr );
        
        _now.threshold( _now, _now.rect, new Point(), '>', 0xFF333333, 0xFFFFFFFF );
        _old.draw( _src, _mtx );
        
        var area:Rectangle = _now.getColorBoundsRect( 0xFFFFFFFF, 0xFFFFFFFF, true );
        _act = ( area.width >( _src.width / 100) * _min || area.height > (_src.height / 100) * _min );
        
        if ( _act )
        {
            _box = area;
            x = _box.x + (_box.width / 2);
            y = _box.y + (_box.width / 2);
        }
    }
    
    /*
    ========================================================
    | Getters + Setters
    ========================================================
    */
    
    /**
     * The image the MotionTracker is working from
     */
    public function get trackingImage():BitmapData { return _now; }
    /**
     * The area of the image the MotionTracker is working from
     */
    public function get trackingArea():Rectangle { return new Rectangle( _src.x, _src.y, _src.width, _src.height ); }
    /**
     * Whether or not movement is currently being detected
     */
    public function get hasMovement():Boolean { return _act; }
    /**
     * The area in which movement is being detected
     */
    public function get motionArea():Rectangle { return _box; }
    
    /* INPUT */
    
    /**
     * The video (usualy created from a Camera) used to track motion
     */
    public function get input():Video { return _src; }
    public function set input( v:Video ):void
    {
        _src = v;
        if ( _now != null ) { _now.dispose(); _old.dispose(); }
        _now = new BitmapData( v.width, v.height, false, 0 );
        _old = new BitmapData( v.width, v.height, false, 0 );
    }
    
    /* BLUR */
    
    /**
     * the blur being applied to the input in order to improve accuracy
     */
    public function get blur():int { return _blr.blurX; }
    public function set blur( n:int ):void { _blr.blurX = _blr.blurY = n; }
    
    /* BRIGHTNESS */
    
    /**
     * The brightness filter being applied to the input
     */
    public function get brightness():int { return _cmx.brightness; }
    public function set brightness( n:int ):void
    {
        _cmx.brightness = n;
        _col = new ColorMatrixFilter( _cmx.getMatrix() );
    }
    
    /* CONTRAST */
    
    /**
     * The contrast filter being applied to the input
     */
    public function get contrast():int { return _cmx.contrast; }
    public function set contrast( n:int ):void
    {
        _cmx.contrast = n;
        _col = new ColorMatrixFilter( _cmx.getMatrix() );
    }
    
    /* MIN AREA */
    
    
    /**
     * The minimum area (percent of the input dimensions) of movement to be considered movement
     */
    public function get minArea():int { return _min; }
    public function set minArea( n:int ):void
    {
        if ( n < 0 ) return;
        _min = n;
    }
    
    /* FLIP INPUT */
    
    /**
     * Whether or not to flip the input for mirroring
     */
    public function get flipInput():Boolean { return _mtx.a < 1; }
    public function set flipInput( b:Boolean ):void
    {
        _mtx = new Matrix();
        if (b) { _mtx.translate( -_src.width, 0 ); _mtx.scale( -1, 1 ); }
    }
    
}