椭圆的绘画算法――(旋转、填充)
作者:handwolf
日期: 2005-4-11
介绍:
窗口中的矩形,带圆角的矩形和椭圆只能由GDI在轴向上绘制。假如有人希望在Windows NT下绘制旋转或歪斜的图形,他可以使用世界坐标系变换。很不幸的是在Windows 95/98下,是没有世界坐标系变换的。作为一个跨平台的解决方案,就需要自己做更多的工作。矩形能由四边形模拟,这样它就能旋转和歪斜了。然而,椭圆又该怎么办呢?基本上有两个选择。
(1)使用贝塞尔曲线来近似绘制椭圆。
这个方法可见http://www.china-askpro.com/msg48/qa12.shtml 。
(2)使用一个定制的函数来画椭圆。
这里介绍一下Bresenham椭圆绘画方法的衍生方法,一般Bresenham的椭圆方法大家可以寻找计算机图形学教程。
我这里加上了各种系数,如轮廓颜色、旋转角度、线宽、填充颜色等!
代码之所以冗长了点,是因为要模拟各种轮廓线条类型,如果大家不需要,可以进行适当的裁剪,得到您需要的功能。
呵呵,在这里最关键的是共享一下代码,
C++代码如下:
#include "math.h"
#define SKEEP_DASH 3
#define SKEEP_DOT 1
//CDC *pDC: 图形设备
//float xc ,float yc: 椭圆中心点
//float fla: 椭圆x轴长度(水平时)
//float flb: 椭圆y轴长度(水平时)
//COLORREF color: 椭圆颜色
//double angle: 椭圆旋转角度
//int nPenWidth: 笔宽
//Creater: handwolf 2003-12-14
void CEllipse::BreEllipse(CDC *pDC,float xc ,float yc,float fla,float flb,COLORREF color,int nPenStyle,double angle,int nPenWidth)
{
int i;
float a,b,x,y;
double dx,dy,di,aa,bb,sinA,cosA;
cosA=cos(angle);
sinA=sin(angle);
a= fla ;
b=flb;
aa=a*a;
bb=b*b;
float flX[4],flY[4];
x=0;
y=b;
dx=0;
dy=2*aa*y;
di=bb-aa*b+aa/4;
flX[0]=xc+x*cosA-y*sinA;
flX[1]=xc+x*cosA+y*sinA;
flX[2]=xc-x*cosA-y*sinA;
flX[3]=xc-x*cosA+y*sinA;
flY[0]=yc+y*cosA+x*sinA;
flY[1]=yc-y*cosA+x*sinA;
flY[2]=yc+y*cosA-x*sinA;
flY[3]=yc-y*cosA-x*sinA;
i=0;
int bDot1 = true, bDot2 = false, bDash = false;//for DashDot/DashDotDot
int nSkeepInit,nSkeep = 0;
switch(nPenStyle) {
case PS_DOT:
case PS_DASHDOT:
case PS_DASHDOTDOT:
nSkeepInit = SKEEP_DOT;
break;
case PS_DASH:
nSkeepInit = SKEEP_DASH;
break;
default:
break;
}
nSkeep = 0;
while(dx<dy)
{
i++;
if(i==3)
{
switch(nPenStyle) {
case PS_SOLID:// ____________
pDC->MoveTo(flX[0],flY[0]);
pDC->LineTo(xc+x*cosA-y*sinA,yc+y*cosA+x*sinA);
pDC->MoveTo(flX[1],flY[1]);
pDC->LineTo(xc+x*cosA+y*sinA,yc-y*cosA+x*sinA);
pDC->MoveTo(flX[2],flY[2]);
pDC->LineTo(xc-x*cosA-y*sinA,yc+y*cosA-x*sinA);
pDC->MoveTo(flX[3],flY[3]);
pDC->LineTo(xc-x*cosA+y*sinA,yc-y*cosA-x*sinA);
break;
case PS_DOT://...........
case PS_DASH://-------------
++nSkeep;
if( nSkeep <= nSkeepInit){
pDC->MoveTo(flX[0],flY[0]);
pDC->LineTo(xc+x*cosA-y*sinA,yc+y*cosA+x*sinA);
pDC->MoveTo(flX[1],flY[1]);
pDC->LineTo(xc+x*cosA+y*sinA,yc-y*cosA+x*sinA);
pDC->MoveTo(flX[2],flY[2]);
pDC->LineTo(xc-x*cosA-y*sinA,yc+y*cosA-x*sinA);
pDC->MoveTo(flX[3],flY[3]);
pDC->LineTo(xc-x*cosA+y*sinA,yc-y*cosA-x*sinA);
}else{
nSkeep = 0;
}
break;
case PS_DASHDOT://__ . __ . __.
++nSkeep;
if( nSkeep <= nSkeepInit){
pDC->MoveTo(flX[0],flY[0]);
pDC->LineTo(xc+x*cosA-y*sinA,yc+y*cosA+x*sinA);
pDC->MoveTo(flX[1],flY[1]);
pDC->LineTo(xc+x*cosA+y*sinA,yc-y*cosA+x*sinA);
pDC->MoveTo(flX[2],flY[2]);
pDC->LineTo(xc-x*cosA-y*sinA,yc+y*cosA-x*sinA);
pDC->MoveTo(flX[3],flY[3]);
pDC->LineTo(xc-x*cosA+y*sinA,yc-y*cosA-x*sinA);
}else{
nSkeep = 0;
if(bDot1 == true){
bDot1 = false;
bDash = true;
nSkeepInit = SKEEP_DASH;
}else{
bDash = false;
bDot1 = true;
nSkeepInit = SKEEP_DOT;
}
}
break;
case PS_DASHDOTDOT://__ . . __ . . __ . .
++nSkeep;
if( nSkeep <= nSkeepInit){
pDC->MoveTo(flX[0],flY[0]);
pDC->LineTo(xc+x*cosA-y*sinA,yc+y*cosA+x*sinA);
pDC->MoveTo(flX[1],flY[1]);
pDC->LineTo(xc+x*cosA+y*sinA,yc-y*cosA+x*sinA);
pDC->MoveTo(flX[2],flY[2]);
pDC->LineTo(xc-x*cosA-y*sinA,yc+y*cosA-x*sinA);
pDC->MoveTo(flX[3],flY[3]);
pDC->LineTo(xc-x*cosA+y*sinA,yc-y*cosA-x*sinA);
}else{
nSkeep = 0;
if(bDot1 == true){
bDot1 = false;
bDot2 = true;
nSkeepInit = SKEEP_DOT;
}else if(bDot2 == true){
bDot2 = false;
bDash = true;
nSkeepInit = SKEEP_DASH;
}else{//bDash == true
bDash = false;
bDot1 = true;
nSkeepInit = SKEEP_DOT;
}
}
break;
default:
break;
}
flX[0]=xc+x*cosA-y*sinA;
flX[1]=xc+x*cosA+y*sinA;
flX[2]=xc-x*cosA-y*sinA;
flX[3]=xc-x*cosA+y*sinA;
flY[0]=yc+y*cosA+x*sinA;
flY[1]=yc-y*cosA+x*sinA;
flY[2]=yc+y*cosA-x*sinA;
flY[3]=yc-y*cosA-x*sinA;
i=0;
}
x++;
dx+=2*bb;
di+=dx+bb;
if(di>=0)
{
dy-=2*aa;
di-=dy;
y--;
}
}
pDC->MoveTo(flX[0],flY[0]);
pDC->LineTo(xc+x*cosA-y*sinA,yc+y*cosA+x*sinA);
pDC->MoveTo(flX[1],flY[1]);
pDC->LineTo(xc+x*cosA+y*sinA,yc-y*cosA+x*sinA);
pDC->MoveTo(flX[2],flY[2]);
pDC->LineTo(xc-x*cosA-y*sinA,yc+y*cosA-x*sinA);
pDC->MoveTo(flX[3],flY[3]);
pDC->LineTo(xc-x*cosA+y*sinA,yc-y*cosA-x*sinA);
di+=int((3*(aa-bb)-2*(dx-dy))/4+0.5);
flX[0]=xc+x*cosA-y*sinA;
flX[1]=xc+x*cosA+y*sinA;
flX[2]=xc-x*cosA-y*sinA;
flX[3]=xc-x*cosA+y*sinA;
flY[0]=yc+y*cosA+x*sinA;
flY[1]=yc-y*cosA+x*sinA;
flY[2]=yc+y*cosA-x*sinA;
flY[3]=yc-y*cosA-x*sinA;
i=0;
bDot1 = true;//for DashDot/DashDotDot
bDot2 = false;
bDash = false;
switch(nPenStyle) {
case PS_DOT:
case PS_DASHDOT:
case PS_DASHDOTDOT:
nSkeepInit = SKEEP_DOT;
break;
case PS_DASH:
nSkeepInit = SKEEP_DASH;
break;
default:
break;
}
nSkeep = 0;
while(y>0)
{
i++;
if(i==5)
{
switch(nPenStyle) {
case PS_SOLID:// ____________
pDC->MoveTo(flX[0],flY[0]);
pDC->LineTo(xc+x*cosA-y*sinA,yc+y*cosA+x*sinA);
pDC->MoveTo(flX[1],flY[1]);
pDC->LineTo(xc+x*cosA+y*sinA,yc-y*cosA+x*sinA);
pDC->MoveTo(flX[2],flY[2]);
pDC->LineTo(xc-x*cosA-y*sinA,yc+y*cosA-x*sinA);
pDC->MoveTo(flX[3],flY[3]);
pDC->LineTo(xc-x*cosA+y*sinA,yc-y*cosA-x*sinA);
break;
case PS_DOT://...........
case PS_DASH://-------------
++nSkeep;
if( nSkeep <= nSkeepInit){
pDC->MoveTo(flX[0],flY[0]);
pDC->LineTo(xc+x*cosA-y*sinA,yc+y*cosA+x*sinA);
pDC->MoveTo(flX[1],flY[1]);
pDC->LineTo(xc+x*cosA+y*sinA,yc-y*cosA+x*sinA);
pDC->MoveTo(flX[2],flY[2]);
pDC->LineTo(xc-x*cosA-y*sinA,yc+y*cosA-x*sinA);
pDC->MoveTo(flX[3],flY[3]);
pDC->LineTo(xc-x*cosA+y*sinA,yc-y*cosA-x*sinA);
}else{
nSkeep = 0;
}
break;
case PS_DASHDOT://__ . __ . __.
++nSkeep;
if( nSkeep <= nSkeepInit){
pDC->MoveTo(flX[0],flY[0]);
pDC->LineTo(xc+x*cosA-y*sinA,yc+y*cosA+x*sinA);
pDC->MoveTo(flX[1],flY[1]);
pDC->LineTo(xc+x*cosA+y*sinA,yc-y*cosA+x*sinA);
pDC->MoveTo(flX[2],flY[2]);
pDC->LineTo(xc-x*cosA-y*sinA,yc+y*cosA-x*sinA);
pDC->MoveTo(flX[3],flY[3]);
pDC->LineTo(xc-x*cosA+y*sinA,yc-y*cosA-x*sinA);
}else{
nSkeep = 0;
if(bDot1 == true){
bDot1 = false;
bDash = true;
nSkeepInit = SKEEP_DASH;
}else{
bDash = false;
bDot1 = true;
nSkeepInit = SKEEP_DOT;
}
}
break;
case PS_DASHDOTDOT://__ . . __ . . __ . .
++nSkeep;
if( nSkeep <= nSkeepInit){
pDC->MoveTo(flX[0],flY[0]);
pDC->LineTo(xc+x*cosA-y*sinA,yc+y*cosA+x*sinA);
pDC->MoveTo(flX[1],flY[1]);
pDC->LineTo(xc+x*cosA+y*sinA,yc-y*cosA+x*sinA);
pDC->MoveTo(flX[2],flY[2]);
pDC->LineTo(xc-x*cosA-y*sinA,yc+y*cosA-x*sinA);
pDC->MoveTo(flX[3],flY[3]);
pDC->LineTo(xc-x*cosA+y*sinA,yc-y*cosA-x*sinA);
}else{
nSkeep = 0;
if(bDot1 == true){
bDot1 = false;
bDot2 = true;
nSkeepInit = SKEEP_DOT;
}else if(bDot2 == true){
bDot2 = false;
bDash = true;
nSkeepInit = SKEEP_DASH;
}else{//bDash == true
bDash = false;
bDot1 = true;
nSkeepInit = SKEEP_DOT;
}
}
break;
default:
break;
}
flX[0]=xc+x*cosA-y*sinA;
flX[1]=xc+x*cosA+y*sinA;
flX[2]=xc-x*cosA-y*sinA;
flX[3]=xc-x*cosA+y*sinA;
flY[0]=yc+y*cosA+x*sinA;
flY[1]=yc-y*cosA+x*sinA;
flY[2]=yc+y*cosA-x*sinA;
flY[3]=yc-y*cosA-x*sinA;
i=0;
}
y--;
dy-=2*aa;
di+=aa-dy;
if(di<0)
{
dx+=2*bb;
di+=dx;
x++;
}
}
pDC->MoveTo(flX[0],flY[0]);
pDC->LineTo(xc+x*cosA-y*sinA,yc+y*cosA+x*sinA);
pDC->MoveTo(flX[1],flY[1]);
pDC->LineTo(xc+x*cosA+y*sinA,yc-y*cosA+x*sinA);
pDC->MoveTo(flX[2],flY[2]);
pDC->LineTo(xc-x*cosA-y*sinA,yc+y*cosA-x*sinA);
pDC->MoveTo(flX[3],flY[3]);
pDC->LineTo(xc-x*cosA+y*sinA,yc-y*cosA-x*sinA);
}