天天看點

繪制貝塞爾曲線通用方法

//計算三次貝塞爾曲線,後面n(n>=3)計算時,都是将點分成一組一組的三次貝塞爾曲線
//計算控制點思路來源:http://www.antigrain.com/research/bezier_interpolation/index.html#PAGE_BEZIER_INTERPOLATION
SkPoint* CalcThreeBezier(Wm5::Vector2d ptw1,Wm5::Vector2d ptw2,Wm5::Vector2d ptw3)
{
	//算出兩條邊中點,再算出中點連線的中點
	SkPoint midOfPtw12,midOfPtw23,mid,midOfmid12mid,midOfmid23mid;
	midOfPtw12.fX = (ptw1.X()+ptw2.X())/2;
	midOfPtw12.fY = (ptw1.Y()+ptw2.Y())/2;
	midOfPtw23.fX = (ptw2.X()+ptw3.X())/2;
	midOfPtw23.fY = (ptw2.Y()+ptw3.Y())/2;
	mid.fX = (midOfPtw12.fX+midOfPtw23.fX)/2;
	mid.fY = (midOfPtw12.fY+midOfPtw23.fY)/2;
	midOfmid12mid.fX = (ptw1.X()+mid.fX)/2;
	midOfmid12mid.fY = (ptw1.Y()+mid.fY)/2;
	midOfmid23mid.fX = (ptw3.X()+mid.fX)/2;
	midOfmid23mid.fY = (ptw3.Y()+mid.fY)/2;

	//通過兩個線段比例,在對應中點連接配接線上找出控制向量,然後進行平移
	double length1 = (ptw2-ptw1).Length();
	double length2 = Wm5::Vector2d(mid.fX-ptw1.X(),mid.fY-ptw1.Y()).Length();
	double scale = length1/(length1+length2);

	SkPoint pointInLeftLine,pointInMidLine,pointInRightLine;
	pointInLeftLine.fX = midOfPtw12.fX + (midOfmid12mid.fX-midOfPtw12.fX)*scale;
	pointInLeftLine.fY = midOfPtw12.fY + (midOfmid12mid.fY-midOfPtw12.fY)*scale;
	pointInMidLine.fX = midOfPtw12.fX + (midOfPtw23.fX-midOfPtw12.fX)*scale;
	pointInMidLine.fY = midOfPtw12.fY + (midOfPtw23.fY-midOfPtw12.fY)*scale;
	pointInRightLine.fX = midOfPtw23.fX + (midOfmid23mid.fX - midOfPtw23.fX)*scale;
	pointInRightLine.fY = midOfPtw23.fY + (midOfmid23mid.fY - midOfPtw23.fY)*scale;

	Wm5::Vector2d v_conPoint1 = Wm5::Vector2d(pointInLeftLine.fX-midOfPtw12.fX,pointInLeftLine.fY-midOfPtw12.fY);
	Wm5::Vector2d v_conPoint2 = Wm5::Vector2d(pointInMidLine.fX-midOfPtw12.fX,pointInMidLine.fY-midOfPtw12.fY);
	Wm5::Vector2d v_conPoint3 = Wm5::Vector2d(midOfPtw23.fX-pointInMidLine.fX,midOfPtw23.fY-pointInMidLine.fY);
	Wm5::Vector2d v_conPoint4 = Wm5::Vector2d(pointInRightLine.fX-midOfPtw23.fX,pointInRightLine.fY-midOfPtw23.fY);

	//算出控件點
	SkPoint conPoint1,conPoint2,conPoint3,conPoint4;
	conPoint1.fX = (ptw1-v_conPoint1).X();
	conPoint1.fY = (ptw1-v_conPoint1).Y();
	conPoint2.fX = (ptw2-v_conPoint2).X();
	conPoint2.fY = (ptw2-v_conPoint2).Y();
	conPoint3.fX = (ptw2+v_conPoint3).X();
	conPoint3.fY = (ptw2+v_conPoint3).Y();
	conPoint4.fX = (ptw3-v_conPoint4).X();
	conPoint4.fY = (ptw3-v_conPoint4).Y();

	//儲存四個控制點
	midConPoint[0] = conPoint1;
	midConPoint[1] = conPoint2;
	midConPoint[2] = conPoint3;
	midConPoint[3] = conPoint4;

	return midConPoint;
}

//繪制貝塞爾曲線函數
void DrawBezier(vector<Point2d> &vectorOfPoint2D,SkCanvas* canvas,SkPaint& paint)
{
	//由于使用輕按兩下停止記錄點,輕按兩下後最後兩個重負,需減掉
	int nPointNum = vectorOfPoint2D.size()-1;
	if (nPointNum < 3)
		return;

	//v_pt存儲每點的向量,pt存儲點,ptCon存儲相應控件點
	Wm5::Vector2d *v_pt = new Wm5::Vector2d[nPointNum];
	SkPoint * ptCon = new SkPoint[2*nPointNum-2];
	SkPoint * pt = new SkPoint[nPointNum];
	for (int i = 0; i < nPointNum; i++ )
	{
		v_pt[i] = Wm5::Vector2d(vectorOfPoint2D[i].dX,vectorOfPoint2D[i].dY);
		pt[i].fX = vectorOfPoint2D[i].dX;
		pt[i].fY = vectorOfPoint2D[i].dY;
	}

	//通過将點分割成多組三次貝塞爾曲線,将各個點的控件點儲存起來
	int nConPointNum = 0;
	for (int i = 0; i < nPointNum-2; i++ )
	{
		SkPoint *pPoint = CalcThreeBezier(v_pt[i],v_pt[i+1],v_pt[i+2]);
		if (i == 0)
			ptCon[nConPointNum++] = pPoint[0];
		ptCon[nConPointNum++] = pPoint[1];
		ptCon[nConPointNum++] = pPoint[2];
		if (i == nPointNum-3)
			ptCon[nConPointNum++] = pPoint[3];
	}

	//畫貝塞爾曲線
	SkPath path;
	nConPointNum = 0;
	path.moveTo(pt[nConPointNum++]);
	for (int i = 0; i < 2*nPointNum-3; i = i+2)
	{
		path.cubicTo(ptCon[i],ptCon[i+1],pt[nConPointNum++]);
	}
	canvas->drawPath(path,paint);

	//畫切線
	nConPointNum = 0;
	path.moveTo(pt[0]);
	path.lineTo(ptCon[nConPointNum++]);
	for (int i = 1; i < nPointNum-1; i++)
	{
		path.moveTo(pt[i]);
		path.lineTo(ptCon[nConPointNum++]);
		path.moveTo(pt[i]);
		path.lineTo(ptCon[nConPointNum++]);
	}
	path.moveTo(pt[nPointNum-1]);
	path.lineTo(ptCon[nConPointNum++]);
	canvas->drawPath(path,paint);

	delete [] v_pt;
	delete ptCon;
	delete pt;

}