package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.geom.Rectangle;
/**
*贝塞尔曲线绘制算法
* @author boycy815*/
public class Main extends Sprite
{
/*C30到C33的值总共才4个直接枚举*/
private const C30:int = 1;
private const C31:int = 3;
private const C32:int = 3;
private const C33:int = 1;
/*定义曲线上点的个数(包括两端)*/
private const LENGTH:int = 10000;
/*btC30到btC33分别是R0 R1 R2 R3四个点当t取前三个值时的权重值*/
private var btC30:Vector. = new Vector.(3);
private var btC31:Vector. = new Vector.(3);
private var btC32:Vector. = new Vector.(3);
private var btC33:Vector. = new Vector.(3);
/*画点的位图*/
private var data:BitmapData;
private var map:Bitmap;
/*用于连接四个点的四条直线*/
private var lin:Shape;
/*分别是四个点的显示对象*/
private var ct0:Sprite;
private var ct1:Sprite;
private var ct2:Sprite;
private var ct3:Sprite;
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
/*初始化位图和一些显示对象*/
data = new BitmapData(800, 600,false);
map = new Bitmap(data);
this.addChild(map);
lin = new Shape();
this.addChild(lin);
ct0 = new Sprite();
ct0.buttonMode = true;
ct0.graphics.lineStyle(0, 0x00000);
ct0.graphics.beginFill(0xffffff);
ct0.graphics.drawCircle(0, 0, 5);
ct0.graphics.endFill();
ct0.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
ct0.addEventListener(MouseEvent.MOUSE_UP, mouseUp);
this.addChild(ct0);
ct1 = new Sprite();
ct1.buttonMode = true;
ct1.graphics.lineStyle(0, 0x00000);
ct1.graphics.beginFill(0xffffff);
ct1.graphics.drawCircle(0, 0, 5);
ct1.graphics.endFill();
ct1.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
ct1.addEventListener(MouseEvent.MOUSE_UP, mouseUp);
this.addChild(ct1);
ct2 = new Sprite();
ct2.buttonMode = true;
ct2.graphics.lineStyle(0, 0x00000);
ct2.graphics.beginFill(0xffffff);
ct2.graphics.drawCircle(0, 0, 5);
ct2.graphics.endFill();
ct2.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
ct2.addEventListener(MouseEvent.MOUSE_UP, mouseUp);
this.addChild(ct2);
ct3 = new Sprite();
ct3.buttonMode = true;
ct3.graphics.lineStyle(0, 0x00000);
ct3.graphics.beginFill(0xffffff);
ct3.graphics.drawCircle(0, 0, 5);
ct3.graphics.endFill();
ct3.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
ct3.addEventListener(MouseEvent.MOUSE_UP, mouseUp);
this.addChild(ct3);
/*初始化计算的一些准备数据*/
btInit();
/*默认进行第一次定位和绘制*/
ct0.x = 100;
ct0.y = 100;
ct1.x = 700;
ct1.y = 100;
ct2.x = 700;
ct2.y = 500;
ct3.x = 100;
ct3.y = 500;
lin.graphics.lineStyle(0, 0x000000);
lin.graphics.moveTo(ct0.x, ct0.y);
lin.graphics.lineTo(ct1.x, ct1.y);
lin.graphics.lineTo(ct2.x, ct2.y);
lin.graphics.lineTo(ct3.x, ct3.y);
drawBezier3(new Point(ct0.x, ct0.y), new Point(ct1.x, ct1.y), new Point(ct2.x, ct2.y), new Point(ct3.x, ct3.y));
}
/*按钮按下便开始可以拖动点*/
private function mouseDown(e:MouseEvent):void
{
e.target.startDrag();
stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMove);
}
/*鼠标移动便不断绘制曲线*/
private function mouseMove(e:MouseEvent):void
{
/*绘制连接四个点的直线*/
lin.graphics.clear();
lin.graphics.lineStyle(0, 0x000000);
lin.graphics.moveTo(ct0.x, ct0.y);
lin.graphics.lineTo(ct1.x, ct1.y);
lin.graphics.lineTo(ct2.x, ct2.y);
lin.graphics.lineTo(ct3.x, ct3.y);
/*绘制曲线*/
data.fillRect(new Rectangle(0, 0, 800, 600), 0xffffffff);
drawBezier3(new Point(ct0.x, ct0.y), new Point(ct1.x, ct1.y), new Point(ct2.x, ct2.y), new Point(ct3.x, ct3.y));
}
/*释放按钮停止拖动*/
private function mouseUp(e:MouseEvent):void
{
var n:int;
e.target.stopDrag();
stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMove);
}
/***********算法部分**********/
/*初始化计算所需数据,主要是btC30到btC33*/
private function btInit():void
{
var add:Number;
/*由于t的值域为[0,1],要绘制LENGTH个点,则必须计算出每次t的增量*/
add = 1.0 / (LENGTH - 1.0);
/*三次白赛尔曲线Ri的权重公式为:C3i * pow( t - 1 , 3 - i ) * pow( t , i )
*前三次t的值分别为0,add,add*2
*则代入公式就能求出R0 R1 R2 R3的前三次的权重值
**/
btC30[0] = 1;
btC30[1] = C30 * Math.pow(1 - add, 3);
btC30[2] = C30 * Math.pow(1 - add * 2, 3);
btC31[0] = 0;
btC31[1] = C31 * Math.pow(1 - add, 2) * add;
btC31[2] = C31 * Math.pow(1 - add * 2, 2) * add * 2;
btC32[0] = 0;
btC32[1] = C32 * (Math.pow(add, 2) - Math.pow(add, 3));
btC32[2] = C32 * (Math.pow(add * 2, 2) - Math.pow(add * 2, 3));
btC33[0] = 0;
btC33[1] = C33 * Math.pow(add, 3);
btC33[2] = C33 * Math.pow(add * 2, 3);
}
/*利用准备好的初始数据,通过递推方法求出四个点对应的贝塞尔曲线上每个点的坐标,并绘制
*计算中用到多项式批量计算优化策略,上一篇博文中有提及具体的方法和思想
**/
private function drawBezier3(p0:Point,p1:Point,p2:Point,p3:Point):void
{
/*qun_x和qun_x本别为x坐标和y坐标的临时数据数组*/
var qun_x:Vector. = new Vector.(3);
var qun_y:Vector. = new Vector.(3);
/*dd_x和dd_y分别为x坐标的多项式和y坐标式求导三次后得到的常数*/
var dd_x:Number;
var dd_y:Number;
var n:int;
/*计算前三次种子数据*/
dd_x = 6 * (3 * p1.x - 3 * p2.x - p0.x + p3.x) / Math.pow(LENGTH - 1.0, 3);
qun_x[0] = p0.x * btC30[0] + p1.x * btC31[0] + p2.x * btC32[0] + p3.x * btC33[0];
dd_y = 6 * (3 * p1.y - 3 * p2.y - p0.y + p3.y) / Math.pow(LENGTH - 1.0, 3);
qun_y[0] = p0.y * btC30[0] + p1.y * btC31[0] + p2.y * btC32[0] + p3.y * btC33[0];
data.setPixel(qun_x[0], qun_y[0], 0x000000);
qun_x[1] = p0.x * btC30[1] + p1.x * btC31[1] + p2.x * btC32[1] + p3.x * btC33[1];
qun_x[0] = qun_x[1] - qun_x[0];
qun_y[1] = p0.y * btC30[1] + p1.y * btC31[1] + p2.y * btC32[1] + p3.y * btC33[1];
qun_y[0] = qun_y[1] - qun_y[0];
data.setPixel(qun_x[1], qun_y[1], 0x000000);
qun_x[2] = p0.x * btC30[2] + p1.x * btC31[2] + p2.x * btC32[2] + p3.x * btC33[2];
qun_x[1] = qun_x[2] - qun_x[1];
qun_x[0] = qun_x[1] - qun_x[0];
qun_y[2] = p0.y * btC30[2] + p1.y * btC31[2] + p2.y * btC32[2] + p3.y * btC33[2];
qun_y[1] = qun_y[2] - qun_y[1];
qun_y[0] = qun_y[1] - qun_y[0];
data.setPixel(qun_x[2], qun_y[2], 0x000000);
/*进行递推计算的循环*/
for (n = 3; n < LENGTH; n++)
{
qun_x[0] += dd_x;
qun_x[1] += qun_x[0];
qun_x[2] += qun_x[1];
qun_y[0] += dd_y;
qun_y[1] += qun_y[0];
qun_y[2] += qun_y[1];
data.setPixel(qun_x[2], qun_y[2], 0x000000);
}
}
}
}