计算机图形学 小型绘图系统
一、实验目的
1.熟悉计算机图形的生成算法
2.用编程的方法实现计算机的生成算法
3.巩固计算机图形学中学到的一些理论知识
二、实验环境
软件:windows7 系统 JavaScript
三、实验内容与步骤(可提供屏幕抓图)
1)绘制直线 (1)DDA
算法的原理
DDA 算法是根据直线的微分方程来计算Δx 或Δy 生成直线的扫描转
换算法。 在一个坐标轴上以单位间隔对线段取样, 以决定另一个坐标轴方向上最靠近理想线段的整数值。
设(x 0, y 0)为直线段的始点, (x 1, y 1)为直线段的终点, 且端点坐标均为整数, 则直线的微分方程为
设|k |≤1, 则有
yi +1=kxi +1+b =k (xi +Δx )+b =yi +k Δx 上式表明, 若Δx =1, 则当x 每递增1时, y 递增k 。
扫描转换开始时, 取直线始点(x 0, y 0)作为初始坐标。 程序代码:
void DDA -line(x0, y0, x1, y1, color) int x0, y0, x1, y1, color; {int x;
float y, k, deltx, delty; deltx=x1-x0; delty=y1-y0; k=delty /deltx; y=y0;
for(x=x0; x<=x1; x++)
{ putpixel(x, int(y+0.5), clolor); y=y+k; } 实现截图
k
x y x x y y =??=--0101
DDA算法绘制直线
(2)中点法
算法的原理
为了讨论的方便, 假定直线的斜率在0~1之间, 其它情况参照下述讨论进行处理。假设直线的起点和终点分别为(x0, y0)和(x1, y1), 则直线方程为
F(x, y)=ax+by+c=0
其中, a=y0-y1, b=x1-x0, c=x0y1-x1y0。对于直线上的点, F(x,y)=0;
对于直线上方的点, F(x,y)>0; 而对于直线下方的点, F(x,y)<0。如图3.1所示, 若直线在x方向上增加一个单位, 则在y方向上的增量只能在0和1之间。假设横坐标为x P的各像素点中最佳逼近于理想直线的像素为(x P,y P), 用实心小圆表示。那么, 下一个与直线最近的像素只能是正右方的P1(x P+1,y P)或右上方的P
(x P+1, y P+1)两者之一, 用空心小圆表示。我们用P1和P2的中
2
点M(x P+1, y P+0.5)与理想直线的位置关系来判定。
中点画线示意图
●程序
void MidPoint-Line(x0, y0, x1, y1, color) int x0, y0, x1, y1, color;
{ int a, b, delta1, delta2, d, x, y;
a=y0-y1;
b=x1-x0;
d=2*a+b
delta1=2*a;
x=x0;
y=y0;
putpixel(x, y, color);
while(x { if(d<0) { x++; y++; d+=delta2; } else { x++; d+=delta1; } putpixel(x, y, color); } } } ●实现截图 (3)Bresenham算法 ●算法的原理 Bresenham画线算法与中点画线法有相似之处, 也是通过在每列像素中确定与理想直线最近的像素来进行直线的扫描转换 的。为了讨论的方便,不妨也假定直线的斜率在0~1之间。如 图所示, 过各行、各列像素中心构造一组虚拟网格线, 按直线从 起点到终点的顺序计算直线与各垂直网格线的交点, 然后确定该 列像素中与该交点最近的像素。 Bresenh a m算法误差项d的几何意义 ●程序 class drawings implements Serializable//父类,基本图形单元,用到串行化接口,保存时所用 { int x1, y1, x2, y2; //定义坐标属性 int R, G, B; //定义色彩属性 float stroke; //定义线条粗细属性 int type; //定义字体属性 String s1; String s2; //定义字体风格属性 void draw(Graphics2D g2d) { } ;//定义绘图函数 } public void BresenhamLine(Graphics g,int x1,int y1,int x2,int y2) throws Exception{ int x,y,dx,dy,e; dx=x2-x1; dy=y2-y1; e=-dx; x=x1; y=y1; while(x<=x2){ g.setColor(Color.red); pset(g,x,y); x++; e=e+2*dy; if(e>0){ y++; e=e-2*dx; } } class Line extends drawings //直线类 { void draw(Graphics2D g2d) { g2d.setPaint(new Color(R, G, B)); g2d.setStroke(new BasicStroke(stroke, BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL)); g2d. BresenhamLine (g2d ,x1,y1,x2,y2); } } 实现截图 2)圆 算法的原理 为了讨论的方便, 我们考虑中心在原点, 半径为R的圆的第二个八分圆弧, 圆的其它部分可通过一系列的简单的反射变换得到。也就是讨论如何从(0, R)到 (R / ,R/ )顺时针确定最佳逼近于该圆弧的像素序列。 中心在原点, 半径为R的圆的方程为 x2+y2=R2 若令F(x,y)=x2+y2-R2, 则上述方程为 F(x, y)=0 如图所示, 假定x坐标为x P的像素中最佳逼近理想圆弧的为P(x P , y P), 那么, 下一个像素只能是正右方的P1(x P+1, y P)或右下方的P2(x P+1, y P-1)两者之一。引入P1和P2的中点M(x P+1, y P-0.5), 当M在圆内时, 应取P1(x P+1, y P)为下一个像素, 否则, 应取 P 2 (x P+1, y P-1)为下一个像素。为此, 构造判别式 d=F(M)=F(x P +1, y P-0.5)=(x P+1)2+(y P-0.5)2-R2 中点画圆法 若d<0, 则应取P1(x P+1, y P)为下一个像素, 而且再下一个像素的判别式为 d′=F(x P +2, y P-0.5)=(x P+2)2+(y P-0.5)2-R2 =d+2x P+3 而d≥0, 则应取P2(x P+1, y P-1)为下一个像素, 而且再下一个像素的判别式为 22 d′=F(x +2, y P-1.5)=(x P+2)2+(y P-1.5)2-R2 P =d+2(x P-y P)+5 由于第一个像素是(0,R), 因而d的初始值为 d =F(1, R-0.5)=1.25-R 程序 void Bresenham-Circle(r, color) Int r, color; { int x, y, delta, delta1, delta2, direction; x=0; y=r; delta=2*(1-r); while(y>=0) { putpixel(x, y, color); if(delta<0) { delta1=2*(delta+y)-1; if(delta1<=0) diretion=1; else direction=2; } else if(delta>0) { delta2=2*(delta-x)-1; if(delta2<=0) direction=2; else direcion=3; } else direction=2; switch(direction) { case 1:x++; delta+=2*x+1; break; case 2: x++; y--; delta+=2*(x-y+1) break; case 3: y--; delta+=(-2*y+1); break; } } } 程序截图 绘制圆 3)椭圆 算法的原理 中点画圆法可以推广到一般二次曲线的生成, 下面以中心在原点的标准椭圆的扫描转换为例说明。设椭圆的方程为 F(x,y)=b2x2+a2y2-a2b2=0 其中, a为沿x轴方向的长半轴长度, b为y轴方向的短半轴长度, a、b均为整数。不失一般性, 我们只讨论第一象限椭圆弧的生成。需要注意的是, 在处理这段椭圆时, 必须以弧上斜率为-1的点(即法向量两个分量相等的点)作为分界把它分为上部分和下部分, 如图所示。 第一象限的椭圆弧 该椭圆上一点(x , y )处的法向量为 其中, i 和j 分别为沿x 轴和y 轴方向的单位向量。 从图3.6可 看出, 在上部分, 法向量的y 分量更大, 而在下部分, 法向量的x 分量更大, 因而, 在上部分若当前最佳逼近理想椭圆弧的像素(x P ,y P )满足下列不等式 b 2(x P +1)<a 2(y P -0.5) 而确定的下一个像素不满足上述不等式, 则表明椭圆弧从上部 分转入下部分。 在上部分, 假设横坐标为x P 的像素中与椭圆弧更接近点是(x P , y P ), 那么下一对候选像素的中点是(x P +1, y P -0.5)。 因此判别式为 d 1=F (x P +1, y P -0.5)=b 2(x P +1)2+a 2(yP -0.5)2-a 2b 2 若d 1<0, 中点在椭圆内, 则应取正右方像素, 且判别式应更新为 d ′1=F (x P +2, y P -0.5)=b 2(x P +2)2+a 2(y P -0.5)2-a 2b 2 =d 1+b 2(2x P +3) 当d 1≥0, 中点在椭圆之外, 这时应取右下方像素, 并且更新判别式为 d ′1=F (x P +2, y P -1.5)=b 2(x P +2)2+a 2(y P -1.5)2-a 2b 2 =d 1+b 2(2x P +3)+a 2(-2y P +2) 由于弧起点为(0, b ), 因此, 第一中点是(1, b -0.5), 对应的 判别式是 d 10=F (1, b -0.5)=b 2+a 2(b -0.5)2-a 2b 2 yi a xi b j y F i x F y x N 2222),(+=??+??= =b2+a2(-b+0.25) 在下部分, 应改为从正下方和右下方两个像素中选择下一像素。如 果在上部分所选择的最后一像素是(x P, y P), 则下部分的中点判别 式d2的初始值为 d =F(x P+0.5, y P-1)=b2(x P+0.5)2+a2(yP-1)2-a2b2 20 d 在正下方向与右下方向的增量计算与上部分类似, 这里不再2 赘述。下部分弧的终止条件是y=0。 程序 程序代码: void MidpointEllipse(a, b, color) int a, b, color; { int x, y; float d1, d2; x=0; y=b; d1=b*b+a*a*(-b+0.25); putpixel(x, y, color);