MFC上如何繪製一個可以旋轉的橢圓

問題描述: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);


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章