Simple Draw from PaintCanvas.as

From NUI Group Community Wiki

Jump to: navigation, search

I decided to look into some of these core files you can find in the Touchlib Library. If you haven't tried it out yet, there is a LOT of powerful stuff in there, but to me it seems rather tucked away. You need to open up the file and just read them to see what they intend to do. Then you can start to simplify.

Now the paint canvas file is used in the AS3\src\deploy\Paint.swf file. It give you a nice palette of color to choose from and uses Filters to blur and fade out and disperse the lines/dots as you draw them. Fact is that you are just dropping dots along a path to create the lines in the file.

Looking at that file, I wanted to pull out the most basic part. And maybe try to change it a little bit. So I don't really want to bring in the color picker, only the touch drawing part. And I want to change that to put down a random color for each time I get a new touch point.

Contents

Drawing Lines

Try pasting this code into the file:

package app.demo.MyTouchApp{

	import flash.events.TUIO;// allows to connect to touchlib/tbeta
	import flash.events.TouchEvent;// allows to use TouchEvent Event Listeners
	import flash.events.Event;
	import flash.display.Sprite;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.filters.*;
	import flash.geom.*;

	public class SimpleDraw extends Sprite {

		private var paintBmpData:BitmapData;
		private var paintBmpData2:BitmapData;
		private var buffer:BitmapData;
		private var paintBmp:Bitmap;
		private var brush:Sprite;
		private var filter:BitmapFilter;
		private var filter2:BitmapFilter;
		public var col:ColorTransform;

		private var colorArray = new Array();

		public function SimpleDraw():void {

			//--------connect to TUIO-----------------
			TUIO.init(this,'localhost',3000,'',true);
			trace("TouchArrayInfo Initialized");

			paintBmpData=new BitmapData(1024,768,true,0x00000000);
			brush=new Sprite;
			brush.graphics.beginFill(0xDDDDDD);
			brush.graphics.drawCircle(0,0,25);
			col = new ColorTransform(0,0,0);
			paintBmp=new Bitmap(paintBmpData);
			var cmat:Array=[1,1,1,1,1,1,1,1,1];
			filter=new ConvolutionFilter(50,50,cmat,10,10,true);
			filter2=new BlurFilter(15,15);
			addChild(paintBmp);

			addEventListener(Event.ENTER_FRAME,updateCall, false, 0, true);

		}
		public function updateCall(e:Event):void {
			for (var i=0; i < TUIO.returnBlobs().length; i++) {
				var localPt:Point=new Point(TUIO.returnBlobs()[i].x,TUIO.returnBlobs()[i].y);
				var m:Matrix=new Matrix;
				m.translate(localPt.x,localPt.y);
				paintBmpData.draw(brush,m);
			}
			paintBmpData.applyFilter(paintBmpData, paintBmpData.rect, new Point(), filter2);
		}
	}
}

You should see white/light grey lines drawn on the stage when you touch. See them? Excellent.

So what is the code doing? Well thanks to our trusty TUIO.returnBlobs() function in the TUIO.as file (refer to this post for more information), we do not have to gather all of the blob information in this AS file. TUIO does that for us, we just need to tap into it, but we don't need to update the lines being draw everytime a blob makes contact, is moved or is removed. All we need to worry about it updating the stage on ENTER_FRAME.

addEventListener(Event.ENTER_FRAME,updateCall, false, 0, true);

Wherein, updateCall only needs to read all the blobs on the stage at that moment and draw a new 'dot.' Since the FPS i have set is at 30, it's quick enough for moderate motion to create the look of a line, especially with blurring on the edges and a ConvolutionFilter. Should you think that the lines need to be drawn even quicker, try upping your FPS, but you may need a stronger computer to be able to keep up.

		public function updateCall(e:Event):void {
			for (var i=0; i < TUIO.returnBlobs().length; i++) {
				var localPt:Point=new Point(TUIO.returnBlobs()[i].x,TUIO.returnBlobs()[i].y);
				var m:Matrix=new Matrix;
				m.translate(localPt.x,localPt.y);
				paintBmpData.draw(brush,m);
			}
			paintBmpData.applyFilter(paintBmpData, paintBmpData.rect, new Point(), filter2);
		}

So for each blob per frame played, it finds the X and Y of each blob and uses the draw function to create a new chunk of line. The parts at the very top of the SimpleDraw function are the minimum pieces needed to make the drawing work. Play around with some of the numbers in the values to see how it changes the output.

Changing Colors

Lets add some new colors to it now

package app.demo.MyTouchApp{

	import flash.events.TUIO;// allows to connect to touchlib/tbeta
	import flash.events.TouchEvent;// allows to use TouchEvent Event Listeners
	import flash.events.Event;
	import flash.display.Sprite;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.filters.*;
	import flash.geom.*;

	public class SimpleDraw extends Sprite {

		private var paintBmpData:BitmapData;
		private var paintBmpData2:BitmapData;
		private var buffer:BitmapData;
		private var paintBmp:Bitmap;
		private var brush:Sprite;
		private var filter:BitmapFilter;
		private var filter2:BitmapFilter;
		public var col:ColorTransform;

		private var colorArray = new Array();

		public function SimpleDraw():void {

			//--------connect to TUIO-----------------
			TUIO.init(this,'localhost',3000,'',true);
			trace("TouchArrayInfo Initialized");

			paintBmpData=new BitmapData(1024,768,true,0x00000000);
			brush=new Sprite;
			brush.graphics.beginFill(0xDDDDDD);
			brush.graphics.drawCircle(0,0,25);
			col = new ColorTransform(0,0,0);
			paintBmp=new Bitmap(paintBmpData);
			var cmat:Array=[1,1,1,1,1,1,1,1,1];
			filter=new ConvolutionFilter(50,50,cmat,10,10,true);
			filter2=new BlurFilter(15,15);
			addChild(paintBmp);
			addEventListener(Event.ENTER_FRAME,updateCall, false, 0, true);

		}
		public function updateCall(e:Event):void {
			col = new ColorTransform(randomNumber(0,1),randomNumber(0,1),randomNumber(0,1));
			for (var i=0; i < TUIO.returnBlobs().length; i++) {
				var localPt:Point=new Point(TUIO.returnBlobs()[i].x,TUIO.returnBlobs()[i].y);
				var m:Matrix=new Matrix;
				m.translate(localPt.x,localPt.y);
				paintBmpData.draw(brush,m,col);
			}
			paintBmpData.applyFilter(paintBmpData, paintBmpData.rect, new Point(), filter2);
		}
		function randomNumber(low:Number=NaN,high:Number=NaN):Number {
			var low:Number=low;
			var high:Number=high;
			return Math.round(Math.random() * high - low) + low;
		}
	}
}

Now is a chance to add in ColorTransform and a random number generator. We'll randomly generate to a new color, but let's start with just changing the color each frame.

		function randomNumber(low:Number=NaN,high:Number=NaN):Number {
			var low:Number=low;
			var high:Number=high;
			return Math.round(Math.random() * high - low) + low;
		}

This function generates a random number from 2 values you pass into it, a high and low.

col = new ColorTransform(randomNumber(0,1),randomNumber(0,1),randomNumber(0,1));

So then this call will give me a random multiplier value of Red, Blue and Green. Changing the white to a new color. Trippy huh?

Unifying The Color

This is nice and all, but now I'd like to see a little more uniformity on the screen. Now, I only want the colors to change when you touch down on the screen with a new blob.

package app.demo.MyTouchApp{

	import flash.events.TUIO;// allows to connect to touchlib/tbeta
	import flash.events.TouchEvent;// allows to use TouchEvent Event Listeners
	import flash.events.Event;
	import flash.display.Sprite;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.filters.*;
	import flash.geom.*;

	public class SimpleDraw extends Sprite {

		private var paintBmpData:BitmapData;
		private var paintBmpData2:BitmapData;
		private var buffer:BitmapData;
		private var paintBmp:Bitmap;
		private var brush:Sprite;
		private var filter:BitmapFilter;
		private var filter2:BitmapFilter;
		public var col:ColorTransform;

		private var colorArray = new Array();

		public function SimpleDraw():void {

			//--------connect to TUIO-----------------
			TUIO.init(this,'localhost',3000,'',true);
			trace("TouchArrayInfo Initialized");

			paintBmpData=new BitmapData(1024,768,true,0x00000000);
			brush=new Sprite;
			brush.graphics.beginFill(0xDDDDDD);
			brush.graphics.drawCircle(0,0,25);
			col = new ColorTransform(0,0,0);
			paintBmp=new Bitmap(paintBmpData);
			var cmat:Array=[1,1,1,1,1,1,1,1,1];
			filter=new ConvolutionFilter(50,50,cmat,10,10,true);
			filter2=new BlurFilter(15,15);
			addChild(paintBmp);
			addEventListener(TouchEvent.MOUSE_DOWN,touchDown);
			addEventListener(Event.ENTER_FRAME,updateCall, false, 0, true);

		}
		public function updateCall(e:Event):void {
			for (var i=0; i < TUIO.returnBlobs().length; i++) {
				var localPt:Point=new Point(TUIO.returnBlobs()[i].x,TUIO.returnBlobs()[i].y);
				var m:Matrix=new Matrix;
				m.translate(localPt.x,localPt.y);
				paintBmpData.draw(brush,m,col);
			}
			paintBmpData.applyFilter(paintBmpData, paintBmpData.rect, new Point(), filter2);
		}
		public function touchDown(e:TouchEvent):void {
			col = new ColorTransform(randomNumber(0,1),randomNumber(0,1),randomNumber(0,1));
		}
		function randomNumber(low:Number=NaN,high:Number=NaN):Number {
			var low:Number=low;
			var high:Number=high;
			return Math.round(Math.random() * high - low) + low;
		}
	}
}

Only a few bits have changed now. A TouchEvent has been added for MOUSE_DOWN.

		public function touchDown(e:TouchEvent):void {
			col = new ColorTransform(randomNumber(0,1),randomNumber(0,1),randomNumber(0,1));
		}

and the Color Change has been taken out of the ENTER_FRAME listener.

Check it out. Color only changes when a new touchpoint is added! But wait... When you touch down, it changes ALL the touch points. Hmm, I want each touchpoint to have it's own random color until it's released from the screen. At least I hope that's what you'd want, cause that's what I'm gonna do.

Wrapping Up Color Coordination

The last thing to get this worked out the way we want is we need a way for each blob to know what color it's supposed to be. But, I don't want to just start adding new variables into the TUIOObject. I still want to keep what I'm going all contained in this one file.

What do we know we can get from the TUIOObject? The X, the Y and the ID...

And what kind of variable can contain multiple information at the same time based of some type of ID? The Array

So what if we make an array to hold the color of the TUIOObject at the array's container of that ID number. I hope that sentance made sense. I'll put is this way, we have blob ID 738 so in the array containing the new random colors colorArray[738] will have the color we want to use for that blob.

Note: So this is where I feel i start to fall on shakey ground. Because I've noticed after a while my blob's IDs are in the 10s of 1000s (20304 and rising). That means the array I speak of skips over all the ones before it and begins containing the information at slot 2030. I do not know how this effects the memory of the machine, or if a garbage collecting can happen, also if the flash is running for an enormous amount of time, would it lock up the system? But if i tried to use .push and .pop the ID and array slot would not be in sync. Any additional advise to my concern would be greatly appreciated. Admiting you have a problem is the first step, till then, I'll push forward. (^_^)//

Right. The Code!

package app.demo.MyTouchApp{

	import flash.events.TUIO;// allows to connect to touchlib/tbeta
	import flash.events.TouchEvent;// allows to use TouchEvent Event Listeners
	import flash.events.Event;
	import flash.display.Sprite;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.filters.*;
	import flash.geom.*;

	public class SimpleDraw extends Sprite {

		private var paintBmpData:BitmapData;
		private var paintBmpData2:BitmapData;
		private var buffer:BitmapData;
		private var paintBmp:Bitmap;
		private var brush:Sprite;
		private var filter:BitmapFilter;
		private var filter2:BitmapFilter;
		public var col:ColorTransform;

		private var colorArray = new Array();

		public function SimpleDraw():void {

			//--------connect to TUIO-----------------
			TUIO.init(this,'localhost',3000,'',true);
			trace("TouchArrayInfo Initialized");

			paintBmpData=new BitmapData(1024,768,true,0x00000000);
			brush=new Sprite;
			brush.graphics.beginFill(0xDDDDDD);
			brush.graphics.drawCircle(0,0,25);
			col = new ColorTransform(0,0,0);
			paintBmp=new Bitmap(paintBmpData);
			var cmat:Array=[1,1,1,1,1,1,1,1,1];
			filter=new ConvolutionFilter(50,50,cmat,10,10,true);
			filter2=new BlurFilter(15,15);
			addChild(paintBmp);
			addEventListener(TouchEvent.MOUSE_DOWN,touchDown);
			addEventListener(Event.ENTER_FRAME,updateCall, false, 0, true);

		}
		public function updateCall(e:Event):void {
			for (var i=0; i < TUIO.returnBlobs().length; i++) {
				var localPt:Point=new Point(TUIO.returnBlobs()[i].x,TUIO.returnBlobs()[i].y);
				var m:Matrix=new Matrix;
				m.translate(localPt.x,localPt.y);
				paintBmpData.draw(brush,m,colorArray[TUIO.returnBlobs()[i].thisID]);
			}
			paintBmpData.applyFilter(paintBmpData, paintBmpData.rect, new Point(), filter2);
		}
		public function touchDown(e:TouchEvent):void {
			col = new ColorTransform(randomNumber(0,1),randomNumber(0,1),randomNumber(0,1));
			colorArray[e.ID] = col;
		}
		function randomNumber(low:Number=NaN,high:Number=NaN):Number {
			var low:Number=low;
			var high:Number=high;
			return Math.round(Math.random() * high - low) + low;
		}
	}
}

You can see the colorArray is added into the touchDown Event.

		public function touchDown(e:TouchEvent):void {
			col = new ColorTransform(randomNumber(0,1),randomNumber(0,1),randomNumber(0,1));
			colorArray[e.ID] = col;
		}

And we can use the TouchEvent data easily to get the ID of the intended blob.

paintBmpData.draw(brush,m,colorArray[TUIO.returnBlobs()[i].thisID]);

Then in the updateCall function, the colorTransformation inside the colorArray array is found by using the ID of that TUIO blob.

Revision -- new Dictionary(true)

Remember that previous part where I wasn't sure how the array would work. Well, thanks you Seth (cerupcat) from NUIGroup (post here). Pointed me to a post on Grant Skinner's site about the AS3: Dictionary Object. It functions very much like an array, being able to set information based on a specific "key." So I tried it out, and here's what I came up with.

package app.demo.MyTouchApp{

	import flash.events.TUIO;// allows to connect to touchlib/tbeta
	import flash.events.TouchEvent;// allows to use TouchEvent Event Listeners
	import flash.events.Event;
	import flash.display.Sprite;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.filters.*;
	import flash.geom.*;
	import flash.utils.Dictionary;

	public class SimpleDraw extends Sprite {

		private var paintBmpData:BitmapData;
		private var paintBmpData2:BitmapData;
		private var buffer:BitmapData;
		private var paintBmp:Bitmap;
		private var brush:Sprite;
		private var filter:BitmapFilter;
		private var filter2:BitmapFilter;
		public var col:ColorTransform;

		private var colorArray = new Dictionary(true);

		public function SimpleDraw():void {

			//--------connect to TUIO-----------------
			TUIO.init(this,'localhost',3000,'',true);
			trace("TouchArrayInfo Initialized");

			paintBmpData=new BitmapData(1024,768,true,0x00000000);
			brush=new Sprite;
			brush.graphics.beginFill(0xDDDDDD);
			brush.graphics.drawCircle(0,0,25);
			col = new ColorTransform(0,0,0);
			paintBmp=new Bitmap(paintBmpData);
			var cmat:Array=[1,1,1,1,1,1,1,1,1];
			filter=new ConvolutionFilter(50,50,cmat,10,10,true);
			filter2=new BlurFilter(15,15);
			addChild(paintBmp);
			addEventListener(TouchEvent.MOUSE_DOWN,touchDown);
			addEventListener(TouchEvent.MOUSE_UP,touchUp);
			addEventListener(Event.ENTER_FRAME,updateCall, false, 0, true);

		}
		public function updateCall(e:Event):void {
			for (var i=0; i < TUIO.returnBlobs().length; i++) {
				var localPt:Point=new Point(TUIO.returnBlobs()[i].x,TUIO.returnBlobs()[i].y);
				var m:Matrix=new Matrix;
				m.translate(localPt.x,localPt.y);
				paintBmpData.draw(brush,m,colorArray[TUIO.returnBlobs()[i].thisID]);
			}
			paintBmpData.applyFilter(paintBmpData, paintBmpData.rect, new Point(), filter2);
		}
		public function touchDown(e:TouchEvent):void {
			col = new ColorTransform(randomNumber(0,1),randomNumber(0,1),randomNumber(0,1));
			colorArray[e.ID] = col;
		}
		public function touchUp(e:TouchEvent):void {
			colorArray[e.ID] = null;
		}
		function randomNumber(low:Number=NaN,high:Number=NaN):Number {
			var low:Number=low;
			var high:Number=high;
			return Math.round(Math.random() * high - low) + low;
		}
	}
}

It works just like the last bit of code, except this 'should' handle memory the best. Honestly, I'm kind of going of guessing because I'm not sure how to prove this is affective or not.

		private var colorArray = new Dictionary(true);

One important thing noted was the (true), this makes sure that the Dictionary's empty information can be garbage collected. Helping to keep the computer's memory clean and the program running smoothly.

		public function touchUp(e:TouchEvent):void {
			colorArray[e.ID] = null;
		}

And since once the touch point is released, the blob's ID will never return again, we can null that Dictionary's bit of information make it ready for garbage collecting.

I hope you've enjoyed this bit of coding. You can see my experiment collection at: http://code.google.com/p/multitouchas3experiments/