Actionscript:
-
var canvas:BitmapData = new BitmapData(stage.stageWidth,stage.stageHeight,false, 0xCCCCCC);
-
addChild(new Bitmap(canvas));
-
var prevX:Number;
-
var prevY:Number;
-
var brush:Shape = new Shape();
-
-
stage.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
-
stage.addEventListener(MouseEvent.MOUSE_UP, onUp);
-
function onDown(evt:MouseEvent):void{
-
prevX = mouseX;
-
prevY = mouseY;
-
addEventListener(Event.ENTER_FRAME, onLoop);
-
}
-
-
function onUp(evt:MouseEvent):void{
-
removeEventListener(Event.ENTER_FRAME, onLoop);
-
}
-
-
function onLoop(evt:Event):void {
-
brush.x = mouseX;
-
brush.y = mouseY;
-
with (brush.graphics){
-
clear();
-
lineStyle(3, 0x000000);
-
lineTo(prevX-mouseX, prevY-mouseY);
-
}
-
canvas.draw(brush, brush.transform.matrix);
-
prevX = mouseX;
-
prevY = mouseY;
-
}
If you write a drawing program with the Graphics class alone, you'll notice that eventually the flash player will start to slow down. This could take quite some time, but it will eventually happen. Because the Graphics class is entirely vector based, the flash player needs to keep track of every single point that makes up any line in your drawing. If you draw something and then erase it by drawing white vector lines over it, flash still needs to know about that thing that you erased.
The snippet I wrote yesterday reminded me of the technique used in the above snippet. In yesterdays post every line is drawn using the graphics class. As a result, eventually the flash player begins to choke.
Today's snippet draws a vector between the previous mouse location and the current mouse location - this vector is then drawn onto a BitmapData object and the vector is cleared. So rather than creating vector graphics that are continuously increasing in complexity, your just changing the color of pixels around on a BitmapData.
To see a visual explanation take a look at the below swf. I slowed it down to 5fps, I tinted the vector red and scaled up the stage so you can differentiate between what's vector and what's bitmap:
Click to see the swf...

Actionscript:
-
var canvas:Shape = Shape(addChild(new Shape()));
-
var gestures:Array=[];
-
var gestureNum:int = 0;
-
var capGesture:Array;
-
stage.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
-
stage.addEventListener(MouseEvent.MOUSE_UP, onUp);
-
function onDown(evt:MouseEvent):void{
-
capGesture=[];
-
addEventListener(Event.ENTER_FRAME, onCapture);
-
-
canvas.graphics.lineStyle(3, 0xFF0000);
-
canvas.x = mouseX;
-
canvas.y = mouseY;
-
canvas.graphics.moveTo(0, 0);
-
}
-
function onUp(evt:MouseEvent):void{
-
gestures.push(capGesture.concat());
-
gestureNum++;
-
canvas.graphics.clear();
-
removeEventListener(Event.ENTER_FRAME, onCapture);
-
}
-
function onCapture(evt:Event):void{
-
capGesture.push(new Point(canvas.mouseX, canvas.mouseY));
-
canvas.graphics.lineTo(canvas.mouseX, canvas.mouseY);
-
}
-
-
var currGesture:Array;
-
var drawing:Boolean = false;
-
var lineThickness:Number = 0;
-
var lineColor:Number = 0x000000;
-
var index:int = 0;
-
var pnt:Point;
-
var trans:Matrix = new Matrix();
-
var i:int
-
addEventListener(Event.ENTER_FRAME, onLoop);
-
function onLoop(evt:Event):void{
-
if (gestureNum> 0){
-
if (!drawing){
-
currGesture = gestures[int(Math.random() * gestureNum)].concat();
-
trans.identity();
-
trans.rotate(Math.random()*6.28);
-
var scale:Number = Math.random() * 2 + .1;
-
trans.scale(scale, scale);
-
trans.tx = Math.random() * stage.stageWidth
-
trans.ty = Math.random() * stage.stageHeight
-
for (i = 0; i<currGesture.length; i++){
-
currGesture[i] = trans.transformPoint(currGesture[i]);
-
}
-
lineThickness = Math.random() * Math.random() * 50;
-
if (int(Math.random()*10) ==1){
-
var col:uint = uint(Math.random()*255);
-
lineColor = col <<16 | col <<8 | col;
-
}
-
index = 0;
-
drawing = true;
-
graphics.lineStyle(lineThickness, lineColor);
-
}else{
-
for (i = 0; i<10; i++){
-
if (drawing == true){
-
pnt = currGesture[index];
-
if (index == 0){
-
graphics.moveTo(pnt.x, pnt.y);
-
}else{
-
graphics.lineTo(pnt.x, pnt.y);
-
}
-
index++;
-
if (index == currGesture.length){
-
drawing = false;
-
}
-
}
-
}
-
}
-
}
-
}
This snippet is an idea I have been meaning to try for sometime. It's a mini-drawing program. You can draw single gestures (shapes, letters etc...) and the program then randomly scales, rotates, tints and translates these gestures repeatedly on the canvas. You can continue to draw as it does this, the more gestures you draw, the more the program will have to randomly choose from.
Have a look at the swf here...

Actionscript:
-
[SWF(width=500, height=500)]
-
var canvasSize:int=stage.stageWidth;
-
var canvas:BitmapData=new BitmapData(canvasSize,canvasSize,false,0x000001);
-
addChild(new Bitmap(canvas, "auto", true));
-
var size:int=canvas.width*canvas.height - canvasSize;
-
var pixels:Vector.<uint>=canvas.getVector(canvas.rect);
-
for (var i:int = 0; i<canvasSize; i++) {
-
var xp:int=int(Math.random()*canvasSize);
-
var yp:int=int(Math.random()*canvasSize);
-
pixels[xp+yp*canvasSize]=0xFF000000;
-
}
-
var targetCol:uint=0xFF000000;
-
var buffer:Vector.<uint>=pixels.concat();
-
var fade:uint=1;
-
-
addEventListener(Event.ENTER_FRAME, onLoop);
-
function onLoop(evt:Event):void {
-
var curr:uint=targetCol;
-
var r:int = (curr>> 16) & 0xFF;
-
var g:int = (curr>> 8) & 0xFF;
-
var b:int=curr&0xFF;
-
r+=fade;
-
g+=fade;
-
b+=fade;
-
if (r>255) r=255;
-
if (g>255) g=255;
-
if (b>255) b=255;
-
var darker:uint=0xFF000000|r<<16|g<<8|b;
-
if (darker==0xFFFFFFFF) {
-
removeEventListener(Event.ENTER_FRAME, onLoop);
-
}
-
for (var i:int = canvasSize; i<size; i++) {
-
curr=pixels[i];
-
if (curr==targetCol) {
-
var index:int=i-canvasSize+int(Math.random()*3) - 1;
-
if (index>0) buffer[index]=darker;
-
if (int(Math.random()*50)==1) {
-
index=i-canvasSize+int(Math.random()*3)-1;
-
if (index>0) buffer[index]=darker;
-
}
-
}
-
}
-
targetCol=darker;
-
canvas.lock();
-
canvas.setVector(canvas.rect, buffer);
-
pixels=buffer.concat();
-
canvas.unlock();
-
}
This snippet uses setVector() to draw something that looks like this:

This is a cellular automaton. It has kind of a strange rule set - but you could easily use this snippet to do more traditional cellular automata.
Actionscript:
-
var canvasSize:int = 400;
-
var canvas:BitmapData = new BitmapData(canvasSize, canvasSize, false, 0xFFFFFF);
-
addChild(new Bitmap(canvas));
-
var size:int = canvas.width * canvas.height;
-
var pixels:Vector.<uint> = canvas.getVector(canvas.rect);
-
-
addEventListener(Event.ENTER_FRAME, onLoop);
-
function onLoop(evt:Event):void {
-
for (var i:int = 0; i<500; i++){
-
fillCircle(int(Math.random() * canvasSize),
-
int(Math.random() * canvasSize),
-
int(Math.random() * 5 + 3),
-
uint(Math.random() * 0xFFFF));
-
}
-
canvas.lock();
-
canvas.setVector(canvas.rect, pixels);
-
canvas.unlock();
-
}
-
-
function fillCircle(xp:int,yp:int, radius:int, col:uint = 0x000000):void {
-
var xoff:int =0;
-
var yoff:int = radius;
-
var balance:int = -radius;
-
while (xoff <= yoff) {
-
var p0:int = xp - xoff;
-
var p1:int = xp - yoff;
-
var w0:int = xoff + xoff;
-
var w1:int = yoff + yoff;
-
hLine(p0, yp + yoff, w0, col);
-
hLine(p0, yp - yoff, w0, col);
-
hLine(p1, yp + xoff, w1, col);
-
hLine(p1, yp - xoff, w1, col);
-
if ((balance += xoff++ + xoff)>= 0) {
-
balance-=--yoff+yoff;
-
}
-
}
-
}
-
function hLine(xp:int, yp:int, w:int, col:uint):void {
-
var index:int = xp + yp * canvasSize;
-
for (var i:int = 0; i <w; i++){
-
index++;
-
if (index> -1 && index <size){
-
pixels[index] = col;
-
}
-
}
-
}
In the past I've posted examples of Bresenham's Circle (here and here). Both of those examples make use of setPixel(). Today's snippet demos a version of Bresenham's Circle that works with setVector().
Actionscript:
-
[SWF(width=401,height=401,background=0xEFEFEF)]
-
-
var w:Number = stage.stageWidth-1;
-
var h:Number = stage.stageHeight-1;
-
var tileSize:Number = 20;
-
var halfTileSize:Number = 20;
-
var hTiles:Number = w / tileSize;
-
var vTiles:Number = h / tileSize;
-
var world:Shape = Shape(addChild(new Shape()));
-
var map:Array=[];
-
populateMap();
-
var gridColor:uint = 0xCCCCCC;
-
grid(tileSize, gridColor);
-
-
vTiles -= 1;
-
var movers:Array = [];
-
for (var i:int = 0; i<100; i++){
-
movers.push(makeMover(i % hTiles, int( i / hTiles),0x000000))
-
movers.push(makeMover(i % hTiles, vTiles - int( i / hTiles),0xFF0000))
-
}
-
var moverNum:int = movers.length;
-
hTiles -= 1;
-
-
addEventListener(Event.ENTER_FRAME, onLoop);
-
function onLoop(evt:Event):void {
-
world.graphics.clear();
-
for (var i:int = 0; i<moverNum; i++){
-
movers[i]();
-
}
-
}
-
function populateMap():void{
-
for (var i:int = 0; i<vTiles; i++){
-
map[i] = [];
-
for (var j:int = 0; j<hTiles; j++){
-
map[i][j] = 0;
-
}
-
}
-
}
-
function grid(size:Number=30, lineColor:uint=0xFFFF00, lineAlpha:Number=1):void {
-
with(graphics){
-
lineStyle(0, lineColor, lineAlpha);
-
drawRect(0,0,w,h);
-
for (var i:Number = size; i<w; i+=size) {
-
moveTo(i, 0);
-
lineTo(i, w);
-
}
-
for (i = size; i<h; i+=size) {
-
moveTo(0, i);
-
lineTo(h, i);
-
}
-
}
-
}
-
function makeMover(x:Number, y:Number, col:uint):Function{
-
var xp:Number = x;
-
var yp:Number = y;
-
var prevX:Number = x;
-
var prevY:Number = y;
-
map[yp][xp] = 1;
-
var dx:Number = xp;
-
var dy:Number = yp;
-
var counter:int = 0;
-
return function():void{
-
if (counter> 20){
-
if (int(Math.random()*30) == 1){
-
xp += int(Math.random()*2) - 1 | 1;
-
xp = xp <0 ? 0 : xp;
-
xp = xp> hTiles ? hTiles : xp;
-
if (map[yp][xp] == 1){
-
xp = prevX;
-
}else{
-
map[prevY][prevX] = 0;
-
map[yp][xp] = 1;
-
counter = 0;
-
}
-
prevX = xp;
-
}else
-
if (int(Math.random()*30) == 1){
-
yp += int(Math.random()*2) - 1 | 1;
-
yp = yp <0 ? 0 : yp;
-
yp = yp> vTiles ? vTiles : yp;
-
if (map[yp][xp] == 1){
-
yp = prevY;
-
}else{
-
map[prevY][prevX] = 0;
-
map[yp][xp] = 1;
-
counter = 0;
-
}
-
prevY = yp;
-
}
-
}
-
counter++;
-
dx += (xp - dx) * 0.5;
-
dy += (yp - dy) * 0.5;
-
with(world.graphics){
-
lineStyle(0, gridColor,1, true)
-
beginFill(col);
-
drawRect(dx * tileSize, dy * tileSize, tileSize, tileSize);
-
}
-
}
-
}
This (somewhat long) snippet moves boxes around on a grid - the boxes avoid one another by reading values in a 2D array. This technique can also be used for collision detection in tile-based games.
Have a look at the swf here...

Actionscript:
-
[SWF(width=800, height=600)]
-
var xn1:Number;
-
var xn:Number = Math.random() * Math.random() * .2;
-
var inc:int = 0;
-
var xp:Number = 10;
-
var yp:Number = 10;
-
var count:int = 1;
-
scaleX = scaleY = 2;
-
graphics.lineStyle(0,0x00000);
-
addEventListener(Event.ENTER_FRAME, onLoop);
-
function onLoop(evt:Event):void {
-
-
xn1 = 2 * xn % 1;
-
xn = xn1;
-
if (inc == 0){
-
graphics.moveTo(xp + inc, yp + 30 - xn1 * 30);
-
}else{
-
graphics.lineTo(xp + inc, yp + 30 - xn1 * 30);
-
}
-
inc++
-
if (inc == 50){
-
inc = 0;
-
xp = 10 + count % 6 * 60;
-
yp = 10 + int(count / 6) * 60;
-
xn = Math.random() * Math.random() * .2;
-
trace(xn);
-
count++;
-
}
-
}
This snippet plots 2x mod 1 maps with random starting values for xn. More info over at wikipedia and mathworld.
Actionscript:
-
var rand:Number = Math.random() * Math.random() * Math.random();
This is a trick I use when I need more contrast in my random numbers. In this case, the variable rand will get closer to the number 1 significantly less frequently than if you just used Math.random() once.
To illustrate this I created this snippet:
Actionscript:
-
[SWF(width=800, height=600)]
-
-
var r:Number = Math.random() * Math.random() * Math.random();
-
var inc:int = 0;
-
var xp:Number = 10;
-
var yp:Number = 10;
-
var count:int = 1;
-
-
var compare:Shape = Shape(addChild(new Shape()));
-
compare.graphics.lineStyle(0,0x2222FF);
-
-
graphics.lineStyle(0,0x00000);
-
scaleX = scaleY = 2;
-
-
addEventListener(Event.ENTER_FRAME, onLoop);
-
function onLoop(evt:Event):void {
-
-
r = Math.random() * Math.random() * Math.random();
-
-
if (inc == 0){
-
graphics.moveTo(xp + inc, yp + 30 - r * 30);
-
}else{
-
graphics.lineTo(xp + inc, yp + 30 - r * 30);
-
}
-
-
r = Math.random();
-
if (inc == 0){
-
compare.graphics.moveTo(xp + inc, yp + 70 - r * 30);
-
}else{
-
compare.graphics.lineTo(xp + inc, yp + 70 - r * 30);
-
}
-
inc++;
-
if (inc == 50){
-
inc = 0;
-
xp = 10 + count % 6 * 60;
-
yp = 10 + int(count / 6) * 80;
-
r = Math.random()*Math.random();
-
count++;
-
}
-
}
The blue lines plots normal Math.random() and the black lines plots Math.random()*Math.random()*Math.random()

Actionscript:
-
import com.actionsnippet.qbox.*;
-
-
var sim:QuickBox2D = new QuickBox2D(this);
-
-
sim.createStageWalls({lineAlpha:0,fillColor:0x000000})
-
sim.addBox({x:3, y:3, width:3, height:3, skin:BoxSkin});
-
sim.addCircle({x:3, y:8,radius:1.5, skin:CircleSkin});
-
sim.addPoly({x:6, y:3, verts:[[1.5,0,3,3,0,3,1.5,0]], skin:TriangleSkin});
-
-
sim.addBox({x:6, y:3, width:3, height:3, skin:BoxSkin});
-
sim.addCircle({x:6, y:8,radius:1.5, skin:CircleSkin});
-
sim.addPoly({x:12, y:3, verts:[[1.5,0,3,3,0,3,1.5,0]], skin:TriangleSkin});
-
-
sim.start();
-
sim.mouseDrag();
You'll need this fla to run this snippet since the graphics are in the library. This snippet shows how to easily use linkage classes as the graphics for your rigid bodies. This was actually one of the first features I implemented in QuickBox2D.
Take a look at the swf here...

Actionscript:
-
import com.actionsnippet.qbox.*;
-
import Box2D.Common.Math.*;
-
-
var sim:QuickBox2D = new QuickBox2D(this);
-
-
sim.createStageWalls();
-
-
createTraveler(3, 3);
-
-
addObstacles();
-
-
function addObstacles():void{
-
sim.setDefault({groupIndex:-1, density:0, height:0.4});
-
sim.addBox({x:6, y:5, width:8, angle:0.17})
-
sim.addBox({x:7, y:7.1, width:8, angle:-0.17})
-
sim.addBox({x:5, y:9.1, width:8, angle:0.10})
-
sim.addBox({x:6, y:11.5, width:8.9, angle:-0.20})
-
sim.addBox({x:5.5, y:16, width:9, angle:0.20})
-
sim.addBox({x:5.5, y:18, width:9})
-
sim.addCircle({x:11, y:20, radius:2.5, groupIndex:1});
-
sim.addBox({x:16, y:19, width:2, height:2, angle:0.0, groupIndex:1})
-
}
-
-
function createTraveler(x:Number, y:Number):QuickObject{
-
var parts:Array = [];
-
parts[0] = sim.addCircle({x:0, y:1, radius:0.25, friction:0.01});
-
parts[1] = sim.addCircle({x:0, y:3, radius:0.25, friction:0.01});
-
parts[2] = sim.addBox({x:0, y:2, width:0.3, height:1.5 , groupIndex:-1});
-
return sim.addGroup({objects:parts, x:x, y:y });
-
}
-
-
sim.start();
-
sim.mouseDrag();
One of the more advanced and useful properties of rigid bodies is the groupIndex. It allows you to specify which rigid bodies collide with one another and which rigid bodies pass through one another. This snippit demo's the groupIndex property. For more information take a look at what the Box2D manual says.
Take a look at the swf here.
Actionscript:
-
[SWF(width=560,height=300,backgroundColor=0x000000,frameRate=30)]
-
-
var key:Object = new Object();
-
var alphabet:Array = "abcdefghijklmnopqrstuvwxyz".split("");
-
-
var num:Number = alphabet.length;
-
var step:Number = 360 / num;
-
-
var colors:Object = new Object();
-
for (var i:int = 0; i<num; i++){
-
var index:String = alphabet[i];
-
key[index] = 65 + i;
-
var c:Array = hsv(i * step, 1, 1);
-
colors[index] = c[0] <<16 | c[1] <<8 | c[2];
-
}
-
alphabet.push("32");
-
num++;
-
key["32"] = 32;
-
colors["32"] = 0x333333;
-
x = y = 10;
-
var count:int = 0;
-
var size:int = 20;
-
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyPressed);
-
function onKeyPressed(evt:KeyboardEvent):void{
-
for (var i:int= 0; i<num; i++){
-
var index:String = alphabet[i];
-
if (index == "32"){
-
trace("hi", evt.keyCode, key[index]);
-
}
-
if (evt.keyCode == key[index]){
-
graphics.beginFill(colors[index]);
-
var xp:int = count % num * size;
-
var yp:int = int(count / num) * size;
-
graphics.drawRect(xp, yp, size, size);
-
count++;
-
}
-
}
-
}
-
// ported from here:
-
//http://www.cs.rit.edu/~ncs/color/t_convert.html
-
function hsv(h:Number, s:Number, v:Number):Array{
-
var r:Number, g:Number, b:Number;
-
var i:int;
-
var f:Number, p:Number, q:Number, t:Number;
-
if (s == 0){
-
r = g = b = v;
-
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
-
}
-
h /= 60;
-
i = Math.floor(h);
-
f = h - i;
-
p = v * (1 - s);
-
q = v * (1 - s * f);
-
t = v * (1 - s * (1 - f));
-
switch( i ) {
-
case 0:
-
r = v, g = t, b = p;
-
break;
-
case 1:
-
r = q, g = v, b = p;
-
break;
-
case 2:
-
r = p, g = v, b = t;
-
break;
-
case 3:
-
r = p, g = q, b = v;
-
break;
-
case 4:
-
r = t, g = p, b = v;
-
break;
-
default: // case 5:
-
r = v, g = p, b = q;
-
break;
-
}
-
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
-
}
This snippet is a typing experiment - for every letter, you type a box filled with a specific color is drawn to the stage. The color associated with each letter is determined by moving through hsv color space - so typing an alphabet will end up with something resembling a spectrum.