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();}
}
}
}