MFC上如何绘制一个可以旋转的椭圆

本文介绍在Windows环境下,利用GDI和贝塞尔曲线两种方法实现任意角度旋转椭圆的绘制技巧。首先通过世界坐标系变换,使用SetWorldTransform函数进行椭圆旋转;其次采用4条贝塞尔曲线模拟椭圆,并通过旋转控制点来实现椭圆旋转。

问题描述:windows下,GDI可以调用Ellipse直接绘制一个水平或垂直方向的椭圆,但是无法按照一定的角度自由旋转。

解决方法:

1. 使用世界坐标系。通过旋转世界坐标系,达到旋转椭圆的目的。需要使用到SetWorldTransform等一系列的函数。

关于这些函数的使用方法参见如下资料:

http://msdn.microsoft.com/en-us/library/windows/desktop/dd145104%28v=vs.85%29.aspx

http://msdn.microsoft.com/en-us/library/windows/desktop/dd145174(v=vs.85).aspx

简单介绍一下旋转椭圆时,如何计算世界坐标系的转换。

椭圆的中心点(x0,y0),从点(x1,y1)旋转到点(x2,y2),旋转角度为q(单位为弧度)。

x2 = cos(q)*(x1-x0) – sin(q)*(y1-y0) + x0;
y2 = sin(q)*(x1-x0) + cos(q)*(y1-y0) + y0;

当使用世界坐标系时,从点(x,y)变换到点(x', y')时,SetWorldTransform需要一个XFORM的参数,其成员的计算关系为:

x’ = x * eM11 + y * eM12 + eDx;
y’ = x * eM12 + y * eM22 + eDy;
因此可以得出:
xform.eM11 = cos(q);
xform.eM12 = sin(q);
xform.eM21 = -sin(q);
xform.eM22 = cos(q);
xform.eDx = x0 – cos(q) * x0 + sin(q) * y0;
xform.eDy = y0 – cos(q) * y0 - sin(q) * x0;

此外,GDI+把上面的一系列世界坐标系的函数封装到了Graphics类里了(头文件为GdiPlusGraphics.h)。

方法缺陷:win95/98不支持世界坐标系,有平台限制。

2. 使用4条贝赛尔曲线来模拟绘制椭圆,而贝塞尔曲线是可以旋转的。

从一个水平或垂直方向的椭圆(未旋转的)的外接边界矩形,计算出13个控制点(0-12)用以控制4条贝塞尔曲线。
下面的计算方法是以Y轴向下为标准的。

// Create points to simulate ellipse using beziers
void EllipseToBezier(CRect& r, CPoint* cCtlPt)
{
    // MAGICAL CONSTANT to map ellipse to beziers
    //   2/3*(sqrt(2)-1) 
    const double EToBConst = 0.2761423749154; 
    CSize offset((int)(r.Width() * EToBConst), (int)(r.Height() * EToBConst));
    //  Use the following line instead for mapping systems where +ve Y is upwards
    //  CSize offset((int)(r.Width() * EToBConst), -(int)(r.Height() * EToBConst));
    CPoint centre((r.left + r.right) / 2, (r.top + r.bottom) / 2);
    cCtlPt[0].x  =                                      //------------------------/
    cCtlPt[1].x  =                                      //                              /
    cCtlPt[11].x =                                    //         2___3___4       /
    cCtlPt[12].x = r.left;                            //     1                    5    /
    cCtlPt[5].x  =                                       //     |                     |    /
    cCtlPt[6].x  =                                       //     |                     |    /
    cCtlPt[7].x  = r.right;                          //     0,12                6    /
    cCtlPt[2].x  =                                       //     |                     |    /
    cCtlPt[10].x = centre.x - offset.cx;       //     |                     |    /
    cCtlPt[4].x  =                                      //    11                   7    /
    cCtlPt[8].x  = centre.x + offset.cx;      //       10___9___8       /
    cCtlPt[3].x  =                                     //                                 /
    cCtlPt[9].x  = centre.x;                      //------------------------*
    cCtlPt[2].y  =
    cCtlPt[3].y  =
    cCtlPt[4].y  = r.top;
    cCtlPt[8].y  =
    cCtlPt[9].y  =
    cCtlPt[10].y = r.bottom;
    cCtlPt[7].y  =
    cCtlPt[11].y = centre.y + offset.cy;
    cCtlPt[1].y  =
    cCtlPt[5].y  = centre.y - offset.cy;
    cCtlPt[0].y  =
    cCtlPt[12].y =
    cCtlPt[6].y  = centre.y;
}

// LDPoint is an equivalent type to CPoint but with floating point precision
void Rotate(double radians, const CPoint& c, CPoint* vCtlPt, unsigned Cnt)
{
    double sinAng           = sin(radians);
    double cosAng           = cos(radians);
    LDPoint constTerm(c.x - c.x * cosAng - c.y * sinAng,
                      c.y + c.x * sinAng - c.y * cosAng);
    
    for (int i = Cnt-1; i>=0; --i)
    {
        vCtlPt[i] = (LDPoint(vCtlPt[i].x * cosAng + vCtlPt[i].y * sinAng,
                             -vCtlPt[i].x * sinAng + vCtlPt[i].y * cosAng) + constTerm).GetCPoint();
    }
}

// Create Ellipse
CRect rect; GetClientRect(&rect);
CPoint ellipsePts[13];
EllipseToBezier(ellipseR, ellipsePts);
// Rotate
Rotate(m_Radians, midPoint, ellipsePts, 13);


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值