[EDIT: Just a slight update to get rid of an unneeded function, and also make it so you can change the background colour easily if you choose to play around.]
Instructions:
-play around with the control points to change the curve
-tap or hold hold " ^ "arrow key to increase the number of steps along the curve
-tap or hold " v " arrow key to decrease the number of steps along the curve
-tap " x " to make the control points disappear
This is really just building on what I did yesterday; however, it's a little different. I again, had some time while doing an illustration for school, because Adobe Illustrator was lagging, so I decided to go cubic instead of Quadratic.
I tried to comment a little, but if you have issues, let me know. I can be messaged using Facebook. https://www.facebook.com/arpace
package {
import flash.ui.Keyboard;
import flash.display.Graphics;
import flash.display.Shape;
import flash.events.MouseEvent;
import flash.events.KeyboardEvent;
import flash.geom.Matrix;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
[SWF(width=465, height=465, backgroundColor=0x00, frameRate=30)]
public class FlashTest extends Sprite {
public function FlashTest() {
//Cubic Bezier Curve this time
var n:uint = 200; //number of steps
var bgColour:uint = 0x00;
//this is a path which is essentially just a storage of control pts
var ctrlPtVec:Vector.<Pt> = new Vector.<Pt>;
//doing it by hand for now
ctrlPtVec[0] = new Pt(100,200);//special control point
ctrlPtVec[1] = new Pt(180,280);
ctrlPtVec[2] = new Pt(300,180);
ctrlPtVec[3] = new Pt(380,300);//special control point
var cpl:Sprite = new Sprite();//control points layer
var lineLayer:Shape = new Shape();
cpl.addChild(lineLayer);
var ctrlBoxVec:Vector.<CtrlPtBox> = new Vector.<CtrlPtBox>;
for(var i:uint=0;i!=ctrlPtVec.length;++i){
ctrlBoxVec[i]= new CtrlPtBox();
ctrlBoxVec[i].x = ctrlPtVec[i].x;
ctrlBoxVec[i].y = ctrlPtVec[i].y;
ctrlBoxVec[i].ptData = ctrlPtVec[i];
cpl.addChild(ctrlBoxVec[i]);
}
var brush:BMDBrush1 = new BMDBrush1();
//making a bitmap canvas the size of the stage
var bmd:BitmapData = new BitmapData(stage.stageWidth,stage.stageHeight,false,0x00);//the colour needs to be 0 in order to be transparent
var bmp:Bitmap = new Bitmap(bmd,'never',true);
stage.addChild(bmp);//add the bitmap to the stage
stage.addChild(cpl);//add the control layer... this will be added and removed for simplicity when the user taps x
//start up the image
drawCtrlLines(ctrlPtVec,lineLayer);
drawPts(bmp,findCubicBezPts(ctrlPtVec[0],ctrlPtVec[1],ctrlPtVec[2],ctrlPtVec[3],n),brush,bgColour);
//stuff to help with dragging when we roll out of the object we are trying to drag
var isDragging:Boolean = false,
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,findCubicBezPts(ctrlPtVec[0],ctrlPtVec[1],ctrlPtVec[2],ctrlPtVec[3],n),brush,bgColour);
drawCtrlLines(ctrlPtVec,lineLayer);
}
me.updateAfterEvent();
});
var ctrlLayerIsVisible:Boolean = true;//this is an easy way to track if the control layer visible
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,findCubicBezPts(ctrlPtVec[0],ctrlPtVec[1],ctrlPtVec[2],ctrlPtVec[3],n),brush,bgColour);
drawCtrlLines(ctrlPtVec,lineLayer);
}
}else if(ke.keyCode==38){
if(n<0xffffff){
n++;
drawPts(bmp,findCubicBezPts(ctrlPtVec[0],ctrlPtVec[1],ctrlPtVec[2],ctrlPtVec[3],n),brush,bgColour);
drawCtrlLines(ctrlPtVec,lineLayer);
}
}
});
}
public function drawCtrlLines(ctrlPts:Vector.<Pt>,container:Shape):void{
var ctrlPtsLen:uint = ctrlPts.length;
var g:Graphics = container.graphics;
g.clear();
g.moveTo(ctrlPts[0].x,ctrlPts[0].y);
for(var i:uint = 1; i!=ctrlPtsLen;++i){
if((i+1)%3==0){
g.lineStyle(1,0x888888,1);
}else{
g.lineStyle(1,0x666666,1);
}
g.lineTo(ctrlPts[i].x,ctrlPts[i].y);
}
}
public function drawPts(bmp:Bitmap,pts:Vector.<Pt>,brush:BitmapData,bgColour:uint):void{
var bmd:BitmapData = new BitmapData(bmp.width,bmp.height,false,bgColour),
lenPts:uint = pts.length,
offsetX:Number = brush.rect.width*0.5,
offsetY:Number = brush.height*0.5,
m:Matrix = new Matrix(1,0,
0,1, 0,0); // we ignore/set the identity matrix, and just edit the translation variables, tx,ty
for (var i:uint = 0;i!=lenPts;++i){
m.tx = pts[i].x - offsetX;
m.ty = pts[i].y - offsetY;
bmd.draw(brush,m);
}
bmp.bitmapData=bmd;
}
public function findCubicBezPts(cp0:Pt,cp1:Pt,cp2:Pt,cp3:Pt,steps:uint=2):Vector.<Pt>{
//this will also include the end pts in the resulting Locus Vector Array
/*
//you could actually get rid of this, but you may want to keep it in later
//but you need this here if you want to prevent issues when step number is less than 1
if(steps<1){
steps==1;
}*/
var locus:Vector.<Pt> = new Vector.<Pt>;
//just in case we need to modify the original Pt at some point
locus[0] = new Pt(cp0.x,cp0.y);
var t:Number=0; //current percent from 1 to 100
var inc:Number=1/steps;//ammount that t will increment by
//stored math to help find x positions
var cp1x_neg_cp0x:Number = cp1.x-cp0.x,
cp2x_neg_cp1x:Number = cp2.x-cp1.x,
cp3x_neg_cp2x:Number = cp3.x-cp2.x;
//stored math to help find y positions
var cp1y_neg_cp0y:Number = cp1.y-cp0.y,
cp2y_neg_cp1y:Number = cp2.y-cp1.y,
cp3y_neg_cp2y:Number = cp3.y-cp2.y;
var m0:Number,
m1:Number,
m2:Number,
mm0:Number,
mm1:Number,
x:Number,
y:Number;
for(var i:uint = 1; i!=steps; ++i){
t+=inc;
m0 = cp0.x + (cp1x_neg_cp0x)*t;
m1 = cp1.x + (cp2x_neg_cp1x)*t;
m2 = cp2.x + (cp3x_neg_cp2x)*t
mm0 = m0+(m1-m0)*t;
mm1 = m1+(m2-m1)*t;
x = mm0+(mm1-mm0)*t;
m0 = cp0.y + (cp1y_neg_cp0y)*t;
m1 = cp1.y + (cp2y_neg_cp1y)*t;
m2 = cp2.y + (cp3y_neg_cp2y)*t
mm0 = m0+(m1-m0)*t;
mm1 = m1+(m2-m1)*t;
y = mm0+(mm1-mm0)*t;
locus[i] = new Pt(x,y);
}
locus[i] = new Pt(cp3.x,cp3.y);
return locus;
}
}
}
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.display.Shape;
import flash.display.Graphics;
import flash.filters.BlurFilter;
import flash.geom.Matrix;
// I wish I could call this a struct, but w/e
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;
}
}
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.lineStyle(1,0x0000ff);
g.beginFill(colour,1); //
g.drawRect(-w/2,-h/2,w,h);
}
}
internal class BMDBrush1 extends BitmapData{
//this is just an example of a brush that you would put in a set of bitmap brushes
//you could make things editable
//but for now I just wanted to be able to call it as the CircleBrush
public function BMDBrush1(lineColour:uint=0xff0000,blurAmount:uint=8,radius:uint=10){
//making the sprite that will be used as a brush
if(radius<10){
radius=10;
}
var brushContainer:Sprite= new Sprite();
var ldot:Shape = new Shape();
var g:Graphics = ldot.graphics;
var rpb:uint = radius+blurAmount;
g.lineStyle(1,lineColour,1);
g.drawCircle(rpb,rpb,radius);
ldot.filters = [new BlurFilter(blurAmount,blurAmount,3)];
var dot:Shape = new Shape();
g = dot.graphics;
//g.lineStyle(1,0xff600)
g.beginFill(0xfeff00,1);
g.drawCircle(rpb,rpb,radius-5);
dot.filters = [new BlurFilter(blurAmount,blurAmount,3)];
//workaround to be sure we get the all the bitmap data including the blurred areas
brushContainer.addChild(dot);
brushContainer.addChild(ldot);
g = brushContainer.graphics;
g.lineStyle(1,0x000000,0);
g.drawRect(0,0,rpb*2,rpb*2);
//turning the brush into bitmap data
super(brushContainer.width,brushContainer.height,true,0);
this.draw(brushContainer,new Matrix(1,0,0,1,0,0));
}
}