Instructions:
-play around with the control points to change the curve
-tap or hold "UP " arrow key to increase the number of steps along the curve
-tap or hold "DOWN" arrow key to decrease the number of steps along the curve
This didn't take too long (thus it's really not the best way to go about doing it), but this code does show how to not only find the pts along the path of a quadratic Bezier curve, but how to draw along the path of a curve at a given point using a Vector based object as a brush, that has been converted to a Bitmap.
I am taking a digital media class right now at OCAD University, and we're using nothing but Adobe Illustrator at the moment, so I figured, although it uses cubic Bezier curves (which is just as easy to do as this), that I would write some sketch code to show how to draw along a path, and, in theory, how to make a vector manipulable bitmap brush.
You can contact me on FB if you have ny questions: https://www.facebook.com/arpace
package {
import flash.filters.BlurFilter;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.geom.Matrix;
import flash.display.BitmapData;
import flash.display.Bitmap;
import flash.display.Graphics;
import flash.display.Shape;
import flash.display.Sprite;
public class FlashTest extends Sprite {
public function FlashTest() {
//setting up the data for the control points layer that will be added later
var cp0:Pt = new Pt(10,200);
var cp1:Pt = new Pt(200,25);
var cp2:Pt = new Pt(400,300);
//control points layer
var cpl:Sprite = new Sprite();
var cp0Box:CtrlPtBox = new CtrlPtBox();
cp0Box.x = cp0.x;
cp0Box.y = cp0.y;
cp0Box.ptData = cp0;
var cp1Box:CtrlPtBox = new CtrlPtBox();
cp1Box.x = cp1.x;
cp1Box.y = cp1.y;
cp1Box.ptData = cp1;
var cp2Box:CtrlPtBox = new CtrlPtBox();
cp2Box.x = cp2.x;
cp2Box.y = cp2.y;
cp2Box.ptData = cp2;
cpl.addChild(cp0Box);
cpl.addChild(cp1Box);
cpl.addChild(cp2Box);
//number of steps to take along the path
var n:uint = 100;
var dotr:uint = 20;
var blurAmount:uint = 8;
//making the sprite that will be used as a brush
var brushContainer:Sprite= new Sprite();
var ldot:Shape = new Shape();
var g:Graphics = ldot.graphics;
g.lineStyle(1,0x666666,1);
g.drawCircle(dotr+blurAmount,dotr+blurAmount,dotr);
ldot.filters = [new BlurFilter(blurAmount,blurAmount,3)];
//workaround to be sure we get the all the bitmap data including the blurred areas
brushContainer.addChild(ldot);
g = brushContainer.graphics;
g.lineStyle(1,0x000000,0);
g.drawRect(0,0,(blurAmount+dotr)*2,(blurAmount+dotr)*2);
//turning the brush into bitmap data
var brushBMD:BitmapData = new BitmapData(brushContainer.width,brushContainer.height,true,0);
brushBMD.draw(brushContainer,new Matrix(1,0,0,1,0,0));
//making a bitmap canvas the size of the stage
var bmd:BitmapData = new BitmapData(stage.stageWidth,stage.stageHeight,false,0xffffff);//the colour needs to be 0 in order to be transparent
var bmp:Bitmap = new Bitmap(bmd,'never',true);
drawPts(bmp,findQuadBezPts_nsubdivisions(cp0,cp1,cp2,n),brushBMD);
stage.addChild(bmp);
//adding the control point layer
stage.addChild(cpl);
var isDragging:Boolean = false;
var instanceOfDraggedObject:Object;
//setting up the listeners using anonymous funtions for now to make things easier on me
//this may be familiar to someone that knows Javascript
cpl.addEventListener(MouseEvent.MOUSE_DOWN,
function(me:MouseEvent):void{
isDragging=true;
instanceOfDraggedObject = me.target;
});
stage.addEventListener(MouseEvent.MOUSE_UP,
function(me:MouseEvent):void{
isDragging=false;
});
stage.addEventListener(MouseEvent.MOUSE_MOVE,
function(me:MouseEvent):void{
if(me.buttonDown && isDragging){
instanceOfDraggedObject.ptData.x = me.stageX;
instanceOfDraggedObject.ptData.y = me.stageY;//
instanceOfDraggedObject.x = me.stageX;
instanceOfDraggedObject.y = me.stageY;
drawPts(bmp,findQuadBezPts_nsubdivisions(cp0,cp1,cp2,n),brushBMD);
}
me.updateAfterEvent();
});
var ctrlLayerIsVisible:Boolean = true;
stage.addEventListener(KeyboardEvent.KEY_UP,
function(ke:KeyboardEvent):void{
if(ke.keyCode == 88){
if(ctrlLayerIsVisible){
ctrlLayerIsVisible = false;
stage.removeChild(cpl);
}else {
ctrlLayerIsVisible = true;
stage.addChild(cpl);
}}});
stage.addEventListener(KeyboardEvent.KEY_DOWN,
function(ke:KeyboardEvent):void{
if(ke.keyCode==40){
if(n>1){
n--;
drawPts(bmp,findQuadBezPts_nsubdivisions(cp0,cp1,cp2,n),brushBMD);
}
}else if(ke.keyCode==38){
if(n<0xffffff){
n++;
drawPts(bmp,findQuadBezPts_nsubdivisions(cp0,cp1,cp2,n),brushBMD);
}}});
}
public function drawPts(bmp:Bitmap,pts:Vector.<Pt>,sBMD:BitmapData):void{
var bmd:BitmapData = new BitmapData(bmp.width,bmp.height,false,0xffffff);
var lenPts:uint = pts.length;
var offsetX:Number = sBMD.rect.width*0.5;
var offsetY:Number = sBMD.height*0.5;
var m:Matrix = new Matrix(1,0,
0,1, 0,0);
for (var i:uint = 0;i!=lenPts;++i){
m.tx = pts[i].x - offsetX;
m.ty = pts[i].y - offsetY;
bmd.draw(sBMD,m);
}
bmp.bitmapData=bmd;
}
public function findQuadBezPts_nsubdivisions(p0:Pt,p1:Pt,p2:Pt,n:uint=100):Vector.<Pt>
{
//the length of the returning vector will be the number of subdivisions +2, in order to include
//the starting and ending points
if(n<1){
n=1;
}
var locus:Vector.<Pt> = new Vector.<Pt>;
//first 1
locus[0] = new Pt(p0.x,p0.y);
//the stuff in the middle
//varibles to store for the loop..
var p1xNegP0x:Number = p1.x - p0.x;
var p1yNegP0y:Number = p1.y - p0.y;
//p2-p1 can be stored
var p2xNegP1x:Number = p2.x - p1.x;
var p2yNegP1y:Number = p2.y - p1.y;
var m0:Number,m1:Number;
var i:uint = 0;
var t:Number = 0;
var inc:Number = 1/n;
for (i=1; i<n;i+=1){
t+=inc;
locus[i] = new Pt();
m0 = p0.x + p1xNegP0x*t;
m1 = p1.x + p2xNegP1x*t;
locus[i].x = m0 + (m1-m0)*t;
m0 = p0.y + p1yNegP0y*t;
m1 = p1.y + p2yNegP1y*t;
locus[i].y = m0 + (m1-m0)*t;
}
//final 1
locus[n] = new Pt(p2.x,p2.y);
return locus;
}
}
}
import flash.display.Graphics;
internal class Pt{
public var x:Number;
public var y:Number;
public function Pt(x:Number=0,y:Number=0){
this.x=x;
this.y=y;
}
}
import flash.display.Sprite;
internal class CtrlPtBox extends Sprite{
public var ptData:Pt;
public function CtrlPtBox(w:uint = 10,h:uint=10,colour:uint=0x000000){
this.buttonMode = true;
drawBox(w,h,colour);
}
public function drawBox(w:uint=10,h:uint=10,colour:uint= 0x000000):void{
var g:Graphics = this.graphics;
g.beginFill(colour,1); //
g.drawRect(-w/2,-h/2,w,h);
}
}