By Zevan | March 12, 2009
Actionscript:
-
var canvas:BitmapData = Bitmap(addChild(new Bitmap(new BitmapData(400, 400, false, 0x000000)))).bitmapData;
-
-
function line(x1:Number, y1:Number, x2:Number, y2:Number, res:int=10):void{
-
var dx:Number = x2 - x1;
-
var dy:Number = y2 - y1;
-
var dist:Number = Math.sqrt((dx * dx) + (dy * dy));
-
var step:Number = 1 / (dist / res);
-
for (var i:Number = 0; i<=1; i+= step){
-
// lerp : a + (b - a) * f
-
canvas.setPixel(x1 + dx * i, y1 + dy * i, 0xFFFFFF);
-
}
-
}
-
-
addEventListener(Event.ENTER_FRAME, onLoop);
-
function onLoop(evt:Event):void {
-
canvas.fillRect(canvas.rect, 0x000000);
-
line(100 , 100, mouseX, mouseY,1);
-
line(100 + 50, 100, mouseX+ 50, mouseY,5);
-
}
Yesterday I posted an implementation of the Bresenham Line Algorithm. Today I'm posting a comparatively slow way to draw a line with setPixel(). This snippet uses lerp and the Pythagorean theorem. It works nicely for small numbers of lines, its easy to draw dotted lines with it and its easy to explain. In a real app where you needed to use setPixel() to draw a line you should use one of the fast algorithms like Wu or Bresenham.
I didn't originally write this snippet to use set pixel... a few weeks ago I wrote something very similar to calculate a set of x y coords between two given points. In the program I used it in speed wasn't an issue (as I only needed to run the function one time). I've needed this kind of function many times before in games and small apps...
This was the original:
Actionscript:
-
function calculatePoints(x1:Number, y1:Number, x2:Number, y2:Number, res:int=10):Array{
-
var points:Array = new Array();
-
var dx:Number = x2 - x1;
-
var dy:Number = y2 - y1;
-
var dist:Number = Math.sqrt((dx * dx) + (dy * dy));
-
var step:Number = 1 / (dist / res);
-
for (var i:Number = 0; i<=1; i+= step){
-
points.push(new Point(x1 + dx * i, y1 + dy * i));
-
}
-
return points;
-
}
-
-
trace(calculatePoints(0,0,100,0,10));
-
/* outputs:
-
(x=0, y=0),(x=10, y=0),(x=20, y=0),(x=30.000000000000004, y=0),(x=40, y=0),(x=50, y=0),(x=60, y=0),(x=70, y=0),(x=80, y=0),(x=89.99999999999999, y=0),(x=99.99999999999999, y=0)
-
*/
...and another version to allow you to specify the number of points to calculate rather than the pixel interval at which they should be calculated:
Actionscript:
-
function calculatePoints(x1:Number, y1:Number, x2:Number, y2:Number, pointNum:int=10):Array{
-
var points:Array = new Array();
-
var step:Number = 1 / (pointNum + 1);
-
for (var i:Number = 0; i<=1; i+= step){
-
points.push(new Point(x1 + (x2 - x1) * i, y1 + (y2 - y1) * i));
-
}
-
return points;
-
}
-
-
trace(calculatePoints(0,30,30,0,1));
-
/* outputs:
-
(x=0, y=30),(x=15, y=15),(x=30, y=0)
-
*/
This last version isn't perfect, sometimes the pointNum will be off by 1, I may fix that in a future post.
By Zevan | March 11, 2009
Actionscript:
-
var canvas:BitmapData = Bitmap(addChild(new Bitmap(new BitmapData(400,400, false, 0x000000)))).bitmapData;
-
-
drawLine(10,10,100,90, 0xFF0000);
-
drawLine(100,90,60,80, 0xFF0000);
-
drawLine(100,90,95,60, 0xFF0000);
-
-
for (var i:int = 0; i<100; i+=1){
-
drawLine(i *4, 100 + i, 200, 390);
-
}
-
// code ported from here:
-
// http://www.edepot.com/linebenchmark.html
-
function drawLine(x1:int, y1:int, x2:int, y2:int, col:uint = 0xFFFFFF){
-
var x:int, y:int;
-
var dx:int, dy:int;
-
var incx:int , incy:int
-
var balance:int;
-
-
if (x2>= x1){
-
dx = x2 - x1;
-
incx = 1;
-
}else{
-
dx = x1 - x2;
-
incx = -1;
-
}
-
-
if (y2>= y1){
-
dy = y2 - y1;
-
incy = 1;
-
}else{
-
dy = y1 - y2;
-
incy = -1;
-
}
-
-
x = x1;
-
y = y1;
-
-
if (dx>= dy){
-
dy <<= 1;
-
balance = dy - dx;
-
dx <<= 1;
-
-
while (x != x2){
-
canvas.setPixel(x, y, col);
-
if (balance>= 0){
-
y += incy;
-
balance -= dx;
-
}
-
balance += dy;
-
x += incx;
-
}
-
canvas.setPixel(x, y, col);
-
}else{
-
dx <<= 1;
-
balance = dx - dy;
-
dy <<= 1;
-
-
while (y != y2){
-
canvas.setPixel(x, y, col);
-
if (balance>= 0){
-
x += incx;
-
balance -= dy;
-
}
-
balance += dx;
-
y += incy;
-
}
-
canvas.setPixel(x, y, col);
-
}
-
}
This snippet shows Brensenham's line drawing algorithm. I ported this implementation from here... all the line algorithms in that link are easy to port to actionscript. I've messed with them all at some point.
Tomorrow I'm going to post a super slow line drawing algorithm... so I figured I'd post a fast line drawing algorithm today.
By Zevan | February 16, 2009
Actionscript:
-
[SWF(width=720,height=360,backgroundColor=0x000000,frameRate=30)]
-
-
// 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)];
-
}
-
-
-
//
-
// -- test it out by drawing a few things
-
//
-
-
var canvas:BitmapData = new BitmapData(720, 360, false, 0x000000);
-
-
addChild(new Bitmap(canvas));
-
-
canvas.lock();
-
var size:int = canvas.width * canvas.height;
-
var xp:int, yp:int, c:Array, i:int;
-
-
for (i = 0; i<size; i++){
-
xp = i % 360;
-
yp = i / 360;
-
c = hsv(xp, 1, yp / 360);
-
canvas.setPixel(xp, yp, c[0] <<16 | c[1] <<8 | c[2]);
-
}
-
-
var dx:Number, dy:Number, dist:Number, ang:Number;
-
-
for (i = 0; i<size; i++){
-
xp = i % 360;
-
yp = i / 360;
-
dx = xp - 180;
-
dy = yp - 180;
-
dist = 1 - Math.sqrt((dx * dx) + (dy * dy)) / 360;
-
ang = Math.atan2(dy, dx) / Math.PI * 180;
-
if (ang <0){
-
ang += 360;
-
}
-
c = hsv(ang, 1, dist);
-
canvas.setPixel(360 + xp, yp, c[0] <<16 | c[1] <<8 | c[2]);
-
}
-
canvas.unlock();
This is one of those things I've been meaning to play with for awhile. The above demos a function called hsv() which takes 3 arguments: angle (0-360), saturation(0-1) and value(0-1). The function returns an array of rgb values each with a range of (0-255).
There's some room for optimization here, but for clarity I left as is. Even just playing with HSV (also know as HSB) for a few minutes, I see some interesting potential for dynamically generating color palettes for generative style experiments.
I looked around for the most elegant looking code snippet to port in order to write this... I eventually stumbled upon this great resource.
If you test the above on your timeline it will generate this image:
I usually only post one snippet a day... not sure why I decided to post two today.
By Zevan | January 27, 2009
Actionscript:
-
var circle:Shape = Shape(addChild(new Shape));
-
with(circle.graphics) beginFill(0x000000), drawCircle(0,0,5);
-
-
var bezierPoint:Point = new Point();
-
function bezier(a:Number, x1:Number, y1:Number, x2:Number, y2:Number, x3:Number, y3:Number):void {
-
var b:Number =1-a;
-
var pre1:Number=a*a;
-
var pre2:Number=2*a*b;
-
var pre3:Number=b*b;
-
bezierPoint.x = pre1*x1 + pre2*x2 + pre3*x3;
-
bezierPoint.y = pre1*y1 + pre2*y2 + pre3*y3;
-
}
-
-
var inc:Number = 0;
-
var theta:Number = 0;
-
-
addEventListener(Event.ENTER_FRAME, onLoop);
-
function onLoop(evt:Event):void {
-
-
graphics.clear();
-
graphics.lineStyle(0,0xFF0000);
-
graphics.moveTo(200,200);
-
graphics.curveTo(mouseX, mouseY, 400, 200);
-
-
theta+= .05;
-
inc = .5 + .5*Math.sin(theta);
-
-
bezier(inc, 200, 200, mouseX, mouseY, 400, 200);
-
circle.x = bezierPoint.x;
-
circle.y = bezierPoint.y;
-
}
The above animates a circle along a quadratic bezier curve. This snippet was written in response to a question spurned by some of the recent bezier posts. I used Math.sin() to animate the circle but you could just as easily use modulus... simply replace lines 25-26 with the following: