DAN ZEN EXPO - CODE EXHIBIT - OSTRICH


package samples {
	
	import flash.display.MovieClip;
	import flash.events.*;
	import flash.utils.Timer;
	
	import com.danzen.interfaces.ostrich.*;
	
	// class to make a custom cursor follow the motion in a Web cam
	// just do not add the OstrichCursor object to the stage but follow it
	// use an ENTER_FRAME event to animate your own cursor - like the fairy...
		
	public class Fairy extends MovieClip {
		
		private var myCamera:OstrichCamera;
		private var myCursor:OstrichCursor;

		
		public function Fairy() {			
			
			trace ("hi from Fairy");
			
			// make an OstrichCamera object			
			myCamera = new OstrichCamera();	
			addChild(myCamera);	
			myCamera.alpha = .2;
			
			setChildIndex(myFairy, numChildren-1);
			
			// make an OstrichCursor object (there are more parameters available)
			myCursor = new OstrichCursor(myCamera); 			
			// uncomment this to see the cursor
			// addChild(myCursor);	
			
			addEventListener(Event.ENTER_FRAME, animate);
			
		}						
		
		private function animate(e:Event) {
			// make the fairy flip its direction
			if (myFairy.x < myCursor.x) {
				myFairy.scaleX = -.7;
			} else {
				myFairy.scaleX = .7;
			}
			// make the fairy follow the OstrichCursor object
			myFairy.x = myCursor.x;
			myFairy.y = myCursor.y;
		}
					
	}
	
}

package com.danzen.interfaces.ostrich{

	// OSTRICH INTRODUCTION  
	// Ostrich lets you follow video motion with a cursor
	// for instance, you can wave at a Webcam and make a cursor move to where you wave
	// this can be used as an interface to control elements of your application

	// FEATURES AND CONSIDERATIONS
	// you can specify a region in which to look for motion
	// you can specify multiple cursors - each with their own region
	// the OstrichButton class is provided to trigger over, out and hold events (no click)
	// the fastest way to find out if a button is activated is to make it a cursor region
	// then use the cursor start and stop to catch activity in that region
	// you can make your own clips follow the OstrichCursor

	// WORKINGS
	// OstrichCursor is optimized for a single person standing in the middle using hands
	// a rectangle is put around all motion and then a cursor is positioned as follows:
	// the y position of the cursor is set to ten pixels beneath the top of the motion rectangle
	// if rectangle is at the left of the camera it takes the left edge of rectangle for cursor x position
	// if rectangle is at right it takes the right edge of rectangle for cursor x position
	// if rectangle is in the center it takes the center of the rectangle for cursor x position
	// if rectangle is anywhere else it uses the proportion to figure cursor x location
	// you can adjust this by reworking the class

	// http://ostrichflash.wordpress.com - by inventor Dan Zen - http://www.danzen.com
	// if you are using Ostrich for commercial purposes, you are welcome to donate to Dan Zen
	// donations can be made to agency@danzen.com at http://www.paypal.com
	// also be aware that Gesture Tek and perhaps others hold patents in these areas

	// INSTALLING CLASSES  
	// suggested installation:
	// create a "classes" folder on your hard drive - for example c:\classes
	// add the classes folder to your Flash class path:
	// Flash menu choose Edit > Preferences > ActionScript - ActionScript 3 Settings 
	// then use the + sign for the source path box to add the path to your classes folder
	// put the provided com/danzen/ directory with its folders and files in the classes folder
	// the readme has more information if you need it

	// USING OSTRICH  
	// please make sure that the following director is in a folder in your class path:
	// com/danzen/interfaces/ostrich/
	// see the samples for how to use the Ostrich classes
	
	// OstrichCamera - sets up a Web cam for capturing motion
	// OstrichCursor - sets up a cursor that follows the motion in OstrichCamera
	// OstrichButton - sets up events for an invisible hotspot per OstrichCursor
	// OstrichBlobs - puts blobs on any motion
	
	// ******************
	// This class should only be called after an OstrichCamera.READY event fires
	


	import flash.display.Sprite;
	import flash.filters.ColorMatrixFilter;
	import flash.filters.BlurFilter;
	import flash.media.Camera;
	import flash.media.Video;
	import flash.utils.Timer;
	import flash.events.*;
	public class OstrichCamera extends Sprite {
		
	// CONSTRUCTOR  
	// OstrichCamera(theL:Number=0, theT:Number=0, theR:Number=640, theB:Number=480, theFlip:Boolean=true):void
	// this class captures the camera and stores it in a cam Video object
	// it flips the camera so you can match your motion like a mirror
	// you can use this class without adding it to the stage with addChild()
	// or you can add it and set the alpha or the visible as desired
	//  just use once and then all OstrichCursor objects can use it to capture motion
	//
	// PARAMETERS:
	// theL:Number - the left side x of the camera
	// theT:Number - the top side y of the camera
	// theR:Number - the right side x of the camera
	// theB:Number - the bottom side y of the camera
	// theFlip:Boolean - do you want to flip the camera

	// EVENTS 
	// OstrichCamera.READY - the camera is on and ready for motion capture 
	// OstrichCamera.NO_CAMERA - called if there is no camera to start
	
	// METHODS (in addition to constructor)
	// dispose():void
	// stops camera - then can remake with different size if desired

	// PROPERTIES  
	// camNum:Number - the cam num starting at 0 
	// camCheck:Boolean - a Boolean used as a safeguard by other Ostrich classes
	// left, right, top and bottom:Number - read only - what was sent in as object was created
	// but it extends a sprite so there is alpha, visible, etc.
	// flip:Boolean - read only

	// CONSTANTS  
	// READY:String - static constant (OstrichCamera.READY) for camera ready event
	// NO_CAMERA:String - static constant (OstrichCamera.NO_CAMERA) for no camera at start
		
		
		public static const READY:String="ready";
		public static const NO_CAMERA:String="noCamera";
		public var cam:Video;// the cam instance
		public var signal:Camera;// the camera signal
		// there are more public properties (getter/setter) down below
		
		// static constants and related
		private static var camTotal:Number=0;// keeps track of cursor numbers starting at 0
		private var myCamNum:Number;// used with getter method at botton of class to return cursorNum
		internal var myFlip:Boolean;
		
		internal var cm:ColorMatrixFilter;// a color matrix
		internal var bf:BlurFilter;// a blur filter
		internal var camCheck:Boolean=false;// use the OstrichCursor.READY event instead!

		private var timerCheckStart:Timer;// timers to check the availability of the camera
		private var timerCheckStart2:Timer;
		private var myTimer:Timer; // delay for camera list check

		public function OstrichCamera(theL:Number=0, theT:Number=0, theR:Number=640, theB:Number=480, theFlip:Boolean=true) {
				
			if (camTotal==0) {
				trace("hi from OstrichCamera");
			}
			
			myCamNum=camTotal++; // which means camNum starts at 0
			cam = new Video(theR-theL, theB-theT);
						
			cam.x=theL;
			cam.y=theT;
			myFlip = theFlip;
					
			myTimer = new Timer(200, 1);
			myTimer.addEventListener(TimerEvent.TIMER, init);
			myTimer.start();						
		}
		
		private function init(e:TimerEvent) {

			if (Camera.names.length == 0) {
				dispatchEvent(new Event(OstrichCamera.NO_CAMERA));				
				return;
			}
			var macCamera:Number = -1;
			for (var i:uint=0; i<Camera.names.length; i++) {				
				if (Camera.getCamera(String(i)).name == "USB Video Class Video") {
					macCamera = i; 
					break;
				}
			}
			if (macCamera >= 0) {
				signal = Camera.getCamera(String(macCamera));
			} else {
				signal = Camera.getCamera();
			}
			signal.setMode(cam.width, cam.height, 30);
			cam.attachCamera(signal);
			addChild(cam);
							
			if (myFlip) {
				// flip the cam instance around to get a mirror effect
				// need to also accomodate for this in cursor class
				cam.scaleX=-1;
				cam.x+=cam.width;
			}
			
			// need to find out when camera is active and set small delay to avoid motion trigger at start
			// can't use status because it does not trigger when camera is automatically accepted
			// set a check every 200 ms to see if camera is accepted
			// once it is accepted, set a delay of 1000 ms until we start checking for motion with camCheck
			timerCheckStart=new Timer(200);
			timerCheckStart.addEventListener(TimerEvent.TIMER, startStopEvents);
			timerCheckStart.start();
			timerCheckStart2=new Timer(1000,1);
			timerCheckStart2.addEventListener(TimerEvent.TIMER, startStopEvents2);
			function startStopEvents(e:TimerEvent) {
				if (! signal.muted) {
					timerCheckStart.stop();
					timerCheckStart2.start();
				}
			}

			function startStopEvents2(e:TimerEvent) {
				camCheck=true;
				dispatchEvent(new Event(OstrichCamera.READY, true));
			}

			// set up some filters for better motion detection
			// we will apply these in the cursor classes

			// first we set up a color matrix filter to up the contrast of the image
			// to do this we boost each color channel then reduce overall brightness
			// we create a color matrix that will boost each color (multiplication)
			// and then drop the brightness of the channel (addition)
			var boost:Number=4;//3
			var brightness:Number=-50;//-60;
			var cmArray:Array = [
			boost,0,0,0,brightness,
			0,boost,0,0,brightness,
			0,0,boost,0,brightness,
			0,0,0,1,0
			];

			// create a new colorMatrixFilter so that we can apply our color matrix
			cm=new ColorMatrixFilter(cmArray);

			// set up a blur filter to help emphasize areas of change
			bf=new BlurFilter(16,16,2);

		}

		// these getter setter methods prevent the camNum from being set
		public function get camNum() {
			return myCamNum;
		}
		public function set camNum(t) {
			trace("camNum is read only");
		}

		// these getter setter methods prevent the dimensions from being set
		public function get left() {
			return cam.x;
		}
		public function set left(t) {
			trace("left is read only - dispose() and recreate class if changes are needed");
		}
		public function get right() {
			return cam.x + cam.width;
		}
		public function set right(t) {
			trace("right is read only - dispose() and recreate class if changes are needed");
		}
		public function get top() {
			return cam.y;
		}
		public function set top(t) {
			trace("top is read only - dispose() and recreate class if changes are needed");
		}
		public function get bottom() {
			return cam.y+cam.height;
		}
		public function set bottom(t) {
			trace("bottom is read only - dispose() and recreate class if changes are needed");
		}
		public function get flip() {
			return myFlip;
		}
		public function set flip(t) {
			trace("flip is read only - dispose() and recreate class if changes are needed");
		}

		public function dispose() {
			if (timerCheckStart) {
				timerCheckStart.stop();
			}
			if (timerCheckStart2) {
				timerCheckStart2.stop();
			}
			removeChild(cam);
		}
	}
}

package com.danzen.interfaces.ostrich {

	// OSTRICH INTRODUCTION  
	// Ostrich lets you follow video motion with a cursor
	// for instance, you can wave at a Webcam and make a cursor move to where you wave
	// this can be used as an interface to control elements of your application
	
	// FEATURES AND CONSIDERATIONS
	// you can specify a region in which to look for motion
	// you can specify multiple cursors - each with their own region
	// the OstrichButton class is provided to trigger over, out and hold events (no click)
	// the fastest way to find out if a button is activated is to make it a cursor region
	// then use the cursor start and stop to catch activity in that region
	// you can make your own clips follow the OstrichCursor

	// WORKINGS
	// OstrichCursor is optimized for a single person standing in the middle using hands
	// a rectangle is put around all motion and then a cursor is positioned as follows:
	// the y position of the cursor is set to ten pixels beneath the top of the motion rectangle
	// if rectangle is at the left of the camera it takes the left edge of rectangle for cursor x position
	// if rectangle is at right it takes the right edge of rectangle for cursor x position
	// if rectangle is in the center it takes the center of the rectangle for cursor x position
	// if rectangle is anywhere else it uses the proportion to figure cursor x location
	// you can adjust this by reworking the class
	
	// http://ostrichflash.wordpress.com - by inventor Dan Zen - http://www.danzen.com
	// if you are using Ostrich for commercial purposes, you are welcome to donate to Dan Zen
	// donations can be made to agency@danzen.com at http://www.paypal.com
	// also be aware that Gesture Tek and perhaps others hold patents in these areas

	// INSTALLING CLASSES  
	// suggested installation:
	// create a "classes" folder on your hard drive - for example c:\classes
	// add the classes folder to your Flash class path:
	// Flash menu choose Edit > Preferences > ActionScript - ActionScript 3 Settings 
	// then use the + sign for the source path box to add the path to your classes folder
	// put the provided com/danzen/ directory with its folders and files in the classes folder
	// the readme has more information if you need it

	// USING OSTRICH  
	// please make sure that the following director is in a folder in your class path:
	// com/danzen/interfaces/ostrich/
	// see the samples for how to use the Ostrich classes
	
	// OstrichCamera - sets up a Web cam for capturing motion
	// OstrichCursor - sets up a cursor that follows the motion in OstrichCamera
	// OstrichButton - sets up events for an invisible hotspot per OstrichCursor
	// OstrichBlobs - puts blobs on any motion
	
	// ******************
	// This class should only be called after an OstrichCamera.READY event fires

	
	
	import flash.display.BitmapData;
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.geom.*;
	import flash.filters.ColorMatrixFilter;
	import flash.filters.BlurFilter;
	import fl.transitions.Tween;
	import fl.transitions.easing.None;	
	import flash.media.Camera;
	import flash.media.Video;
	import flash.utils.Timer;
	import flash.events.*
	
	public class OstrichCursor extends Sprite {
		
		// CONSTRUCTOR  
		// OstrichCursor(theCam:OstrichCamera, theL:Number=0, theT:Number=0, theR:Number=0, theB:Number=0, theResponse:Number=4):void
		// 		OstrichCursor makes a cursor follow motion in a specified area of an OstrichCamera
	    // 		you can then hide the cursor and make another shape follow it (custom cursor)
		// 		and / or you can use the cursor to rollover an OstrichButton and hold to activate
		// 		or you can use the cursor just as any other moving sprite to make a game controller, etc.
		//		
		// 		PARAMETERS:
		//		theCam:OstrichCamera - the cam used for motion detection
		// 		theL:Number - the left side x of the region (with respect to the OstrichCam left)
		// 		theT:Number - the top side y of the region (with respect to the OstrichCam top)
		// 		theR:Number - the right side x of the region (with respect to the OstrichCam left)
		// 		theB:Number - the bottom side y of the region (with respect to the OstrichCam top)
		// 		theResponse:Number - from 1-10 default 4.  1 is fast but jumpy - 10 is slow and smooth

		// EVENTS 
		// OstrichCursor.MOTION_START 	motion has started for a cursor 
		// OstrichCursor.MOTION_STOP 	motion has stopped for a cursor 
		
		// METHODS (in addition to constructor)
		// dispose():void - stops and removes cursor

		// PROPERTIES  
		// cursorNum:Number - read only - the cursor num starting at 0 
		// cam:OstrichCamera - the cam feed passed to the OstrichCursor object
		// x:Number - the x position of the cursor - setting this will do you no good ;-)
		// y:Number - the y position of the cursor - setting this will do you no good ;-)
		// response:Number - between 1-10 - cursor is checked each followInterval
		//					 but reported every response number of times
		//					 movement between reports is averaged to smoothen the motion
		
		// CONSTANTS  
		// MOTION_START:String - static constant (OstrichCursor.MOTION_START) for motion start event
		// MOTION_STOP:String - static constant (OstrichCursor.MOTION_STOP) for motion stop event
		
				
		// event constants
		public static const MOTION_START:String = "MotionStart";
		public static const MOTION_STOP:String = "MotionStop";
		
		// static constants and related
		private static var cursorTotal:Number = 0; // keeps track of cursor numbers starting at 0
		private var myCursorNum:Number; // used with getter method at botton of class to return cursorNum
										
		// a few initial condition varibles
		private var cursorSize:Number = 14;	// a square is drawn for the cursor
		private var fromTop:Number = 10;	// pixels cursor is drawn from top of motion rectangle		
		private var followInterval:Number = 100; // motion checking interval in ms		
		
		// various holder variables and checks
		private var myCam:OstrichCamera; // the cam instance
		private var myResponse:Number;
		private var motionRectangle:Sprite; // a holder for the motion rectangle (hidden)
		private var motionCursor:Sprite; // a holder for the cursor clip		
		private var older:BitmapData; // the old frame of motion
		private var newer:BitmapData; // the new frame of motion		
		private var myMatrix:Matrix; // to handle flipping of the camera
		private var rect:Rectangle; // from getColorBoundsRect around motion color between old and new frames		
		private var motionCheck:Boolean = false; // true when motion over an interval based on followInterval * response		
		private var timerFollow:Timer; // interval for testing motion based on followInterval		
		private var timerMoveCursor:Timer; // interval for moving the cursor based on response * followInterval
		private var tweenObject:Object = new Object(); // store the tweens on an object so we can delete them		
		
		// these are variables used in the calculations		
		private var cursorSpeed:Number; // the interval the cursor moves based on followInterval * response in ms
		private var mL:Number; // m for motionRectangle
		private var mR:Number;
		private var mT:Number;
		private var mB:Number;	
		private var mW:Number;	
		private var mM:Number;	
		private var mX:Number;	
		private var regionL:Number; 
		private var regionR:Number;
		private var regionT:Number;
		private var regionB:Number;
		private var regionW:Number;
		private var regionH:Number;
		private var regionR1:Number;
		private var regionR2:Number;
		private var regionT1:Number;
		private var regionT2:Number;
		private var moveX:Number;
		private var moveY:Number;			
		private var motionL:Number = 0; // set some initial motion variables for the cursor
		private var motionR:Number = 0;
		private var motionT:Number = 0;
		private var motionB:Number = 0;
		private var motionLtotal:Number = 0;
		private var motionRtotal:Number = 0;	
		private var motionTtotal:Number = 0;	
		private var motionBtotal:Number = 0;
		private var motionTally:Number = 0;
		
		public function OstrichCursor(theCam:OstrichCamera, theL:Number=0, theT:Number=0, theR:Number=0, theB:Number=0, theResponse:Number=4) {	
			
			myCam = theCam;
						
			// set the region in which the cursor will work			
			regionL = theL; 
			regionT = theT;
			regionR = (theR != 0) ? theR : theL + myCam.width; 			
			regionB = (theB != 0) ? theB : theT + myCam.height; 
									
			myResponse = Math.max(theResponse, 1);
			myResponse = Math.min(theResponse, 10);
			
			if (theCam.camCheck) { // double check the camera is ready
				init();
			} else {
				trace ("--------------------------------");
				trace ("please call the OstrichCursor class");
				trace ("after using an OstrichCamera.READY event");
				trace ("--------------------------------");				
			}
		} 
		
		private function init() {
			
			if (cursorTotal == 0) {trace ("hi from OstrichCursor");}		
			
			myCursorNum = cursorTotal++; // which means cursorNum starts at 0			
			
			// create the sprite that will hold cursor
			motionCursor = new Sprite();
			addChild(motionCursor);
		
			// create a sprite that will hold the overall motion rectangle that the cursor follows
			motionRectangle = new Sprite();
			//addChild(motionRectangle);
						
			cursorSpeed = myResponse * followInterval;
						
			// draw a cursor with black outside and grey inside square	
			drawCursor(motionCursor);	
			
			// get the width and height of the region to draw
			regionW = regionR - regionL;  
			regionH = regionB - regionT;					
	
			// here we figure out translations required to capture our rectangle			
			if (myCam.myFlip) {								
				moveX = myCam.width - regionR;
				moveY = regionT;				
			} else {							
				moveX = regionL;
				moveY = regionT;				
			}
			
			// set up the matrix to capture our region later on
			myMatrix = new Matrix();					
			myMatrix.translate(-moveX, -moveY);
			
			// prepare two bitmap objects to store the previous and current video frames		
			older = new BitmapData(regionW, regionH, false);
			newer = new BitmapData(regionW, regionH, false);					
			
			// this interval runs the function follow every followInterval milliseconds
			// follow puts a rectangle around motion			
			timerFollow = new Timer(followInterval);
			timerFollow.addEventListener(TimerEvent.TIMER, follow);
			timerFollow.start();			
	
			// this interval runs moveCursor which puts the cursor on the top
			// of the motion rectangle and to the sides as applicable			
			timerMoveCursor = new Timer(cursorSpeed);
			timerMoveCursor.addEventListener(TimerEvent.TIMER, moveCursor);
			timerMoveCursor.start();			
			
		}	
				
		
		private function follow(c) {		
				
			// Generally, capture two frames across time and apply a difference filter
			// color will only show where there is movement - turn this to one color
			// draw a rectangle around the color to create a "motion rectangle"
			// Technique learned from Grant Skinner Talk at FITC 2006 & at Interaccess
		
			// We copy the picture from the old frame to a new bitmap
			newer.copyPixels(older,older.rect,new Point(0,0));	
						
			// We then draw what is currently on the camera over top of the old frame
			// As we are specifying using the difference filter, any pixels of the new
			// frame that have the same color as the old frame will have a difference of zero
			// zero means black and then every where that is not black will be some color
			newer.draw(myCam.cam,myMatrix,null,"difference"); 
						
			// Below we draw the unaffected camera feed to the old frame so that
			// when the follow function is called again, we have a copy of the old frame
			older.draw(myCam.cam,myMatrix,null);
			
			// We apply the contrast color filter from the OstrichCamera to focus in on our motion
			newer.applyFilter(newer,newer.rect,new Point(0,0),myCam.cm);
						
			// We apply the blur filter from the OstrichCamera to smoothen our motion region
			newer.applyFilter(newer,newer.rect,new Point(0,0),myCam.bf);
						
			// We apply a threshold to turn all colors above almost black (first number) 
			// to green (second number) the last number is a mask number (confusing)
			// this for some reason will not work unless we set the alpha channel up
			// even though we are not caring about alpha in our bitmap declaration
			// that is, the threshold would work but then the colorbounds would not
			newer.threshold(newer,newer.rect,new Point(0,0),">",0x00110000,0xFF00FF00,0x00FFFFFF);
			
			// Below we get a rectangle that encompasses the color (second number)
			// the first number is a mask (confusing because it deals with bitwise operators)
			// true means a rectangle around the color - false means a rectangle not around the color
			rect = newer.getColorBoundsRect(0x00FFFFFF,0xFF00FF00,true);
			
			// below we keep a running total of rectangle positions and a tally
			// this will allow us to average the rectangle to position the cursor			
			if (rect.width > 0) {
				if (myCam.myFlip) {
					motionL = Math.round(myCam.x + myCam.cam.x - rect.right);
					motionR = Math.round(myCam.x + myCam.cam.x - (rect.right - rect.width));
				} else {
					motionL = Math.round(myCam.x + myCam.cam.x + rect.left);
					motionR = Math.round(myCam.x + myCam.cam.x + rect.right);
				}
				motionT = Math.round(myCam.y + myCam.cam.y + rect.top);
				motionB = Math.round(myCam.y + myCam.cam.y + rect.bottom);		
					
				motionLtotal += motionL;
				motionRtotal += motionR;
				motionTtotal += motionT;	
				motionBtotal += motionB;	
				motionTally++;		
			}	
			
		}	
		

		private function moveCursor(c) {			
			
			// handle checking for any motion					
			if (motionTally > 0 && motionCheck == false && myCam.camCheck) {
				motionCheck = true;
				dispatchEvent(new Event(OstrichCursor.MOTION_START, true));
			} else if (motionTally == 0 && motionCheck && myCam.camCheck) {
				motionCheck = false;
				dispatchEvent(new Event(OstrichCursor.MOTION_STOP, true));	
			}			
			
			// averaging cursor motion		
			mL = motionLtotal / motionTally + moveX * myCam.cam.scaleX;
			mR = motionRtotal / motionTally + moveX * myCam.cam.scaleX;
			mT = motionTtotal / motionTally + moveY * myCam.cam.scaleY;
			mB = motionBtotal / motionTally + moveY * myCam.cam.scaleY;
									
			// draw the motion rectangle		
			drawRect(motionRectangle);
						
			// get a width and a middle used in the calculation that follows
			mW = mR - mL;
			if (myCam.myFlip) {
				mM = mL + mW / 2 - (myCam.x + myCam.cam.x - myCam.width);	
			} else {
				mM = mL + mW / 2 - (myCam.x + myCam.cam.x);	
			}
									
			// place cursor to left more as motion rectangle moves left
			// place cursor to right more as motion rectangle moves left
			// place cursor at the top of the motion rectangle
			mX = mL + mM / myCam.width * mW;		
			motionLtotal = motionRtotal = motionTtotal = motionBtotal = motionTally = 0;	
			
			if (mW > 0) {				
				// tween cursor to next position			
				delete(tweenObject.cursorTweenX);
				tweenObject.cursorTweenX = new Tween(motionCursor, "x", None.easeOut, motionCursor.x, mX, cursorSpeed/1000, true);
				delete(tweenObject.cursorTweenY);
				tweenObject.cursorTweenY = new Tween(motionCursor, "y", None.easeOut, motionCursor.y, mT + fromTop, cursorSpeed/1000, true);		
			}
		}		
		
		private function drawCursor(c) {
			c.graphics.moveTo(-cursorSize/2,-cursorSize/2); 
			c.graphics.lineStyle(1,0x000000);		
			c.graphics.lineTo(cursorSize/2,-cursorSize/2);
			c.graphics.lineTo(cursorSize/2, cursorSize/2);
			c.graphics.lineTo(-cursorSize/2, cursorSize/2);
			c.graphics.lineTo(-cursorSize/2,-cursorSize/2); 
			c.graphics.moveTo(-cursorSize/2+1,-cursorSize/2+1); 
			c.graphics.lineStyle(1,0xCCCCCC);
			c.graphics.lineTo(cursorSize/2-1, -cursorSize/2+1);
			c.graphics.lineTo(cursorSize/2-1, cursorSize/2-1);
			c.graphics.lineTo(-cursorSize/2+1, cursorSize/2-1); 
			c.graphics.lineTo(-cursorSize/2+1,-cursorSize/2+1);			
		}		
		
		private function drawRect(r) {
			// used to draw the overall motion rectangle that the cursor follows			
			r.graphics.clear();
			r.graphics.lineStyle(2,0xcc0000);
			r.graphics.moveTo(mL, mT);
			r.graphics.lineTo(mR, mT);
			r.graphics.lineTo(mR, mB);
			r.graphics.lineTo(mL, mB);
			r.graphics.lineTo(mL, mT);
		}		
		
		
		// when another class follows the x y positions of the OstrichCursor it		
		// really follows the x y position of the motionCursor sprite within OstrichCursor		
		protected var theX:Number;
		public override function get x():Number {
			theX = motionCursor.x;
			return this.theX;
		} 
		public override function set x(t:Number):void {
			motionCursor.x = t;
		} 
		
		protected var theY:Number;		
		public override function get y():Number {
			theY = motionCursor.y;
			return this.theY;			
		} 			
		public override function set y(t:Number):void {
			motionCursor.y = t;
		} 
		
		// these getter setter methods prevent the cursorNum from being set
		public function get cursorNum() {return myCursorNum;}
		public function set cursorNum(t) {trace ("cursorNum is read only");}
		
		// these getter setter methods prevent the cam from being set
		public function get cam() {return myCam;}
		public function set cam(t) {trace ("cam is read only");}		
		
		public function get response():Number {
			return myResponse;
		}
		
		public function set response(r:Number) {
			myResponse = Math.max(Math.min(10,r),1);
			cursorSpeed = myResponse * followInterval;	
			timerMoveCursor.delay = cursorSpeed;
		}
		
		public function dispose() {
			if (timerFollow) {timerFollow.stop();}
			if (timerMoveCursor) {timerMoveCursor.stop();}
			delete(tweenObject.cursorTweenX);
			delete(tweenObject.cursorTweenY);
			removeChild(motionCursor);
		}				
				
	}
	
}


package com.danzen.interfaces.ostrich {

	// OSTRICH INTRODUCTION  
	// Ostrich lets you follow video motion with a cursor
	// for instance, you can wave at a Webcam and make a cursor move to where you wave
	// this can be used as an interface to control elements of your application
	
	// FEATURES AND CONSIDERATIONS
	// you can specify a region in which to look for motion
	// you can specify multiple cursors - each with their own region
	// the OstrichButton class is provided to trigger over, out and hold events (no click)
	// the fastest way to find out if a button is activated is to make it a cursor region
	// then use the cursor start and stop to catch activity in that region
	// you can make your own clips follow the OstrichCursor
	
	// **** people on macs may need to adjust their active camera setting in Flash

	// WORKINGS
	// OstrichCursor is optimized for a single person standing in the middle using hands
	// a rectangle is put around all motion and then a cursor is positioned as follows:
	// the y position of the cursor is set to ten pixels beneath the top of the motion rectangle
	// if rectangle is at the left of the camera it takes the left edge of rectangle for cursor x position
	// if rectangle is at right it takes the right edge of rectangle for cursor x position
	// if rectangle is in the center it takes the center of the rectangle for cursor x position
	// if rectangle is anywhere else it uses the proportion to figure cursor x location
	// you can adjust this by reworking the class
	
	// http://ostrichflash.wordpress.com - by inventor Dan Zen - http://www.danzen.com
	// if you are using Ostrich for commercial purposes, you are welcome to donate to Dan Zen
	// donations can be made to agency@danzen.com at http://www.paypal.com
	// also be aware that Gesture Tek and perhaps others hold patents in these areas

	// INSTALLING CLASSES  
	// suggested installation:
	// create a "classes" folder on your hard drive - for example c:\classes
	// add the classes folder to your Flash class path:
	// Flash menu choose Edit > Preferences > ActionScript - ActionScript 3 Settings 
	// then use the + sign for the source path box to add the path to your classes folder
	// put the provided com/danzen/ directory with its folders and files in the classes folder
	// the readme has more information if you need it

	// USING OSTRICH  
	// please make sure that the following director is in a folder in your class path:
	// com/danzen/interfaces/ostrich/
	// see the samples for how to use the Ostrich classes
	
	// OstrichCamera - sets up a Web cam for capturing motion
	// OstrichCursor - sets up a cursor that follows the motion in OstrichCamera
	// OstrichButton - sets up events for an invisible hotspot per OstrichCursor
	// OstrichBlobs - puts blobs on any motion	
	
	// ******************
	// This class should only be called after an OstrichCamera.READY event fires	

	import flash.display.*
	import flash.utils.Timer;
	import flash.events.*

	public class OstrichButton extends Sprite {
		
		// CONSTRUCTOR  
		// OstrichCursor(theCursor:OstrichCursor, theButton:Object, theHoldDuration:Number=2):void
		// 		OstrichButton takes in an OstrichCursor and some premade "button" DisplayObject
		// 		It then checks to see if the cursor moves over or out of the button
		// 		and also if the user holds on the button for the time given by the third constructor parameter
		//		
		// 		PARAMETERS:
		// 		theCursor:OstrichCursor - the OstrichCursor object we are using
		// 		theButton:Object - a Sprite or MovieClip - the class just uses location and size
		// 		theHoldDuration:Number - seconds on button before a hold is triggered (default 2)

		// EVENTS 
		// OstrichButton.MOTION_OVER 	the cursor has moved over the button 
		// OstrichButton.MOTION_OUT 	the cursor has moved out from the button 
		// OstrichButton.MOTION_HOLD 	the cursor has stayed on the button for the specified myHoldTime
		
		// METHODS (in addition to constructor)
		// dispose():void - removes OstrichButton object - not your original button

		// PROPERTIES  
		// buttonNum:Number - read only - the button num starting at 0 
		// button:Object - reference to the "button" passed in to the OstrichButton object
		// cursor:OstrichCursor - the OstrichCursor passed in to the OstrichButton object
		
		// CONSTANTS  
		// MOTION_START:String - static constant (OstrichCursor.MOTION_START) for motion start event
		// MOTION_STOP:String - static constant (OstrichCursor.MOTION_STOP) for motion stop event
		
		
		public static const MOTION_OVER:String = "MotionOver";
		public static const MOTION_OUT:String = "MotionOut";		
		public static const MOTION_HOLD:String = "MotionHold";
		
		// static constants and related
		private static var buttonTotal:Number = 0; // keeps track of button numbers starting at 0
		private var myButtonNum:Number; // used with getter method at botton of class to return buttonNum
				
		// general holder and check variables
		private var myCursor:OstrichCursor;
		private var myButton:Object;	
		private var myHoldDuration:Number;
		private var motionOnButton:Object;
		private var motionOnButtonHold:Object;
		private var clearCheck:Boolean;
		private var clearCheckHold:Boolean = true;
		private var timerButton:Timer;
		private var timerHold:Timer;
		
		public function OstrichButton(theCursor:OstrichCursor, theButton:Object, theHoldDuration:Number=2) {
						
			if (buttonTotal == 0) {trace ("hi from OstrichButton");}						
						
			myButtonNum = buttonTotal++; // which means cursorNum starts at 0
	
			myCursor = theCursor;	
			myButton = theButton;
			myHoldDuration = theHoldDuration;
			
			timerButton = new Timer(500);
			timerButton.start();
			
			motionOver();
			motionOut();
			motionHold();
			
		}		
		
		// --------------  Over function ----------------------------
		
		private function motionOver() {		
			timerButton.addEventListener(TimerEvent.TIMER, overFunction);
			clearCheck = true;	
		}
		private function overFunction(e:Event) {
			if (myCursor.hitTestObject(DisplayObject(myButton))) {			
				if (clearCheck) {
					dispatchEvent(new Event(OstrichButton.MOTION_OVER, true));
					motionOnButton = myButton;
					clearCheck = false;
				} 		
			} else {
				clearCheck = true;
			}
		}		
		
		// --------------  Out function ----------------------------
		
		private function motionOut() {
			timerButton.addEventListener(TimerEvent.TIMER, outFunction);
			clearCheck = true;	
		}
		private function outFunction(e:Event) {
			if (!myCursor.hitTestObject(DisplayObject(myButton))) {
				if (motionOnButton == myButton) {
					dispatchEvent(new Event(OstrichButton.MOTION_OUT, true));
					motionOnButton = null;
				} 		
			} 
		}		
		
		// --------------  Hold function ---------------------------- 
		
		private function motionHold() {
			timerButton.addEventListener(TimerEvent.TIMER, holdFunction);
			clearCheck = true;				
		}
		private function holdFunction(e:Event) {
			if (myCursor.hitTestObject(DisplayObject(myButton))) {
				if (clearCheckHold) {					
					timerHold = new Timer(myHoldDuration*1000,1);
					timerHold.addEventListener(TimerEvent.TIMER, holdTime);
					timerHold.start();	
					motionOnButtonHold = myButton;
					clearCheckHold = false;
				} 		
			} else {
				clearCheckHold = true;
				if (motionOnButtonHold == myButton) {
					timerHold.stop();
					motionOnButtonHold = null;
				} 				
			}
		}		
		private function holdTime(e:Event) {
			motionOnButtonHold = null;
			dispatchEvent(new Event(OstrichButton.MOTION_HOLD, true));
		}			
		
		// these getter setter methods prevent the buttonNum from being set
		public function get buttonNum() {return myButtonNum;}
		public function set buttonNum(t) {trace ("buttonNum is read only");}		
		
		// these getter setter methods prevent the cursor from being set
		public function get cursor() {return myCursor;}
		public function set cursor(t) {trace ("cursor is read only");}		
		
		// these getter setter methods prevent the button from being set
		public function get button() {return myButton;}
		public function set button(t) {trace ("button is read only");}				
		
		public function dispose() {
			if (timerButton) {timerButton.stop();}
			if (timerHold) {timerHold.stop();}
		}		
		
	}
	
}


package com.danzen.interfaces.ostrich {
	
	// OSTRICH INTRODUCTION  
	// Ostrich lets you follow video motion with a cursor
	// for instance, you can wave at a Webcam and make a cursor move to where you wave
	// this can be used as an interface to control elements of your application
	
	// FEATURES AND CONSIDERATIONS
	// you can specify a region in which to look for motion
	// you can specify multiple cursors - each with their own region
	// the OstrichButton class is provided to trigger over, out and hold events (no click)
	// the fastest way to find out if a button is activated is to make it a cursor region
	// then use the cursor start and stop to catch activity in that region
	// you can make your own clips follow the OstrichCursor

	// WORKINGS
	// OstrichCursor is optimized for a single person standing in the middle using hands
	// a rectangle is put around all motion and then a cursor is positioned as follows:
	// the y position of the cursor is set to ten pixels beneath the top of the motion rectangle
	// if rectangle is at the left of the camera it takes the left edge of rectangle for cursor x position
	// if rectangle is at right it takes the right edge of rectangle for cursor x position
	// if rectangle is in the center it takes the center of the rectangle for cursor x position
	// if rectangle is anywhere else it uses the proportion to figure cursor x location
	// you can adjust this by reworking the class
		
	// http://ostrichflash.wordpress.com - by inventor Dan Zen - http://www.danzen.com
	// if you are using Ostrich for commercial purposes, you are welcome to donate to Dan Zen
	// donations can be made to agency@danzen.com at http://www.paypal.com
	// also be aware that Gesture Tek and perhaps others hold patents in these areas

	// INSTALLING CLASSES  
	// suggested installation:
	// create a "classes" folder on your hard drive - for example c:\classes
	// add the classes folder to your Flash class path:
	// Flash menu choose Edit > Preferences > ActionScript - ActionScript 3 Settings 
	// then use the + sign for the source path box to add the path to your classes folder
	// put the provided com/danzen/ directory with its folders and files in the classes folder
	// the readme has more information if you need it

	// USING OSTRICH  
	// please make sure that the following director is in a folder in your class path:
	// com/danzen/interfaces/ostrich/
	// see the samples for how to use the Ostrich classes
	
	// OstrichCamera - sets up a Web cam for capturing motion
	// OstrichCursor - sets up a cursor that follows the motion in OstrichCamera
	// OstrichButton - sets up events for an invisible hotspot per OstrichCursor
	// OstrichBlobs - puts blobs on any motion	
	
	// ******************
	// This class should only be called after an OstrichCamera.READY event fires	

	import flash.display.Sprite;
	import flash.events.*;
	import flash.utils.Timer;
	
	public class OstrichBlobs extends Sprite {
		
		// CONSTRUCTOR  
		// OstrichBlobs(theCam:OstrichCamera, theResponse:Number=4):void
		// 		OstrichBlobs puts blobs on any motion
	    // 		You can hide the blobs by not adding them to the stage
		// 		and then you can use their location to trigger interactivity with hitTestPoint(), etc.
		// 		or you can put your own Sprites or MovieClips where the blobs are
		//		a blob Sprite is made for each grid square the OstrichBlobs analyze
		//		if there is no motion in a square then the x of the blob is at -2000
		//		
		// 		PARAMETERS:
		//		theCam:OstrichCamera - the cam used for motion detection
		// 		theResponse:Number - from 1-10 default 4.  1 is fast but jumpy - 10 is slow and smooth
		
		// METHODS (in addition to constructor)
		// dispose():void - stops and removes cursor

		// PROPERTIES  
		// cam:OstrichCamera - the cam feed passed to the OstrichCursor object
		// response:Number - between 1-10 - cursor is checked each followInterval
		//					 but reported every response number of times
		//					 movement between reports is averaged to smoothen the motion
		// blobs:Array - an array of blob Sprites - so you can get x, y and width, etc.
						
		
		public var myCamera:OstrichCamera;
		public var myCursors:Array = [];	
		private var myGridMotion:Array = [];
		private var myGridLocation:Array = [];
		private var myCursorClips:Array = [];
		private var doneList:Array;
		private var blobList:Array;		
		private var readyCheck:Boolean = false;
		
		private var max:Number = 12;
		private var threshhold:Number = 3;
		private var startDelay:Number = 3; // seconds
		
		private var myResponse:Number;
		private var myTimer:Timer;
				
		public function OstrichBlobs(theCam:OstrichCamera, theResponse:Number=4) {
			
			trace ("hi from OstrichBlobs");
						
			myTimer = new Timer(startDelay*1000,1);
			myTimer.addEventListener(TimerEvent.TIMER, function (e:TimerEvent) {readyCheck = true;});
			myTimer.start();
			
			myCamera = theCam;
			
			var temp:OstrichCursor;
			var tempSprite:Sprite;
			
			var gridW:Number = 640 / max;
			var gridH:Number = 480 / max;			
			
			for (var i:uint = 0; i<max; i++) {
				for (var j:uint = 0; j<max; j++) {					
					temp = new OstrichCursor(myCamera, i*gridW, j*gridH, (i+1)*gridW, (j+1)*gridH, theResponse);
					myCursors.push(temp); 					
					myGridMotion.push(0);
					myGridLocation.push([i*gridW+gridW/2, j*gridH+gridH/2]);
					temp.addEventListener(OstrichCursor.MOTION_START, onStart);
					temp.addEventListener(OstrichCursor.MOTION_STOP, onStop);
					
					tempSprite = new Sprite;
					tempSprite.graphics.beginFill(0xFF99CC, .6);
					tempSprite.graphics.drawCircle(0,0,100);
					tempSprite.x = -2000;
					addChild(tempSprite);
					myCursorClips.push(tempSprite);			
					
				}
			}			
			
		}
		
		private function onStart(e:Event) {
			myGridMotion[e.target.cursorNum] = 1;
			analyseGrid();
		}
		
		private function onStop(e:Event) {
			myGridMotion[e.target.cursorNum] = 0;
			analyseGrid();
		}				
		
		
		private function analyseGrid() {
			
			if (!readyCheck) {return;}
			
			//set the grid max to 6 and uncomment this to view this arrangement
			/*myGridMotion = [1,1,0,0,0,0,
							1,1,0,0,0,0,
							0,0,0,0,0,0,
							0,0,0,0,0,0,
							0,0,0,0,1,1,
							0,0,0,0,1,1];*/
			
			
			doneList = [];
			blobList = [];
			
			var gridW:Number = 640 / max;
			var gridH:Number = 480 / max;
			
			var num:Number;
			var m:uint;
			var n:uint;			
			
			function goR(n:Number) {
				var col:Number = n % max + 1;
				if (col+1 > max) {return -1;} else {return n+1;}
			}
			function goL(n:Number) {
				var col:Number = n % max + 1;
				if (col-1 < 1) {return -1;} else {return n-1;}
			}	
			function goB(n:Number) {
				var row:Number = Math.floor(n / max) + 1;				
				if (row+1 > max) {return -1;} else {return n+max;}
			}
			function goT(n:Number) {
				var row:Number = Math.floor(n / max) + 1;				
				if (row-1 < 1) {return -1;} else {return n-max;}
			}				
			function checkAround(n:Number) {
				var newNum:Number;
				var functionList:Array = [goR, goL, goB, goT];
				for (var r:uint=0; r<4; r++) {
					newNum = functionList[r](n);
					if (newNum != -1 && myGridMotion[newNum] == 1 && doneList.indexOf(newNum) == -1) {
						doneList.push(newNum)
						blobList[blobList.length-1].push(newNum);
						checkAround(newNum);
					}
				}
			}			
			
			for (var i:uint = 0; i<max; i++) {
				for (var j:uint = 0; j<max; j++) {
					num = i * max + j;					
					if (myGridMotion[num] == 1 && doneList.indexOf(num) == -1) {					
						blobList.push([num]);
						doneList.push(num);						
						checkAround(num);
					}				
				}				
			}
				
			
			var e:Number;
			var tX:Number;
			var tY:Number;
			var t:Number;
			var factor:Number = (640+480)/2/max;
			var blobCursors:Array = [];
			for (var b:uint=0; b<blobList.length; b++) {
				t = blobList[b].length;
				if (t < threshhold) {continue;}
				tX = tY = 0;
				for (e=0; e<t; e++) {
					tX += myGridLocation[blobList[b][e]][0];
					tY += myGridLocation[blobList[b][e]][1];
				}
				// x average, y average, radius average
				blobCursors.push([Math.round(tX / t), Math.round(tY / t), Math.sqrt(t)*factor/2]);
			}
			
			for (var q:uint=0; q<Math.pow(max,2); q++) {				
				myCursorClips[q].x = -2000;
			}
					
			var c:uint;			
			for (c=0; c<blobCursors.length; c++) {				
				myCursorClips[c].width = myCursorClips[c].height = blobCursors[c][2] * 2;
				myCursorClips[c].x = blobCursors[c][0]+myCamera.x;
				myCursorClips[c].y = blobCursors[c][1]+myCamera.y;
			}		
			
			
		}
		
		public function get response():Number {
			return myResponse;
		}
		
		public function set response(r:Number) {
			myResponse = Math.max(Math.min(10,r),1);
			for (var i:uint = 0; i<max*max; i++) {
				myCursors[i].response = myResponse; 					
			}		
		}
		
		public function get blobs():Array {
			return myCursorClips;
		}
		
		public function dispose() {
			for (var i:uint = 0; i<max*max; i++) {
				myCursors[i].removeEventListener(OstrichCursor.MOTION_START, onStart);
				myCursors[i].removeEventListener(OstrichCursor.MOTION_STOP, onStop);
				myCursors[i].dispose();				
				removeChild(myCursorClips[i]);
				delete myCursorClips[i];
			}
			for (i=0; i<max*max; i++) {
				delete myCursors[i];
			}
			myTimer.stop();
			myTimer = null;			
		}	
		
	}
	
}