Browse the notebook

Category: Vector

drawTriangles() 2D Textured Plane

[SWF(width = 700, height = 700)]

var loader:Loader = new Loader();
loader.load(new URLRequest("https://actionsnippet.com/wp-content/chair.jpg"));
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaded);
var tex:BitmapData;
function onLoaded(evt:Event):void{
	tex = Bitmap(loader.content).bitmapData;
	addEventListener(Event.ENTER_FRAME, onLoop);
}

var plane:Shape = Shape(addChild(new Shape()));
plane.x = plane.y = 50;

var verts:Vector.<Number> = new Vector.<Number>();
var uvs:Vector.<Number> = new Vector.<Number>();
var indices:Vector.<int> = new Vector.<int>();
var rows:int = 30;
var size:Number = rows + 1;
var vertNum:Number = size * size;
var polySize:Number = 20;
var vIndex:int = 0;
var uvIndex:int = 0;
var gridSize:Number = rows * polySize;
for (var i:Number = 0; i<vertNum; i++){
	var xp:Number = i % size * polySize;
	var yp:Number =  int(i / size) * polySize;
	verts[vIndex++] = xp
	verts[vIndex++] = yp;
	uvs[uvIndex++] = xp / gridSize;
	uvs[uvIndex++] = yp / gridSize;
	if (i % size != rows){
		  indices.push(i, i+1, i+size, i+size, i+size+1, i+1);
	}
}

// render and show that the verts can be changed around
function onLoop(evt:Event):void {
	// shake the verts
	for (var i:int = 0; i<verts.length; i++){
		verts[i] += Math.random() - 0.5;
	}
	with(plane.graphics){
		clear();
		beginBitmapFill(tex,null, false, true);
		drawTriangles(verts, indices,uvs);
	}
}

This snippet is shows how to draw a textured 2D plane using drawTriangles(). This is similar to the wireframe 2D plane post from yesterday… the main difference is that you need to calculate UV coordinates if you want to use a bitmap fill with drawTriangles().

Here is a still of this snippet:

Read snippet: drawTriangles() 2D Textured Plane

Wireframe Plane 2D

var plane:Shape = Shape(addChild(new Shape()));
plane.x = plane.y = 50;

var verts:Vector.<Number> = new Vector.<Number>();
var indices:Vector.<int> = new Vector.<int>();
var rows:int = 20;
var size:Number = rows + 1;
var vertNum:Number = size * size;
var polySize:Number = 20;
var vIndex:int = 0;
for (var i:Number = 0; i<vertNum; i++){
	verts[vIndex++] = i % size * polySize;
	verts[vIndex++] = int(i / size) * polySize;
	if (i % size != rows){
		  indices.push(i, i+1, i+size, i+size, i+size+1, i+1);
	}
}

// render and show that the verts can be changed around
addEventListener(Event.ENTER_FRAME, onLoop);
function onLoop(evt:Event):void {
	// shake the verts
	for (var i:int = 0; i<verts.length; i++){
		verts[i] += Math.random() - 0.5;
	}
	with(plane.graphics){
		clear();
		lineStyle(0,0);
		drawTriangles(verts, indices);
	}
}

This snippet shows a simple way to draw a 2D wireframe plane using drawTriangles(). To demonstrate the flexibility of the plane… the vertices are randomly shaken on enterframe.

Read snippet: Wireframe Plane 2D

drawTriangles() Terrain

[SWF(width=500,height=500,backgroundColor=0x333333, frameRate=40)]
var terrain:Shape = Shape(addChild(new Shape()));
terrain.x = terrain.y = 250;
var rows:int = 60;
var size:int = rows + 1;
var vertNum:int = size * size;
var polySize:Number = 5;
var gridWidth:Number = polySize * rows;
var halfWidth:Number = gridWidth / 2;
var verts:Vector.<Number> = new Vector.<Number>();
var pVerts:Vector.<Number> = new Vector.<Number>();
var indices:Vector.<int> = new Vector.<int>();
var uvs:Vector.<Number> = new Vector.<Number>();
var uvts:Vector.<Number> = new Vector.<Number>();
var tex:BitmapData = new BitmapData(gridWidth, gridWidth, false, 0x000000);
var pix:int = gridWidth * gridWidth;
var perlin:BitmapData = new BitmapData(gridWidth, gridWidth, false, 0x000000);
// generate the texture and the terrain
function generate():void{
	tex.fillRect(tex.rect, 0x000000);
	var i:Number, xp:Number, yp:Number;
	for (i = 0; i<pix; i++){
		xp = i % gridWidth;
		yp= int(i / gridWidth);
		var dx:Number = xp - gridWidth / 2;
		var dy:Number = yp - gridWidth / 2;
		var d:Number = 255 - Math.sqrt(dx * dx + dy * dy) / halfWidth* 255;
		if (d <0) d = 0;
		if (d> 255) d = 255;
		var c:uint = uint(d);
		tex.setPixel(xp, yp, c <<16 | c <<8 | c);
	}
	perlin.perlinNoise(100,100,3,Math.random()*100,false,false,7,true);
	perlin.draw(perlin, null, null, BlendMode.SCREEN);
	tex.draw(perlin, null, null, BlendMode.MULTIPLY);
	// calculate verts, uvs and indices
	var vIndex:int = 0;
	var uvIndex:int = 0;
	indices = new Vector.<int>();
	for (i = 0; i<vertNum; i++){
		var xMod:Number = i % size;
		xp = xMod * polySize;
		yp = int(i / size) * polySize;
		verts[vIndex++] = xp - halfWidth;
		verts[vIndex++] = yp - halfWidth;
		verts[vIndex++] = tex.getPixel(xp, yp) & 0xFF;
		uvs[uvIndex++] = xp /  gridWidth;
		uvs[uvIndex++] = yp / gridWidth;
		if (xMod != rows){
			  indices.push(i, i+1, i+size, i+1, i+size+1, i+size);
		}
	}
}
generate();
stage.addEventListener(MouseEvent.MOUSE_DOWN, onGenerate);
function onGenerate(evt:MouseEvent):void{ generate() };
var m:Matrix3D = new Matrix3D();
var rot:Number = 0;
addEventListener(Event.ENTER_FRAME, onLoop);
function onLoop(evt:Event):void {
		m.identity();
		var destRot:Number = mouseX / stage.stageWidth * 90;
		if (destRot <0) destRot = 0;
		if (destRot> 90) destRot = 90;
		rot += (destRot - rot) * 0.2;
		m.appendRotation(rot,Vector3D.Z_AXIS);
		m.appendRotation(60,Vector3D.X_AXIS);
		Utils3D.projectVectors(m, verts, pVerts, uvts);
		with(terrain.graphics){
			clear();
			beginBitmapFill(tex, null, false, true);
			drawTriangles(pVerts, indices, uvs, TriangleCulling.NEGATIVE);
		}
}

This snippet draws a pretty simple isometric terrain using fp10 graphics stuff and perlin noise.


Have a look at the swf here…

Read snippet: drawTriangles() Terrain

Variable Line Resolution

var loc:Vector.<Point> = new Vector.<Point>();
var lifts:Vector.<int> = new Vector.<int>();
var index:int = 0;
var resolution:int = 1;
var down:Boolean;
stage.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
stage.addEventListener(MouseEvent.MOUSE_UP, onUp);
stage.addEventListener(KeyboardEvent.KEY_UP, onKeyReleased);
addEventListener(Event.ENTER_FRAME, onLoop);
function onKeyReleased(evt:KeyboardEvent):void{
	if (evt.keyCode == Keyboard.RIGHT){
		resolution -= 1;
		if (resolution < 1) resolution = 1;
	}else 
	if (evt.keyCode == Keyboard.LEFT){
		resolution += 1;
	}
}
function onDown(evt:MouseEvent):void{
	down = true;
	lifts.push(index);
}
function onUp(evt:MouseEvent):void{
	down = false;
}
function onLoop(evt:Event):void {
	if (down){
		loc[index] = new Point(mouseX, mouseY);
		index++;
	}
	graphics.clear();
	graphics.lineStyle(0,0x000000);
	var liftIndex:int = 0;
	for (var i:int = 0; i<loc.length; i++){
		if (liftIndex == lifts.length) liftIndex = 0;
		if (i == lifts[liftIndex]){
			graphics.moveTo(loc[i].x, loc[i].y);
		    liftIndex++;
		}else{
			if (i % resolution == 1 || resolution == 1){
				graphics.lineTo(loc[i].x, loc[i].y);
			}
		}
	}
}

This snippet shows a simple approach to creating variable resolution graphics.

Below is a drawing done using this snippet. The left arrow key is used to decrease resolution and the right arrow key is used to increase resolution:


Click to view swf…

Read snippet: Variable Line Resolution

Utils3D.projectVectors() Lathe

[SWF(width = 500, height = 500, backgroundColor = 0x000000)]
var halfWidth:Number = stage.stageWidth / 2;
var halfHeight:Number = stage.stageHeight / 2;
var loc:Vector.<Number>;

graphics.lineStyle(0, 0xFF0000);
graphics.moveTo(halfWidth, 0);
graphics.lineTo(halfWidth, stage.stageHeight);
graphics.moveTo(0, halfHeight);
graphics.lineTo(stage.stageWidth, halfHeight);

var line:Shape = Shape(addChild(new Shape()));
line.x = halfWidth;

var idle:Function = function(){};
var currentMode:Function = idle;
stage.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
stage.addEventListener(MouseEvent.MOUSE_UP, onUp);
addEventListener(Event.ENTER_FRAME, onLoop);
function onDown(evt:MouseEvent):void{
	if (contains(frame)){
		removeChild(frame);
		currentMode = idle;
		line.graphics.clear();
		return;
	}
	loc = new Vector.<Number>();
	line.graphics.lineStyle(0,0xFFFFFF);
	line.x = halfWidth
	line.y = halfHeight;
	line.graphics.moveTo(line.mouseX, line.mouseY);
	currentMode = captureLocs;
	canvas.fillRect(canvas.rect, 0x000000);
}
function onUp(evt:MouseEvent):void{
	if (currentMode == idle) return;
	setupLathe();
	currentMode = showLathe;
}

function onLoop(evt:Event):void{
	currentMode();
}
function captureLocs():void{
	loc.push(line.mouseX);
	loc.push(line.mouseY);
	loc.push(0);
	line.graphics.lineTo(line.mouseX, line.mouseY);
}
/**
 -- Lathe Stuff:
*/
var canvas:BitmapData = new BitmapData(stage.stageWidth, stage.stageHeight,false, 0x000000);
var frame:Bitmap = new Bitmap(canvas);
var dx:Number=0;
var dy:Number=0;
 var matrix:Matrix3D = new Matrix3D();
var pVerts:Vector.<Number>;
var uvts:Vector.<Number>;
function setupLathe():void{
	addChild(frame);
	pVerts =  new Vector.<Number>();
	uvts = new Vector.<Number>();
	var nVerts:Vector.<Number> = new Vector.<Number>();
	var tVerts:Vector.<Number> = new Vector.<Number>();
	matrix.identity();
	var step:Number = 2;
	for (var i:int = 0; i <360; i+=step){
		matrix.appendRotation(step,Vector3D.Y_AXIS);
		matrix.transformVectors(loc, tVerts);
		nVerts = nVerts.concat(tVerts);
	}
	loc = nVerts.concat();
}
function showLathe():void{
	dx += (mouseX - dx)/4;
	dy += (mouseY - dy)/4;
	matrix.identity();
	matrix.appendRotation(dy,Vector3D.X_AXIS);
	matrix.appendRotation(dx,Vector3D.Y_AXIS);
	matrix.appendTranslation(halfWidth, halfHeight, 0);
	Utils3D.projectVectors(matrix, loc, pVerts, uvts);
	canvas.lock();
	canvas.fillRect(canvas.rect, 0x000000);
	var leng:int = pVerts.length;
	for (var i:int = 0; i<leng; i+=2){
		canvas.setPixel( pVerts[i], pVerts[i + 1], 0xFFFFFF);
	}
	canvas.unlock();
}

This snippet allows you to create 3D lathe shapes by drawing a 2D line. This is done using Utils3D.projectVectors() and Matrix.transformVectors().

Have a look at the swf here…

Read snippet: Utils3D.projectVectors() Lathe

setVector() CA Texture

[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.

Read snippet: setVector() CA Texture

x and y Coordinates in 1D Array / Vector

var canvas:BitmapData=new BitmapData(400,400,false,0x000000);
addChild(new Bitmap(canvas));
var pix:Vector.<uint>=canvas.getVector(canvas.rect);

canvas.lock();
for (var i:int = 0; i<300; i++) {
	var xp:int=50+i;
	var yp:int=50+i/2;
	// target x and y coords in 1D array
	pix[xp+yp*400]=0xFFFFFF;
}
canvas.setVector(canvas.rect, pix);
canvas.unlock();

This snippet shows how to target x and y coordinates in a 1D Array / Vector. This can be useful sometimes when working with setVector().

This is sort of like re-inventing setPixel()…. and for that reason is kind of pointless – that said, it’s interesting to know. I first learned about this technique from using processing.

Read snippet: x and y Coordinates in 1D Array / Vector

IGraphicsData Example

// note the high framerate for testing purposes
[SWF(width = 800, height = 600, frameRate=60)]
// red, yellow, blue
var fills:Vector.<IGraphicsData> = Vector.<IGraphicsData>([new GraphicsSolidFill(0xFF0000),  new GraphicsSolidFill(0xFFCC00), new GraphicsSolidFill(0x0033FF)]);

var stroke:IGraphicsData = new GraphicsStroke();

var cmds:Vector.<int> = new Vector.<int>();
var ci:int = 0;
var dat:Vector.<Number> = new Vector.<Number>();
var di:int = 0;
var igraph:Vector.<IGraphicsData> = new Vector.<IGraphicsData>();
var ig:int  = 0;
var path:Vector.<GraphicsPath> = new Vector.<GraphicsPath>();
var boxNum:int = 1500;
var locX:Vector.<Number> = new Vector.<Number>(boxNum);
var locY:Vector.<Number> = new Vector.<Number>(boxNum);
var velX:Vector.<Number> = new Vector.<Number>(boxNum);
var velY:Vector.<Number> = new Vector.<Number>(boxNum);
var cols:int = 50;
for (var i:int = 0; i<boxNum; i++){
	path[i] = new GraphicsPath(new Vector.<int>(), new Vector.<Number>());
	path[i].winding = GraphicsPathWinding.NON_ZERO;
	var theta:Number = i * Math.PI/180;
	var sin:Number = Math.cos(theta);
	var cos:Number =  Math.sin(theta);
	var r:Number = Math.random()*100;
	var vr:Number = r * 0.05;
	velX[i] = vr * cos;
	velY[i] = vr * sin;
	locX[i] = 400 + r * cos;
    locY[i] = 300 + r * sin;
}

// box vars
var xp:Number, yp:Number, size:Number, hs:Number;
// corners
var x0:Number, y0:Number, x1:Number, y1:Number;

addEventListener(Event.ENTER_FRAME, onLoop);
function onLoop(evt:Event):void {
	graphics.clear();
	ig = 0;
	for (i= 0; i<boxNum; i++){
		// inline function:
		locX[i] += velX[i];
		locY[i] += velY[i];
		xp = locX[i];
		yp = locY[i];
		if (xp < 0 || xp > 800){
			velX[i] *= -1;
		}
		if (yp < 0 || yp > 600){
			velY[i] *= -1;
		}
		size = 10 + i % 10;
		hs = size * 0.5;
		x0 = xp - hs, y0 = yp - hs;
		x1 = xp + hs, y1 = yp + hs;
		ci = 0;
		di = 0;
		cmds = path[i].commands;
		dat = path[i].data;
		// GraphicsPathCommand.MOVE_TO
		cmds[ci++] = 1;
		dat[di++] = x0 , dat[di++] = y0;
		// GraphicsPathCommand.LINE_TO
		cmds[ci++] = 2;
		dat[di++] = x1 , dat[di++] = y0;
		// GraphicsPathCommand.LINE_TO
		cmds[ci++] = 2;
		dat[di++] = x1 , dat[di++] = y1;
		// GraphicsPathCommand.LINE_TO
		cmds[ci++] = 2;
		dat[di++] = x0 , dat[di++] = y1;
		// end inline function
		 
		path[i].commands = cmds;
		path[i].data = dat;
		igraph[ig++] = fills[i % 3];
		igraph[ig++] = path[i];
	}
	// everything is drawn with one function call
    graphics.drawGraphicsData(igraph);
}

I haven’t spent much time with IGraphicsData. It’s a very cool feature and I think its going to take me awhile to realize its full potential. Since I have to start somewhere I decided to write this snippet to show that it is rather fast. I also recalled reading some benchmark info for the new fp10 graphics stuff over at bytearray.org…. pretty nice info there…

This snippet is pretty optimized, there are a few areas (such as the recurring population of the cmds Vector) that could be optimized more but are left this way for SOME flexibility.

Read snippet: IGraphicsData Example

Tetris Sand CA

stage.frameRate = 30;
scaleX = scaleY = 2.5;
var canvas:BitmapData = new BitmapData(200,200,false, 0x000000);
addChild(new Bitmap(canvas));
var w:int = canvas.width;
var size:int = canvas.width * canvas.height;
var read:Vector.<uint> = new Vector.<uint>(size);
var write:Vector.<uint> = new Vector.<uint>(size);

read = canvas.getVector(canvas.rect);
var loc:int = 199*w;
for(var i:int = 0; i <= 200; i++){
       read[loc+i]=0xFFFFFFFF;
}
write = read.concat();

var shapes:Array = [];
shapes[0] = [[1,1],[1,1]];
shapes[1] = [[1,1,1,1]];
shapes[2] = [[1],[1],[1],[1]];
shapes[3] = [[1,1,1],[0,1,0]];
shapes[4] = [[1,0],[1,1],[1,1],[0,1]];
shapes[5] = [[1,0,0],[1,1,1]];
shapes[6] = [[1,1,0], [0,1,1]];
shapes[7] = [[1,1], [1,0],[1,0]];
function drawShape(xp:int, yp:int, pattern:Array, col:uint):void{
	var i:int = 0;
	var loc:int = xp + yp * w;
	if (int(Math.random()*2) == 1){
		pattern.reverse();
	}
	if (int(Math.random()*2)){
		for (i= 0; i<pattern.length; i++){
			pattern[i].reverse();
		}
	}
	for (i = 0; i<pattern.length; i++){
		 for (var j:int = 0; j<pattern[i].length; j++){
			 if (pattern[i][j] == 1){
				write[loc + i * w + j] = col;
			 }
		}
	}
}

var counter:int = 0;
addEventListener(Event.ENTER_FRAME, onLoop);
function onLoop(evt:Event):void {
	 var i:int;
     canvas.lock();
	 if (counter % 20 == 1){
		for ( i= 0; i<20; i++){
			 drawShape(5+  i * 10, 10, shapes[int(Math.random() * shapes.length)], Math.random()*0xFFFFFF);
		}
	 }
	 counter++;
	 read = write.concat();
	 for (i = 0; i<size; i++){
		 var curr:uint = read[i];
		 if (curr != 0xFF000000 && curr != 0xFFFFFFFF){
			var below:uint = read[i + w];
			if (below == 0xFF000000 || below == curr){
			  var above:uint = read[i - w];
			  if (above == 0xFF000000){
				 write[i] = 0xFF000000;
			  }
			  write[i + w] = curr;
			}else{
				var index:uint;
				if (int(Math.random()*2) == 1){
					index = i + w - 1;
				}else{
					index = i + w + 1;
				}
				if (read[index] == 0xFF000000){
					write[index] = curr;
				}
			}
		 }
	 }
	 canvas.setVector(canvas.rect, write);
	 canvas.unlock();
}

Quickly thrown together sand cellular automata – with some tetris shapes.

Read snippet: Tetris Sand CA

Distance Line Gradient

[SWF(width=600,height=500,frameRate=30)]
var canvas:BitmapData=new BitmapData(400,400,false,0x000000);
addChild(new Bitmap(canvas));

var size:Number=canvas.width*canvas.height;
var w:Number=canvas.width;
var pix:Vector.<uint> = new Vector.<uint>(size);
 
addEventListener(Event.ENTER_FRAME, onLoop);
function onLoop(evt:Event):void {
	canvas.lock();
		
 	var i:int = size;
	var x1:Number=mouseX;
	var y1:Number=mouseY;
	var x2:Number=250;
	var y2:Number=250;
	var dx:Number=x2-x1;
	var dy:Number=y2-y1;
	var denom:Number =  1/(dx * dx + dy * dy);
	if (dx==0&&dy==0) {
			x2+=1;
			y2+=1;
			dx=dy=1;
	}

	while( --i > -1 ){
		var xp:int= i % w;
		var yp:int = i / w;
		var u:Number = ((xp - x1) * dx + (yp - y1) * dy) * denom;

		var closestX:Number;
		var closestY:Number;
		if (u<0) {
			closestX=x1;
			closestY=y1;
		} else if (u > 1) {
			closestX=x2;
			closestY=y2;
		} else {
			closestX=x1+u*dx;
			closestY=y1+u*dy;
		}
		var dfx:Number=closestX-xp;
		var dfy:Number=closestY-yp;
		var d:Number=255-Math.sqrt(dfx*dfx+dfy*dfy);
		if (d<0) d=0;
		 pix[i]=uint(d);
	}
	canvas.setVector(canvas.rect, pix);
	canvas.unlock();
}

This didn’t come out as interesting as I thought it would for some reason – just used the technique from the last two posts to draw a gradient. Event though it’s visually boring, it does show how I went about inlining the function from yesterday… so I figured I’d post it.

Read snippet: Distance Line Gradient