opengl算法學習--圓弧繪制
整圓的繪制算法有逐點比較法、Bresenham算法和中點畫圓法,這些算法可以在生成1/4象限圓弧或者1/8象限圓弧的基礎上通過已生成的象限對稱而繪制出其他象限的圓弧,稱為4路對稱或8路對稱。
4/8路對稱
根據圓弧在多個象限上的對稱性,我們就可以通過一個象限上的點來推出其他象限上的點,以4路對稱為例,若知道一個點在第一象限上的坐標(x,y),即可通過x軸,y軸以及圓心對稱,得知該點在其它象限上得坐標
根據4路對稱,我們可以通過角度cos與sin的性質,推出8路對稱,如下圖所示
代碼實現(8路對稱)
int ar[4][2]={{1,1},{-1,1},{1,-1},{-1,-1}};
void circledrew(int x,int y,int ox,int oy)
{
for(int i=0;i<4;i++) Setpoint(ox+x*ar[i][0],oy+y*ar[i][1]);
for(int i=0;i<4;i++) Setpoint(ox+y*ar[i][0],oy+x*ar[i][1]);
}
Bresenham畫圓算法
Bresenham畫圓算法適合於生成整圓,它使用8路對稱法,只需計算出90°~45°內的點,沿着右下方向(+x,-y)逐點掃描。
方法概述
設點P\((x_{i},y_{i})\),為第i步選定的坐標,由於計算區間為90°~45°,所以下一個候選點必定為T\((x_{i}+1,y_{i})\)或者S\((x_{i}+1,y_{i}-1)\)
令D(T)為T點到原點距離的與半徑的平方差,D(S)為S點到原點距離的與半徑的平方差。
可知
定義
可推出
當\(d_{i}>0\)時 \(y_{i+1}=y_{i}-1\)
當\(d_{i}\leq 0\)時 \(y_{i+1}=y_{i}\)
因為起點坐標為(0,r)
所以
代碼實現
void Bresenham(int ox,int oy,int r)
{
int x=0,y=r,d=3-(r<<1);
circledrew(x,y,ox,oy);
while (x<=y)
{
if(d<0) d+=(x<<2)+6;
else
{
d+=((x-y)<<2)+10;
--y;
}
++x;
circledrew(x,y,ox,oy);
}
}
中點畫圓法
中點畫圓法與Bresenham畫圓法類似,通過對預測點的中點,計算下一個點的位置
方法概述
設點\(P(x_{i},y_{i})\)為當前點亮像素,接下來的候選像素點為\(T(x_{i}+1,y_{i})\)或\(S(x_{i}+1,y_{i}-1)\),線段ST的中點\(M(x_{i}+1,y_{i}-0.5)\)。
構造函數\(F(x,y)=x^{2}+y^{2}-r^{2}\)
若\(F(M)<0\),說明M在圓內,選取T點
若\(F(M)\geq 0\),說明M在圓外,選取S點
設
若\(d_{i}<0\),則下個點選擇T
若\(d_{i}\geq 0\),則下個點選擇S
因為起點坐標為(0,r)
所以
由於在實際實現中,之后的遞推式中並不會出現浮點運算,所以0.25並不會影響之后的正負關系,因此可以將d1簡化為1-r,可提高該算法效率
代碼實現
void midpointcircle(int ox,int oy,int r)
{
int x=0,y=r,d=1-r;
circledrew(x,y,ox,oy);
while (x<=y)
{
if(d<0) d+=(x<<1)+3;
else
{
d+=((x-y)<<1)+5;
--y;
}
++x;
circledrew(x,y,ox,oy);
}
}
角度離散法
角度離散法利用已有的直線算法來分段繪制圓弧或橢圓弧,即借助與參數方程繪制曲線,並采用“以直代曲”的策略。采用這種方式的優點在於靈活性強(自由控制所繪制的弧角),而不足之處在於計算效率不高。
方法概述
若已知圓心坐標\((x_{c},y_{c})\),半徑 r ,則該圓以角度t為參數的參數方程為
通過離散化參數t對圓的參數方程進行離散化,離散步長\(\delta t\)通常根據半徑的大小經驗確定,半徑越大,步長\(\delta t\) 適當取小;半徑較小時,步長\(\delta t\) 適當取大。以逆時針方向為正,當參數t從\(t_{s}\)變化至\(t_{e}\)時,表示繪制一段圓弧。
代碼實現
void arccircle(int ox,int oy,double r,double angs,double ange)
{
if(ange<angs) ange+=2*PI;
double dt=0.4/r;
int n=(int)((ange-angs)/dt);
int x=Round(ox+r*cos(angs));
int y=Round(oy+r*sin(ange));
glVertex2i(x,y);
glBegin(GL_LINE_STRIP);
for(int i=0;i<n;i++)
{
glVertex2i(Round(x+r*cos(angs+i*dt)),Round(y+r*sin(angs+i*dt)));
}
glEnd();
glFlush();
}