图形旋转和歪斜的椭圆

图形旋转和歪斜的椭圆

介绍

窗口中的矩形,带圆角的矩形和椭圆只能由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

//使用贝塞尔曲线创建点,模拟椭圆

voidEllipseToBezier(CRect& r, CPoint* cCtlPt)

{

// MAGICAL CONSTANT to map ellipse to beziers

// 2/3*(sqrt(2)-1)

// 把椭圆映射为贝塞尔曲线的常量 2/3*(sqrt(2)-1)

const double EToBConst = 0.[1**********]54;

CSizeoffset((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));

CPointcentre((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;

}

Rotation of the Ellipse can be accomplished using code similar to:

使用与下面近似的代码可完成椭圆的旋转

// LDPoint is an equivalent type to CPoint but with floating point precision

// LDPoint是一个和CPoint 相当的类型,不过它还具有浮点精度。

void Rotate(double radians, constCPoint& c, CPoint* vCtlPt, unsigned Cnt) {

doublesinAng = sin(radians);

doublecosAng = cos(radians);

LDPointconstTerm(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

// 创建椭圆

CRectrect; GetClientRect(&rect);

CPointellipsePts[13];

EllipseToBezier(ellipseR, ellipsePts);

// Rotate

// 旋转

Rotate(m_Radians, midPoint, ellipsePts, 13);

Filled Ellipses

Of course, four bezier curves together only make up the outline of an ellipse, whether rotated or not. Thankfully, Win32 Path functions are there for filled ellipses. One only needs to enclose the PolyBezier(...) call in a Path Bracket. The resulting path can be stroked and filled to one's satisfaction. If one is feeling adventurous, further special fills like gradients, custom bitmaps or fractals etc. can be achieved by first setting the clipping region to the path via SelectClipPath(...).

填充椭圆

当然,无论是不是旋转,四条贝塞尔曲线只完成了椭圆的轮廓。幸运的是, 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只支持实体宽线。然而,虚线或点椭圆轮廓能容易的由一系列贝塞尔曲

线段模拟。

图形旋转和歪斜的椭圆

介绍

窗口中的矩形,带圆角的矩形和椭圆只能由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

//使用贝塞尔曲线创建点,模拟椭圆

voidEllipseToBezier(CRect& r, CPoint* cCtlPt)

{

// MAGICAL CONSTANT to map ellipse to beziers

// 2/3*(sqrt(2)-1)

// 把椭圆映射为贝塞尔曲线的常量 2/3*(sqrt(2)-1)

const double EToBConst = 0.[1**********]54;

CSizeoffset((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));

CPointcentre((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;

}

Rotation of the Ellipse can be accomplished using code similar to:

使用与下面近似的代码可完成椭圆的旋转

// LDPoint is an equivalent type to CPoint but with floating point precision

// LDPoint是一个和CPoint 相当的类型,不过它还具有浮点精度。

void Rotate(double radians, constCPoint& c, CPoint* vCtlPt, unsigned Cnt) {

doublesinAng = sin(radians);

doublecosAng = cos(radians);

LDPointconstTerm(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

// 创建椭圆

CRectrect; GetClientRect(&rect);

CPointellipsePts[13];

EllipseToBezier(ellipseR, ellipsePts);

// Rotate

// 旋转

Rotate(m_Radians, midPoint, ellipsePts, 13);

Filled Ellipses

Of course, four bezier curves together only make up the outline of an ellipse, whether rotated or not. Thankfully, Win32 Path functions are there for filled ellipses. One only needs to enclose the PolyBezier(...) call in a Path Bracket. The resulting path can be stroked and filled to one's satisfaction. If one is feeling adventurous, further special fills like gradients, custom bitmaps or fractals etc. can be achieved by first setting the clipping region to the path via SelectClipPath(...).

填充椭圆

当然,无论是不是旋转,四条贝塞尔曲线只完成了椭圆的轮廓。幸运的是, 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只支持实体宽线。然而,虚线或点椭圆轮廓能容易的由一系列贝塞尔曲

线段模拟。


相关文章

  • 偏振光的研究最好最完整的实验报告
  • 物理实验实验名称:偏振光的研究我的班级:本硕111班我的姓名:龚林吉我的学号:5701111066实验老师:袁吉仁(T035) 实验时间:2012年10月23日下午13:00 报告 目录 偏振光的研究.................... ...查看


  • 交互式电子白板的使用教程
  • 交互式电子白板的使用教程(王沛传) 第一章.概述 TRACEBoard 交互式电子白板是由天士博(TRACEBoard)北京电子技术有限公司自主研发,并拥有完全自主知识产权的国际领先水平系列产品,全球独有的 HIKey(灵巧智能键)技术,让 ...查看


  • 偏振光研究
  • 偏振光的研究 系别:11系 姓名:赵海波 学号:PB06210381 实验目的:学习掌握线偏振光.圆偏振光.椭圆偏振光的产生条件与鉴别方法, 了解马吕斯定律以及各偏振光的数学推导.学习使用计算机鉴别偏振光以及找出消光位置并观察随四分之一波片 ...查看


  • 管道轴测图CAD画法
  • 管道轴测图CAD 画法 轴测图是反映物体三维形状的二维图形,它富有立体感,能帮人们更快更清楚地认识产品结构.绘制一个零件的轴测图是在二维平面中完成,相对三维图形更简洁方便.一个实体的轴测投影只有三个可见平面,为了便于绘图,我们将这三个面作为 ...查看


  • 第七节课 椭圆及圆弧.镜像.旋转
  • 一.(1)椭圆 el 方法:① 点三点,任意画 ②以中心输入半轴长度绘制 el c 点椭圆中心,根据鼠标方向分别输入半轴长度 (2)椭圆弧命令:EL 回车A 回车 方法:EL 回车A 回车,点五点(前三点确定椭圆弧的形状,第四点确定弧起点, ...查看


  • 使用示波器测量电流和电压的方法
  • 使用示波器测量电流和电压的方法 (一)电压的测量 利用示波器所做的任何测量,都是归结为对电压的测量.示波器可以测量各种波形的电压幅度,既可以测量直流电压和正弦电压,又可以测量脉冲或非正弦电压的幅度.更有用的是它可以测量一个脉冲电压波形各部分 ...查看


  • 情人节贺卡
  • 1.5情人节贺卡 2007-10-2 17:46:00 本节要制作一个综合范例――情人节贺卡.在这个范例的制作过程中会使用前面学习的很多绘图工具,并且讲解一些新的绘图方法和技巧. 另外,由于这个范例图形比较复杂,我们还使用了"图形 ...查看


  • 数码照片后期处理
  • 数码照片后期处理 目录 一.基本处理 ..................................................................................................... ...查看


  • 中班科学教学设计[旋转的纸片]
  • 中班科学教学设计<旋转的纸片> 中班科学教学设计<旋转的纸片> 活动背景 自从体育区有了各类民间体育玩具后,孩子们个个玩得不亦乐乎.在玩耍过程中,发现他们对转陀螺游戏情有独钟.于是便从幼儿的兴趣.需要出发,生成了主题 ...查看


热门内容