DAN ZEN EXPO - CODE EXHIBIT - DODO


package samples {
	
	import flash.display.Sprite;
	import flash.events.*;
	import com.danzen.interfaces.dodo.*;
		
	
	public class Blobs extends Sprite {
		
		private var myCamera:DodoCamera;
		private var myBlobs:DodoBlobs;
		private var myConfig:DodoConfig;
				
		public function Blobs() {		
					
			trace ("hi from Blobs");			
			
			// make a DodoCamera object			
			myCamera = new DodoCamera();	
			addChild(myCamera);			
			
			myCamera.addEventListener(DodoCamera.READY, init);
		}
		private function init(e:Event):void {
			
			// make a DodoBlobs object
			myBlobs = new DodoBlobs(myCamera); 
			addChild(myBlobs); 
			
			// config lets you set sensitivity via a slider
			// pressing SHIFT D for Dodo will toggle the visibility of the slider
			// need to drop slider and radiobutton components into your library ;-)
			// also the DodoIcon component - just copy it from this example
			myConfig = new DodoConfig(myBlobs);
			addChild(myConfig);
			
		}						
		
	}
	
}

package com.danzen.interfaces.dodo {
	
	// DODO INTRODUCTION  
	// Dodo lets you track light objects on a dark background or dark objects on a light background
	// It puts blobs where it finds objects
	
	// http://dodoflash.wordpress.com - by inventor Dan Zen - http://www.danzen.com
	// if you are using Dodo 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
			
	// FEATURES
	// Dodo generates XML as ManyML and this can be fed to multitouch classes of Goose
	// ManyML - http://manyml.wordpress.com
	// Goose Multitouch for Flash - http://gooseflash.wordpress.com
	// this allows you to pick things up and resize them etc. with multiple fingers or wands

	// WORKINGS
	// Dodo uses a threshold system and you can set the sensitivity with DodoConfig
	// if the background is dark and the sensitivity is 5 then anything lighter than middle grey will show
	// if the sensitivity is 2 then it needs to be lighter than light grey (fewer objects)
	// if the sensitivity is 8 then it needs to be lighter than dark grey (more objects)
	
	// COMPARISON
	// the Flash Feathers series also provides Ostrich at http://ostrichflash.wordpress.com
	// Ostrich captures and tracks video motion and also has blobs
	// Ostrich works well under general ambient light as it is locating motion only
	// Dodo does not locate motion but rather opposite shades and thus does not work well under ambient light
	// it is best to have high contrast or at least a solid background 
	// with only the blobs you want to detect being visible
	// as such, if there is too much detected then the recursive calculations bog the application
	
	// PHYSICAL SYSTEMS
	// there are all sorts of multitouch table designs - you can search on YouTube
	// generally, you want a single background with the opposite shade as items of touch
	// so the surface might be a light vellum and then your fingers are dark
	// or make a dark box with a black plastic duotang cover and shine pen lights at it
		
	// 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 DODO  
	// please make sure that the following director is in a folder in your class path:
	// com/danzen/interfaces/dodo/
	// see the samples for how to use the Dodo classes
	
	// DodoCamera - sets up the Web cam - macs may need to adjust their active camera setting in Flash
	// DodoBlob - detects for a blob in a single location
	// DodoBlobs - runs a series of DodoBlobs to detect for blobs across the whole camera
	// DodoConfig - sets up a config panel for sensitivity and light or dark background
	// DodoData - outputs location of blobs in ManyML XML format - can use with Goose Multitouch

	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 DodoCamera 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 Dodo 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 
	// DodoCamera.READY - the camera is on and ready for motion capture 
	// DodoCamera.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 (DodoCamera.READY) for camera ready event
	// NO_CAMERA:String - static constant (DodoCamera.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 cam 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 DodoCursor.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 DodoCamera(theL:Number=0, theT:Number=0, theR:Number=640, theB:Number=480, theFlip:Boolean=true) {
				
			if (camTotal==0) {
				trace("hi from DodoCamera");
			}
			
			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(DodoCamera.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(DodoCamera.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.dodo {
	
	// DODO INTRODUCTION  
	// Dodo lets you track light objects on a dark background or dark objects on a light background
	// It puts blobs where it finds objects
	
	// http://dodoflash.wordpress.com - by inventor Dan Zen - http://www.danzen.com
	// if you are using Dodo 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
			
	// FEATURES
	// Dodo generates XML as ManyML and this can be fed to multitouch classes of Goose
	// ManyML - http://manyml.wordpress.com
	// Goose Multitouch for Flash - http://gooseflash.wordpress.com
	// this allows you to pick things up and resize them etc. with multiple fingers or wands
	
	// WORKINGS
	// Dodo uses a threshold system and you can set the sensitivity with DodoConfig
	// if the background is dark and the sensitivity is 5 then anything lighter than middle grey will show
	// if the sensitivity is 2 then it needs to be lighter than light grey (fewer objects)
	// if the sensitivity is 8 then it needs to be lighter than dark grey (more objects)
	
	// COMPARISON
	// the Flash Feathers series also provides Ostrich at http://ostrichflash.wordpress.com
	// Ostrich captures and tracks video motion and also has blobs
	// Ostrich works well under general ambient light as it is locating motion only
	// Dodo does not locate motion but rather opposite shades and thus does not work well under ambient light
	// it is best to have high contrast or at least a solid background 
	// with only the blobs you want to detect being visible
	// as such, if there is too much detected then the recursive calculations bog the application
	
	// PHYSICAL SYSTEMS
	// there are all sorts of multitouch table designs - you can search on YouTube
	// generally, you want a single background with the opposite shade as items of touch
	// so the surface might be a light vellum and then your fingers are dark
	// or make a dark box with a black plastic duotang cover and shine pen lights at it
		
	// 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 DODO  
	// please make sure that the following director is in a folder in your class path:
	// com/danzen/interfaces/dodo/
	// see the samples for how to use the Dodo classes
	
	// DodoCamera - sets up the Web cam - macs may need to adjust their active camera setting in Flash
	// DodoBlob - detects for a blob in a single location
	// DodoBlobs - runs a series of DodoBlobs to detect for blobs across the whole camera
	// DodoConfig - sets up a config panel for sensitivity and light or dark background
	// DodoData - outputs location of blobs in ManyML XML format - can use with Goose Multitouch
	
	import flash.display.MovieClip;
	import flash.events.*;
	import fl.controls.Slider;
	import fl.controls.RadioButton;
	import flash.text.TextField;

	import com.danzen.interfaces.Pane;

	
	public class DodoConfig extends MovieClip {
		
		// CONSTRUCTOR  
		// DodoConfig(theBlobs:DodoBlobs):void
		// 		DodoConfig lets you set the sensitivity and background of DodoBlobs
		//		It is a little panel that you can turn off and on with SHIFT D
		
		// 		PARAMETERS:
		//		theCam:DodoCamera - the cam used for blob detection		

		// EVENTS 
		
		// METHODS (in addition to constructor)
		// dispose():void - stops and removes cursor

		// PROPERTIES 
		
		// CONSTANTS 
		
		// USAGE
		// myConfig = new DodoConfig(myBlobs);
		// addChild(myConfig);		
		
		private var myBlobs:DodoBlobs;
		private var myPane:Pane;
		private var myIcon:DodoIcon;
		private var mySlider:Slider;
		private var myRadioButtons:RadioButton;
		private var myText:TextField;
				
		public function DodoConfig(theBlobs:DodoBlobs) {
			myBlobs = theBlobs;
			addEventListener(Event.ADDED_TO_STAGE, init);
		}
		
		private function init(e:Event) {
			
			trace ("hi from DodoConfig");
			
			myPane = new Pane(500,80,true,0xFFFFFF,.8,true);
			myPane.x = 70; 
			myPane.y = 370;
			addChild(myPane);
			myPane.addEventListener(Pane.EXIT, togglePane);			
			stage.addEventListener(KeyboardEvent.KEY_DOWN, checkKey);
			
			myIcon = new DodoIcon();
			myIcon.x = 10;
			myIcon.y = 10;
			myPane.addChild(myIcon);			
			
			myText = new TextField();
			myText.text = "SENSITIVITY";
			myText.x = 144;
			myText.y = 6;
			myText.alpha = .5;
			myText.selectable = false;
			myPane.addChild(myText);
			
			mySlider = new Slider();
			mySlider.x = 144;
			mySlider.y = 34;
			mySlider.maximum = 10;
			mySlider.minimum = 0;
			mySlider.tickInterval = 1;
			mySlider.snapInterval = .5;
			mySlider.liveDragging = true;
			mySlider.width = 300;
			mySlider.value = myBlobs.sensitivity;
			mySlider.addEventListener(Event.CHANGE, doSlider);
			myPane.addChild(mySlider);
						
			var radio1:RadioButton = new RadioButton();
			radio1.label = "DARK BACKGROUND";
			radio1.move(144, 52);
			radio1.width = 200;
			radio1.addEventListener(MouseEvent.CLICK, doRadio);
			myPane.addChild(radio1);
			
			var radio2:RadioButton = new RadioButton();
			radio2.label = "LIGHT BACKGROUND";
			radio2.move(282, 52);
			radio2.width = 200;
			radio2.addEventListener(MouseEvent.CLICK, doRadio);
			myPane.addChild(radio2);
			
			radio1.alpha = .5;
			radio2.alpha = .5;
			
			if (myBlobs.background == "dark") {
				radio1.selected = true;
			} else {
				radio2.selected = true;
			}			
		}
		
		private function doRadio(e:MouseEvent) {
			if (e.currentTarget.label == "DARK BACKGROUND"){
				myBlobs.background = "dark";
			} else {
				myBlobs.background = "light";
			}
		}
		
		private function doSlider(e:Event) {
			myBlobs.sensitivity = e.currentTarget.value;
		}
		
		private function togglePane(e:Event) {
			if (contains(myPane)) {
				removeChild(myPane);			
			} else {
				addChild(myPane);	
			}
			stage.focus = null;								
		}
		
		private function checkKey(e:KeyboardEvent) {	
			if (e.shiftKey && e.keyCode==68) { // SHIFT D for Dodo
				togglePane(new Event(Pane.EXIT));
			}
		}
		
		public function dispose() {
			mySlider.removeEventListener(Event.CHANGE, doSlider);			
			myPane.removeEventListener(Pane.EXIT, togglePane);		
			if (contains(myPane)) {
				removeChild(myPane);
			}
			myPane.dispose();			
			myPane = null;
			stage.removeEventListener(KeyboardEvent.KEY_DOWN, checkKey);
		}
	}	
}


package com.danzen.interfaces.dodo {
	
	// DODO INTRODUCTION  
	// Dodo lets you track light objects on a dark background or dark objects on a light background
	// It puts blobs where it finds objects
	
	// http://dodoflash.wordpress.com - by inventor Dan Zen - http://www.danzen.com
	// if you are using Dodo 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
			
	// FEATURES
	// Dodo generates XML as ManyML and this can be fed to multitouch classes of Goose
	// ManyML - http://manyml.wordpress.com
	// Goose Multitouch for Flash - http://gooseflash.wordpress.com
	// this allows you to pick things up and resize them etc. with multiple fingers or wands
		
	// WORKINGS
	// Dodo uses a threshold system and you can set the sensitivity with DodoConfig
	// if the background is dark and the sensitivity is 5 then anything lighter than middle grey will show
	// if the sensitivity is 2 then it needs to be lighter than light grey (fewer objects)
	// if the sensitivity is 8 then it needs to be lighter than dark grey (more objects)
	
	// COMPARISON
	// the Flash Feathers series also provides Ostrich at http://ostrichflash.wordpress.com
	// Ostrich captures and tracks video motion and also has blobs
	// Ostrich works well under general ambient light as it is locating motion only
	// Dodo does not locate motion but rather opposite shades and thus does not work well under ambient light
	// it is best to have high contrast or at least a solid background 
	// with only the blobs you want to detect being visible
	// as such, if there is too much detected then the recursive calculations bog the application
	
	// PHYSICAL SYSTEMS
	// there are all sorts of multitouch table designs - you can search on YouTube
	// generally, you want a single background with the opposite shade as items of touch
	// so the surface might be a light vellum and then your fingers are dark
	// or make a dark box with a black plastic duotang cover and shine pen lights at it
		
	// 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 DODO  
	// please make sure that the following director is in a folder in your class path:
	// com/danzen/interfaces/dodo/
	// see the samples for how to use the Dodo classes
	
	// DodoCamera - sets up the Web cam - macs may need to adjust their active camera setting in Flash
	// DodoBlob - detects for a blob in a single location
	// DodoBlobs - runs a series of DodoBlobs to detect for blobs across the whole camera
	// DodoConfig - sets up a config panel for sensitivity and light or dark background
	// DodoData - outputs location of blobs in ManyML XML format - can use with Goose Multitouch	
	
	import flash.display.Sprite;
	import flash.events.*;
	import flash.utils.Timer;
	
	public class DodoData extends Sprite {
		
		// CONSTRUCTOR  
		// DodoData(theBlobs:DodoBlobs):void
		// 		DodoConfig lets you set the sensitivity and background of DodoBlobs
		//		It is a little panel that you can turn off and on with SHIFT D
		
		// 		PARAMETERS:
		//		theBlobs:DodoBlobs - the blobs to get the x, y data from		

		// EVENTS 
		// Event.CHANGE - triggers on ENTER_FRAME not really on change...
		
		// METHODS (in addition to constructor)
		// dispose():void - stops data from dispatching

		// PROPERTIES 
		// xmlData:XML - the data in the ManyML XML format http://manyml.wordpress.com
		
		// CONSTANTS 
		
		// USAGE
		// myDodoData = new DodoData(myBlobs);
		// myDodoData.addEventListener(Event.CHANGE, giveData);	
		// function giveData(e:Event) {
		// 	trace (myDodoData.xmlData);
		// }
		
		private var myBlobs:DodoBlobs;
		public var xmlData:XML;
		private var myTimer:Timer;
				
		public function DodoData(theBlobs:DodoBlobs) {
			myBlobs = theBlobs;
			myTimer = new Timer(300);
			myTimer.start();
			myTimer.addEventListener(TimerEvent.TIMER, doData);
		}
		
		private function doData(e:TimerEvent) {
						
			var theXML:String = "<manyml>\n";
			
			var len:Number = myBlobs.blobs.length;			
			for (var i:uint=0; i<len; i++) {
				if (myBlobs.blobs[i].x > -1000) {					
					theXML += '<item id="' + i + '" x="' + myBlobs.blobs[i].x + '" y="' + myBlobs.blobs[i].y + '" z="1" />\n';
				} else {
					// it appears that Goose has a bug 
					// it gets confused if the cursors do not hang around "unpressed"
					// will correct this one day...
					if (i < 10) {
						theXML += '<item id="' + i + '" x="' + myBlobs.blobs[i].x + '" y="' + myBlobs.blobs[i].y + '" z="-100" />\n';
					}					
				}
			}
			theXML += "</manyml>";
			xmlData = XML(theXML);
			dispatchEvent(new Event(Event.CHANGE));
		}
		
		public function dispose() {		
			removeEventListener(Event.ENTER_FRAME, doData);
			xmlData = null;
		}				
		
	}	
}



package com.danzen.interfaces.dodo {
	
	// DODO INTRODUCTION  
	// Dodo lets you track light objects on a dark background or dark objects on a light background
	// It puts blobs where it finds objects
	
	// http://dodoflash.wordpress.com - by inventor Dan Zen - http://www.danzen.com
	// if you are using Dodo 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
			
	// FEATURES
	// Dodo generates XML as ManyML and this can be fed to multitouch classes of Goose
	// ManyML - http://manyml.wordpress.com
	// Goose Multitouch for Flash - http://gooseflash.wordpress.com
	// this allows you to pick things up and resize them etc. with multiple fingers or wands
	
	// WORKINGS
	// Dodo uses a threshold system and you can set the sensitivity with DodoConfig
	// if the background is dark and the sensitivity is 5 then anything lighter than middle grey will show
	// if the sensitivity is 2 then it needs to be lighter than light grey (fewer objects)
	// if the sensitivity is 8 then it needs to be lighter than dark grey (more objects)
	
	// COMPARISON
	// the Flash Feathers series also provides Ostrich at http://ostrichflash.wordpress.com
	// Ostrich captures and tracks video motion and also has blobs
	// Ostrich works well under general ambient light as it is locating motion only
	// Dodo does not locate motion but rather opposite shades and thus does not work well under ambient light
	// it is best to have high contrast or at least a solid background 
	// with only the blobs you want to detect being visible
	// as such, if there is too much detected then the recursive calculations bog the application
	
	// PHYSICAL SYSTEMS
	// there are all sorts of multitouch table designs - you can search on YouTube
	// generally, you want a single background with the opposite shade as items of touch
	// so the surface might be a light vellum and then your fingers are dark
	// or make a dark box with a black plastic duotang cover and shine pen lights at it
		
	// 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 DODO  
	// please make sure that the following director is in a folder in your class path:
	// com/danzen/interfaces/dodo/
	// see the samples for how to use the Dodo classes
	
	// DodoCamera - sets up the Web cam - macs may need to adjust their active camera setting in Flash
	// DodoBlob - detects for a blob in a single location
	// DodoBlobs - runs a series of DodoBlobs to detect for blobs across the whole camera
	// DodoConfig - sets up a config panel for sensitivity and light or dark background
	// DodoData - outputs location of blobs in ManyML XML format - can use with Goose Multitouch	
	import flash.display.Sprite;
	import flash.events.*;
	import flash.utils.Timer;
	
	public class DodoColors extends Sprite {
		
		// CONSTRUCTOR  
		// DodoColors(theCam:DodoCamera, theResponse:Number=2, theBackground:String="dark", theSensitivity:Number=5):void
		// 		DodoBlobs puts blobs on any non background color
	    // 		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 DodoBlobs analyze
		//		if there is only background color in a square then the x of the blob is at -2000
		//		
		// 		PARAMETERS:
		//		theCam:DodoCamera - the cam used for motion detection
// 		//		theResponse:Number - from 1-10 default 4.  1 is fast but jumpy - 10 is slow and smooth
		// 		theBackground:String - "light" or "dark" - default dark
		//		theSensitivity:Number - from 0-10 default 5 - higher finds more blobs, lower finds less blobs	
		
		// METHODS (in addition to constructor)
		// dispose():void - stops and removes cursor

		// PROPERTIES  
		// cam:DodoCamera - the cam feed passed to the DodoCursor 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
		// background:String - "light" or "dark" background hence either dark or light blobs expected
		// sensitivity:Number - from 0-10 higher finds more blobs, lower finds less blobs
		// blobs:Array - an array of blob Sprites - so you can get x, y and width, etc.		
				
		// USAGE
		// in your classes you would use:
		//		var myCam:DodoCamera = new DodoCamera(0,0,640,480);
		//		var myBlobs:DodoBlobs = new DodoBlobs(myCam, 2); 
		//		addEventListener(Event.ENTER_FRAME, myFunction);
		//		function myFunction(e:Event) {
		//			var len:Number = myBlobs.blobs.length;
		//			for (var i:uint=0; i<len; i++) {
		//				if (myBlobs.blobs[i].x > -1000) {
		//					trace (i, myBlobs.blobs[i].x, myBlobs.blobs[i].y);
		//				}
		//			}
		//		}					
		
		public var myCamera:DodoCamera;
		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 mySensitivity:Number;
		private var myResponse:Number;
		private var myBackground:String;
		private var myTimer:Timer;
		
		// ********* max is the dimension of the grid of sensors
		// ********* this checks a grid of 30 x 30 squares
		// ********* and if there are at least 3 (threshold) squares with motion
		// ********* then a blob is placed here
		// ********* reducing the max will reduce processing but only work with larger targets
		private var max:Number = 30;
		private var threshhold:Number = 3;
		private var startDelay:Number = 3; // seconds
				
		public function DodoBlobs(theCam:DodoCamera, theResponse:Number=2, theBackground:String="dark", theSensitivity:Number=5) {			
			
			trace ("hi from DodoBlobs");
			
			myTimer = new Timer(startDelay*1000,1);
			myTimer.addEventListener(TimerEvent.TIMER, function (e:TimerEvent) {readyCheck = true;});
			myTimer.start();
			
			// make a VideoMotionCamera object			
			myCamera = theCam;				

			var temp:DodoBlob;
			var tempSprite:Sprite;
			
			var gridW:Number = myCamera.width / max;
			var gridH:Number = myCamera.height / max;			
			
			for (var i:uint = 0; i<max; i++) {
				for (var j:uint = 0; j<max; j++) {					
					temp = new DodoBlob(myCamera, i*gridW, j*gridH, (i+1)*gridW, (j+1)*gridH, 2, theBackground, 5);
					myCursors.push(temp); 					
					myGridMotion.push(0);
					myGridLocation.push([i*gridW+gridW/2, j*gridH+gridH/2]);
					temp.addEventListener(DodoBlob.MOTION_START, onStart);
					temp.addEventListener(DodoBlob.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);			
					
				}
			}			
			sensitivity = theSensitivity;
			response = theResponse;
			background = theBackground;
			
		}
		
		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 = myCamera.width / max;
			var gridH:Number = myCamera.height / 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 = (myCamera.width+myCamera.height)/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];
				myCursorClips[c].y = blobCursors[c][1];
			}		
			
			
		}
		
		public function get sensitivity():Number {
			return mySensitivity;
		}
		public function get response():Number {
			return myResponse;
		}
		public function get background():String {
			return myBackground;
		}
		public function set background(b:String):void {
			if (b == "dark" || b == "black" || b == "DARK" || b == "BLACK") {
				myBackground = "dark";
			} else {
				myBackground = "light";
			}
			for (var i:uint = 0; i<max*max; i++) {
				myCursors[i].background = myBackground; 					
			}	
		}
		public function set sensitivity(s:Number):void {
			mySensitivity = Math.max(Math.min(10,s),0);			
			for (var i:uint = 0; i<max*max; i++) {
				myCursors[i].sensitivity = mySensitivity; 					
			}						
		}
		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(DodoBlob.MOTION_START, onStart);
				myCursors[i].removeEventListener(DodoBlob.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;			
		}	
		
		
	}
	
}


package com.danzen.interfaces.dodo {
	
	// DODO INTRODUCTION  
	// Dodo lets you track light objects on a dark background or dark objects on a light background
	// It puts blobs where it finds objects
	
	// http://dodoflash.wordpress.com - by inventor Dan Zen - http://www.danzen.com
	// if you are using Dodo 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
			
	// FEATURES
	// Dodo generates XML as ManyML and this can be fed to multitouch classes of Goose
	// ManyML - http://manyml.wordpress.com
	// Goose Multitouch for Flash - http://gooseflash.wordpress.com
	// this allows you to pick things up and resize them etc. with multiple fingers or wands
	
	// WORKINGS
	// Dodo uses a threshold system and you can set the sensitivity with DodoConfig
	// if the background is dark and the sensitivity is 5 then anything lighter than middle grey will show
	// if the sensitivity is 2 then it needs to be lighter than light grey (fewer objects)
	// if the sensitivity is 8 then it needs to be lighter than dark grey (more objects)
	
	// COMPARISON
	// the Flash Feathers series also provides Ostrich at http://ostrichflash.wordpress.com
	// Ostrich captures and tracks video motion and also has blobs
	// Ostrich works well under general ambient light as it is locating motion only
	// Dodo does not locate motion but rather opposite shades and thus does not work well under ambient light
	// it is best to have high contrast or at least a solid background 
	// with only the blobs you want to detect being visible
	// as such, if there is too much detected then the recursive calculations bog the application
	
	// PHYSICAL SYSTEMS
	// there are all sorts of multitouch table designs - you can search on YouTube
	// generally, you want a single background with the opposite shade as items of touch
	// so the surface might be a light vellum and then your fingers are dark
	// or make a dark box with a black plastic duotang cover and shine pen lights at it
		
	// 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 DODO  
	// please make sure that the following director is in a folder in your class path:
	// com/danzen/interfaces/dodo/
	// see the samples for how to use the Dodo classes
	
	// DodoCamera - sets up the Web cam - macs may need to adjust their active camera setting in Flash
	// DodoBlob - detects for a blob in a single location
	// DodoBlobs - runs a series of DodoBlobs to detect for blobs across the whole camera
	// DodoConfig - sets up a config panel for sensitivity and light or dark background
	// DodoData - outputs location of blobs in ManyML XML format - can use with Goose Multitouch	
	import flash.display.Sprite;
	import flash.events.*;
	import flash.utils.Timer;
	
	public class DodoBlobs extends Sprite {
		
		// CONSTRUCTOR  
		// DodoBlobs(theCam:DodoCamera, theResponse:Number=2, theBackground:String="dark", theSensitivity:Number=5):void
		// 		DodoBlobs puts blobs on any non background color
	    // 		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 DodoBlobs analyze
		//		if there is only background color in a square then the x of the blob is at -2000
		//		
		// 		PARAMETERS:
		//		theCam:DodoCamera - the cam used for motion detection
// 		//		theResponse:Number - from 1-10 default 4.  1 is fast but jumpy - 10 is slow and smooth
		// 		theBackground:String - "light" or "dark" - default dark
		//		theSensitivity:Number - from 0-10 default 5 - higher finds more blobs, lower finds less blobs	
		
		// METHODS (in addition to constructor)
		// dispose():void - stops and removes cursor

		// PROPERTIES  
		// cam:DodoCamera - the cam feed passed to the DodoCursor 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
		// background:String - "light" or "dark" background hence either dark or light blobs expected
		// sensitivity:Number - from 0-10 higher finds more blobs, lower finds less blobs
		// blobs:Array - an array of blob Sprites - so you can get x, y and width, etc.		
				
		// USAGE
		// in your classes you would use:
		//		var myCam:DodoCamera = new DodoCamera(0,0,640,480);
		//		var myBlobs:DodoBlobs = new DodoBlobs(myCam, 2); 
		//		addEventListener(Event.ENTER_FRAME, myFunction);
		//		function myFunction(e:Event) {
		//			var len:Number = myBlobs.blobs.length;
		//			for (var i:uint=0; i<len; i++) {
		//				if (myBlobs.blobs[i].x > -1000) {
		//					trace (i, myBlobs.blobs[i].x, myBlobs.blobs[i].y);
		//				}
		//			}
		//		}					
		
		public var myCamera:DodoCamera;
		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 mySensitivity:Number;
		private var myResponse:Number;
		private var myBackground:String;
		private var myTimer:Timer;
		
		// ********* max is the dimension of the grid of sensors
		// ********* this checks a grid of 30 x 30 squares
		// ********* and if there are at least 3 (threshold) squares with motion
		// ********* then a blob is placed here
		// ********* reducing the max will reduce processing but only work with larger targets
		private var max:Number = 30;
		private var threshhold:Number = 3;
		private var startDelay:Number = 3; // seconds
				
		public function DodoBlobs(theCam:DodoCamera, theResponse:Number=2, theBackground:String="dark", theSensitivity:Number=5) {			
			
			trace ("hi from DodoBlobs");
			
			myTimer = new Timer(startDelay*1000,1);
			myTimer.addEventListener(TimerEvent.TIMER, function (e:TimerEvent) {readyCheck = true;});
			myTimer.start();
			
			// make a VideoMotionCamera object			
			myCamera = theCam;				

			var temp:DodoBlob;
			var tempSprite:Sprite;
			
			var gridW:Number = myCamera.width / max;
			var gridH:Number = myCamera.height / max;			
			
			for (var i:uint = 0; i<max; i++) {
				for (var j:uint = 0; j<max; j++) {					
					temp = new DodoBlob(myCamera, i*gridW, j*gridH, (i+1)*gridW, (j+1)*gridH, 2, theBackground, 5);
					myCursors.push(temp); 					
					myGridMotion.push(0);
					myGridLocation.push([i*gridW+gridW/2, j*gridH+gridH/2]);
					temp.addEventListener(DodoBlob.MOTION_START, onStart);
					temp.addEventListener(DodoBlob.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);			
					
				}
			}			
			sensitivity = theSensitivity;
			response = theResponse;
			background = theBackground;
			
		}
		
		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 = myCamera.width / max;
			var gridH:Number = myCamera.height / 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 = (myCamera.width+myCamera.height)/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];
				myCursorClips[c].y = blobCursors[c][1];
			}		
			
			
		}
		
		public function get sensitivity():Number {
			return mySensitivity;
		}
		public function get response():Number {
			return myResponse;
		}
		public function get background():String {
			return myBackground;
		}
		public function set background(b:String):void {
			if (b == "dark" || b == "black" || b == "DARK" || b == "BLACK") {
				myBackground = "dark";
			} else {
				myBackground = "light";
			}
			for (var i:uint = 0; i<max*max; i++) {
				myCursors[i].background = myBackground; 					
			}	
		}
		public function set sensitivity(s:Number):void {
			mySensitivity = Math.max(Math.min(10,s),0);			
			for (var i:uint = 0; i<max*max; i++) {
				myCursors[i].sensitivity = mySensitivity; 					
			}						
		}
		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(DodoBlob.MOTION_START, onStart);
				myCursors[i].removeEventListener(DodoBlob.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;			
		}	
		
		
	}
	
}


package com.danzen.interfaces.dodo {
	
	// DODO INTRODUCTION  
	// Dodo lets you track light objects on a dark background or dark objects on a light background
	// It puts blobs where it finds objects
	
	// http://dodoflash.wordpress.com - by inventor Dan Zen - http://www.danzen.com
	// if you are using Dodo 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
			
	// FEATURES
	// Dodo generates XML as ManyML and this can be fed to multitouch classes of Goose
	// ManyML - http://manyml.wordpress.com
	// Goose Multitouch for Flash - http://gooseflash.wordpress.com
	// this allows you to pick things up and resize them etc. with multiple fingers or wands
		
	// WORKINGS
	// Dodo uses a threshold system and you can set the sensitivity with DodoConfig
	// if the background is dark and the sensitivity is 5 then anything lighter than middle grey will show
	// if the sensitivity is 2 then it needs to be lighter than light grey (fewer objects)
	// if the sensitivity is 8 then it needs to be lighter than dark grey (more objects)
	
	// COMPARISON
	// the Flash Feathers series also provides Ostrich at http://ostrichflash.wordpress.com
	// Ostrich captures and tracks video motion and also has blobs
	// Ostrich works well under general ambient light as it is locating motion only
	// Dodo does not locate motion but rather opposite shades and thus does not work well under ambient light
	// it is best to have high contrast or at least a solid background 
	// with only the blobs you want to detect being visible
	// as such, if there is too much detected then the recursive calculations bog the application
	
	// PHYSICAL SYSTEMS
	// there are all sorts of multitouch table designs - you can search on YouTube
	// generally, you want a single background with the opposite shade as items of touch
	// so the surface might be a light vellum and then your fingers are dark
	// or make a dark box with a black plastic duotang cover and shine pen lights at it
		
	// 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 DODO  
	// please make sure that the following director is in a folder in your class path:
	// com/danzen/interfaces/dodo/
	// see the samples for how to use the Dodo classes
	
	// DodoCamera - sets up the Web cam - macs may need to adjust their active camera setting in Flash
	// DodoBlob - detects for a blob in a single location
	// DodoBlobs - runs a series of DodoBlobs to detect for blobs across the whole camera
	// DodoConfig - sets up a config panel for sensitivity and light or dark background
	// DodoData - outputs location of blobs in ManyML XML format - can use with Goose Multitouch

	import flash.display.BitmapData;
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.geom.*;
	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 DodoBlob extends Sprite {
		
		// *** cursor means blob in the below descriptions - this is modified from the Ostrich Cursor Class
		
		// CONSTRUCTOR  
		// DodoBlob(theCam:DodoCamera, theL:Number=0, theT:Number=0, theR:Number=0, theB:Number=0, theResponse:Number=4, theBackground:String="dark", theSensitivity:Number=5):void
		// 		DodoBlob detects a blob in a specified area of a DodoCamera
		//		It is then used by DodoBlobs to make blobs across the whole camera
		
		// 		PARAMETERS:
		//		theCam:DodoCamera - the cam used for blob detection
		// 		theL:Number - the left side x of the region (with respect to the DodoCam left)
		// 		theT:Number - the top side y of the region (with respect to the DodoCam top)
		// 		theR:Number - the right side x of the region (with respect to the DodoCam left)
		// 		theB:Number - the bottom side y of the region (with respect to the DodoCam top)
		// 		theResponse:Number - from 1-10 default 4.  1 is fast but jumpy - 10 is slow and smooth
		// 		theBackground:String - "light" or "dark" - default dark
		//		theSensitivity:Number - from 0-10 default 5 - higher finds more blobs, lower finds less blobs
		

		// EVENTS 
		// DodoBlob.MOTION_START 	blob first detected 
		// DodoBlob.MOTION_STOP 	blob stopped being detected 
		
		// METHODS (in addition to constructor)
		// dispose():void - stops and removes cursor

		// PROPERTIES  
		// cursorNum:Number - read only - the cursor num starting at 0 
		// cam:DodoCamera - the cam feed passed to the DodoBlob 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
		// background:String - "light" or "dark" background hence either dark or light blobs expected
		// sensitivity:Number - from 0-10 higher finds more blobs, lower finds less blobs		
		
		// CONSTANTS  
		// MOTION_START:String - static constant (DodoBlob.MOTION_START) for motion start event
		// MOTION_STOP:String - static constant (DodoBlob.MOTION_STOP) for motion stop event
		
		// USAGE
		// used only by DodoBlobs
		
				
		// 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
										
		// ********* increasing followInterval will reduce processing
		private var followInterval:Number = 100; // motion checking interval in ms
		
		// various holder variables and checks
		private var myCam:DodoCamera; // the cam instance
		private var motionRectangle:Sprite; // a holder for the motion rectangle (hidden)
		private var myBM:BitmapData; // the 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
				
		// these are variables used in the calculations		
		private var cursorSpeed:Number; // the interval the cursor moves based on followInterval * response in ms
		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 motionTally:Number = 0;		
		
		private var myResponse:Number;
		private var mySensitivity:Number;
		private var myBackground:String;

		public function DodoBlob(theCam:DodoCamera, theL:Number=0, theT:Number=0, theR:Number=0, theB:Number=0, theResponse:Number=4, theBackground:String="dark", theSensitivity:Number=5) {	
			
			if (cursorTotal == 0) {trace ("hi from DodoBlob");}		
			
			myCursorNum = cursorTotal++; // which means cursorNum starts at 0
								
			myCam = theCam;
			background = theBackground;
			sensitivity = theSensitivity;
			
			// this interval reports any motion collected by the tests
			// it operates on an interval of the response times the followInterval
			// it acts to make the blobs less jumpy but then also less responsive
			timerMoveCursor = new Timer(theResponse * followInterval);
			timerMoveCursor.addEventListener(TimerEvent.TIMER, moveCursor);
			timerMoveCursor.start();			
			
			response = theResponse;
			
			// create a sprite that will hold the overall motion rectangle that the cursor follows
			motionRectangle = new Sprite();
			addChild(motionRectangle);
							
			// 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; 
			
			if (theCam.camCheck) { // double check the camera is ready
				init();
			} else {
				trace ("--------------------------------");
				trace ("please call the DodoBlob(s) class");
				trace ("after using an DodoCamera.READY event");
				trace ("--------------------------------");				
			}
		}
			
		private function init() {
			
			if (cursorTotal == 0) {trace ("hi from DodoBlob");}					
												
			// 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 bitmap object to store the current video frames			
			myBM = 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();
			
		}				
			
		private function follow(c) {		
											
			// 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
			myBM.draw(myCam.cam,myMatrix,null); 
								
			// We apply the contrast color filter from the DodoCamera to focus in on our motion
			myBM.applyFilter(myBM,myBM.rect,new Point(0,0),myCam.cm);
						
			// We apply the blur filter from the DodoCamera to smoothen our motion region
			myBM.applyFilter(myBM,myBM.rect,new Point(0,0),myCam.bf);
									
			// the higher the sensitivity the more it picks up
			
			var myColor:Number;
			if (myBackground == "dark") {				
				myColor = (10-sensitivity) / 10 * 0xFFFFFF;
				myBM.threshold(myBM,myBM.rect,new Point(0,0),">",myColor,0xFF00FF00,0x00FFFFFF);
			} else {				
				myColor = sensitivity / 10 * 0xFFFFFF;   
				myBM.threshold(myBM,myBM.rect,new Point(0,0),"<",myColor,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 = myBM.getColorBoundsRect(0x00FFFFFF,0xFF00FF00,true);
			if (rect.width > 0) {
				motionTally++;		
			}	
			
		}	
		

		private function moveCursor(c) {			
			
			// handle checking for any motion				
			if (motionTally > 0 && motionCheck == false && myCam.camCheck) {
				motionCheck = true;
				dispatchEvent(new Event(DodoBlob.MOTION_START, true));
			} else if (motionTally == 0 && motionCheck && myCam.camCheck) {
				motionCheck = false;
				dispatchEvent(new Event(DodoBlob.MOTION_STOP, true));	
			}	
			motionTally = 0;			
			
		}		
		
		
		// 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 sensitivity():Number {
			return mySensitivity;
		}
		public function get response():Number {
			return myResponse;
		}
		public function get background():String {
			return myBackground;
		}
		public function set background(b:String):void {
			if (b == "dark" || b == "black") {
				myBackground = "dark";
			} else {
				myBackground = "light"
			}	
		}
		public function set sensitivity(s:Number):void {
			mySensitivity = Math.max(Math.min(10,s),0);									
		}
		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();}
		}				
				
	}
	
}