By Zevan | January 12, 2009
Actionscript:
-
// graphics, vector of xy coords, closed boolean
-
function bezierSkin(g:Graphics, bez:Vector.<Number>, closed:Boolean = true):void {
-
var avg:Vector.<Number> = calcAvgs(bez);
-
var i:int, n:int;
-
var leng:int = bez.length;
-
-
if (closed){
-
g.moveTo(avg[0], avg[1]);
-
for (i = 2; i <leng; i+=2) {
-
n=i+1;
-
g.curveTo(bez[i], bez[n], avg[i], avg[n]);
-
}
-
g.curveTo(bez[0], bez[1], avg[0], avg[1]);
-
}else{
-
g.moveTo(bez[0], bez[1]);
-
g.lineTo(avg[0], avg[1]);
-
for (i = 2; i <leng-2; i+=2) {
-
n=i+1;
-
g.curveTo(bez[i], bez[n], avg[i], avg[n]);
-
}
-
g.lineTo(bez[ leng - 2], bez[ leng - 1]);
-
}
-
}
-
-
// create anchor points by averaging the control points
-
function calcAvgs(v:Vector.<Number>):Vector.<Number> {
-
var avg:Vector.<Number> = new Vector.<Number>();
-
var leng:int=v.length;
-
for (var i:int = 2; i<leng; i+=1) {
-
var prev:Number=i-2;
-
avg.push( (v[prev] + v[i]) / 2 );
-
}
-
// close
-
avg.push( (v[0] + v[ leng - 2]) / 2 );
-
avg.push( (v[1] + v[ leng - 1]) / 2 );
-
return avg;
-
}
-
-
// test out the functions:
-
-
graphics.lineStyle(0,0x000000);
-
-
// draw a very rounded rect
-
bezierSkin(graphics, Vector.<Number>([100,100,200,100,200,200,100,200]) );
-
-
// draw a random curve with 10 xy coords
-
var rnd:Vector.<Number> = new Vector.<Number>();
-
for (var i:int = 0; i<20; i++) rnd.push(Math.random()*100);
-
bezierSkin(graphics, rnd);
-
-
// erase everything and allow the used to draw a smooth curve with the mouse
-
stage.addEventListener(MouseEvent.MOUSE_DOWN, onLoop);
-
var index:int = 0;
-
var pnts:Vector.<Number> = new Vector.<Number>();
-
-
function onLoop(evt:MouseEvent):void {
-
pnts.push(mouseX);
-
pnts.push(mouseY);
-
-
graphics.clear();
-
graphics.lineStyle(0,0x000000);
-
-
// draw a smooth curved line
-
bezierSkin(graphics,pnts,false);
-
}
The above code uses averaging to generate anchor points for curveTo() calls. This is an easy way to always draw smooth curves.
This is an old one that I've seen floating around since the drawing api first came out... but I it's an important snippet, so I figured I'd post it.
By Zevan | January 11, 2009
Actionscript:
-
var canvas:BitmapData=new BitmapData(280,280,false,0x000000);
-
addChild(new Bitmap(canvas, PixelSnapping.AUTO, true));
-
var color:uint;
-
// anchor x1, anchor y1,
-
// control-handle x2, control-handle y2,
-
// anchor x3, anchor y3, [resolution incremental value between 0-1]
-
function quadBezier(x1:Number, y1:Number, x2:Number, y2:Number, x3:Number, y3:Number, resolution:Number=.03):void {
-
var b:Number,pre1:Number,pre2:Number,pre3:Number,pre4:Number;
-
for (var a:Number = 0; a <1;a+=resolution) {
-
-
b=1-a;
-
pre1=(a*a);
-
pre2=2*a*b;
-
pre3=(b*b);
-
-
canvas.setPixel(pre1*x1 + pre2*x2 + pre3*x3 ,
-
pre1*y1 + pre2*y2 + pre3*y3, color);
-
}
-
}
-
-
// draw a few
-
color = 0xFFFFFF;
-
-
for (var i:int = 0; i<20; i++){
-
quadBezier(40,100, 150 , 20 + i * 10 , 200, 100,.01);
-
}
-
-
color = 0xFF0000;
-
-
for (i= 0; i<20; i++){
-
quadBezier(150,200, 100 + i * 10, 100 , 120, 30,.01);
-
}
The above demos a function that draws quadratic bezier curves using setPixel().
One of the first posts on this site was a snippet that used setPixel() to draw a cubic bezier curve. I recently needed to do the exact same thing but I wanted to use a quadratic bezier... I knew I had the code laying around somewhere, but I couldn't seem to find it so I just looked on wikipedia and changed the previous cubicBezier() function accordingly.
By Zevan | January 10, 2009
Actionscript:
-
var canvas:BitmapData = new BitmapData(400, 400, false, 0xCCCCCC);
-
addChild(new Bitmap(canvas));
-
-
fillCircle(100,100,50,0xFF0000);
-
-
function fillCircle(xp:Number,yp:Number, radius:Number, 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:Number, yp:Number, w:Number, col:uint):void {
-
for (var i:int = 0; i <w; i++){
-
canvas.setPixel(xp + i, yp, col);
-
}
-
}
An implementation of yesterdays post that draws a filled circle instead of an outlined circle.
By Zevan | January 9, 2009
Actionscript:
-
var canvas:BitmapData = new BitmapData(400, 400, false, 0xCCCCCC);
-
addChild(new Bitmap(canvas));
-
-
drawCircle(200,100, 50);
-
-
// y, y radius, color
-
function drawCircle(xp:Number,yp:Number, radius:Number, col:uint =0x000000):void {
-
var balance:int;
-
var xoff:int;
-
var yoff:int;
-
xoff=0;
-
yoff=radius;
-
balance=- radius;
-
-
while (xoff <= yoff) {
-
canvas.setPixel(xp+xoff, yp+yoff, col);
-
canvas.setPixel(xp-xoff, yp+yoff, col);
-
canvas.setPixel(xp-xoff, yp-yoff, col);
-
canvas.setPixel(xp+xoff, yp-yoff, col);
-
canvas.setPixel(xp+yoff, yp+xoff, col);
-
canvas.setPixel(xp-yoff, yp+xoff, col);
-
canvas.setPixel(xp-yoff, yp-xoff, col);
-
canvas.setPixel(xp+yoff, yp-xoff, col);
-
-
if ((balance += xoff++ + xoff)>= 0) {
-
balance-=--yoff+yoff;
-
}
-
}
-
}
The above demos a circle drawing algorithm. This will draw an outlined circle with no fill. This implementation doesn't use multiplication.
Using setPixel to draw primitive shapes can be a good learning experience. I found this basic implementation of the Bresenham Circle algorithm a few years back and lost the original link.... I dug around for a good hour trying to find the original but to no avail. So if someone recognizes this interesting implementation .... let me know
The original code was written in java I think.
If your curious. The standard implementation I keep finding online looks something like this:
Actionscript:
-
// ported from http://www.codeuu.com/Bresenham_Circle
-
function drawCircle(cx:Number, cy:Number, r:Number, col:uint):void{
-
var xp:int = 0, yp:int= r ;
-
var d:int = 3 - (2 * r);
-
-
while(xp <= yp){
-
-
canvas.setPixel(cx+xp,cy+yp,col);
-
canvas.setPixel(cx+yp,cy+xp,col);
-
canvas.setPixel(cx-xp,cy+yp,col);
-
canvas.setPixel(cx+yp,cy-xp,col);
-
canvas.setPixel(cx-xp,cy-yp,col);
-
canvas.setPixel(cx-yp,cy-xp,col);
-
canvas.setPixel(cx+xp,cy-yp,col);
-
canvas.setPixel(cx-yp,cy+xp,col);
-
-
if (d<0){
-
d += (4*xp)+6;
-
}else{
-
d += (4*(xp-yp))+10;
-
yp -= 1;
-
}
-
xp++;
-
}
-
-
}
I did a few speed tests against the first one that doesn't make use of multiplication and it is only ever so slightly faster....