用vc怎么画旋转(非线性)椭圆

介绍
    窗口中的矩形,带圆角的矩形和椭圆只能由GDI在轴向上绘制。假如有人希望在Windows NT下绘制旋转或歪斜的图形,他可以使用世界坐标系变换。很不幸的是在Windows 95/98下,是没有世界坐标系变换的。作为一个跨平台的解决方案,就需要自己做更多的工作。矩形能由四边形模拟,这样它就能旋转和歪斜了。然而,椭圆又该怎么办呢?基本上有三个选择。
    两种选择
    使用一个定制的函数来画椭圆。
    椭圆的数学模型相对简单,而且还有用于在标准文本中旋转椭圆的修改过的Bresenham方程。然而,这种方法必须自己执行光栅操作,这样在绘制宽线时就变得复杂了。这种努力只有在向一个脱离屏幕的表面(比如DirectDraw)或位图上绘制视才是值得的 用连接的线段来绘制椭圆。
    实际的线条可以通过LineTo(...)或Polyline(...)图形设备接口调用。你可以自己完成椭圆的近似,或者使用GDI的FlattenPath(...) 函数。
    使用贝塞尔曲线来近似绘制椭圆。
    这里就举例说明这种方法。
    用贝塞尔曲线绘制椭圆
    使用四条贝塞尔曲线,每条代表原轴向椭圆的90度,这样就能获得一个相当近似的椭圆,最大误差只有0.027%。这个最大误差相当于长径3700的椭圆的误差小于一个像素,这已经超出我们所要求的准确度了。
    优点
     简单。
     只需要有四个GDI调用。贝塞尔曲线控制点的计算代价是很小的。
     快速
    你可以利用现在新的显卡对曲线绘制的硬件支持。在我的系统上,这和调用GDI函数Ellipse(...)绘制椭圆的速度比,如果不是更快,至少也是一样快。
     变化
    因为贝塞尔曲线在旋转、缩放和平移时是不变的,在对椭圆做同样的变化时就只需要传送控制点。更巧的是,因为在一个三次贝塞尔曲线上的每个点都是控制点的重心组合,在仿射映射中曲线上控制点之间的关系是不变的。
     设备无关性
     假如想自己把椭圆转化为线段或光栅,那么每次表面的分辨率和设备描述表改变时(例如向一个打印机设备描述表绘制时),就必须重新光栅化。而使用贝塞尔曲线时就不需要这样做。还有一个好处就是椭圆能通过图元文件输出到绘画程序,例如CORELDRAW,在其中可以没有失真的缩放图形。
     过程
     首先以一个轴向椭圆的外接边界矩形开始(使用一个普通的GDI调用)。13个定义4条组成椭圆的贝塞尔曲线的控制点(以下标为0-12)可使用一个经验常量计算得出。下列代码为Y轴正方向向下的的映射模式产生控制点(例如MM_TEXT)。在Y轴正方向向上时,只要如注释中所示,把偏移量设为负值就行了。
     // Create points to simulate ellipse using beziers
    
//使用贝塞尔曲线创建点,模拟椭圆
    
void EllipseToBezier(CRect& r, CPoint* cCtlPt)
    
  // MAGICAL CONSTANT to map ellipse to beziers
    
  //
    
/3*(sqrt(2)-1)
    
  // 把椭圆映射为贝塞尔曲线的常量 2/3*(sqrt(2)-1)
    
  const double EToBConst =
    
.2761423749154;
    
  CSize offset((int)(r.Width() * EToBConst), (int)(r.Height() * EToBConst));
    
// Use the following line instead for mapping systems where +ve Y is upwards
    
// 在Y轴正方向向上时,使用下面一行
    
// 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
    
// LDPoint是一个和CPoint相当的类型,不过它还具有浮点精度。
    
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);
    

    填充椭圆
    当然,无论是不是旋转,四条贝塞尔曲线只完成了椭圆的轮廓。幸运的是,Win32路径功能可用于填充椭圆。你只在需要调用PolyBezier(...)来封闭路径。完成的路径是一笔画出的,而且能被让人满意的填充。假如有人觉得还不够,比如更特殊的填充,比如斜线、用户位图或不规则碎片等。这些能由SelectClipPath(...)来把剪贴区域设置到路径上来而获得。
    dc.BeginPath();
    
dc.PolyBezier(ellipsePts);
    
dc.EndPath();
    
dc.StrokePath;
    
// or FillPath();
    
// or StrokeAndFillPath();
    
// or PathToRegion(dc.m_hDC);
    
//
    
//或者 FillPath();
    
//或者StrokeAndFillPath();
    
//或者PathToRegion(dc.m_hDC);
    
    
在Win95/8下宽的虚线或点椭圆轮廓。Win95/8只支持实体宽线。然而,虚线或点椭圆轮廓能容易的由一系列贝塞尔曲线段模拟。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值