1、习题习题 4 4 4 4.在本章第一节说明 Bresenham 算法如何选择下一个像素点位置的图 2-3 中,其实是假定了在当前选择的点是(x,y)时,真正直线与横坐标为x+1的直线的交点是在y和y+1之间。如果不是这样,而是下面两种情况:(1)在 y 到 y-1 之间。例如从(0,0)到(7,2)的直线,在点(2,1)处向后。(2)在 y+1 到 y+2 之间。例如从(0,0)到(7,5)的直线,在点(2,1)处向后。试说明为什么对所列两种情况算法仍能正确地工作。解答:解答:在给出两种情况下,Bresenham 算法仍然能够正确工作的原因是判别式 d 保证了在这两种总情况下,仍然能正确的取到
2、最接近真正直线的像素点。在 Bresenham 算法中,按照判别式 d 的正负来决定选择哪一点作为下一个像素点,判别式 d如下计算:d=d1 d2。其中d1=yp y,d2=y+1 yp,y 是当前选中点(x,y)的纵坐标,yp是真正直线与横坐标为 x+1 的直线的交点的纵坐标。当 d0 时,取上点,即(x+1,y+1)作为下一个像素点,当 d0 时,取下点,即(x+1,y)作为下一个像素点。这里还要说明一下,因为此处是按照书上给出的 Bresenham 算法来进行像素选择的,所以实际上有一个先决条件,即直线的斜率处于 0 和 1 之间。只要保证斜率满足条件,在当前选择的点是(x,y)时,真正
3、直线与横坐标为 x+1 的直线的交点即使不在 y 和 y+1 之间,书上所给的Bresenham 都可以正确工作。题目所给两种情况中的直线斜率都满足算法的先决条件。在第(1)种情况下,如下图所示:当前选择点为(x,y),下一点实际为 P 点,该点处于(x+1,y-1)和(x+1,y)两点之间,实际应取(x+1,y)点作为下一个像素点。现在来看一下为什么一定会取(x+1,y)点作为下一个像素点,而不是取(x+1,y-1)点作为下一个像素点。设 Q 点为选择(x,y)点作为显示像素点时实际的直线上的点,根据 Bresenham 算法的特点 Q 点的纵坐标 yq一定满足 y-0,5yqy+0.5,否
4、则不会选择(x,y)点作为显示像素点,因为 P 点的纵坐标处于 y 和 y-1 之间,所以 y-0.5yqy,否则该直线斜率将不满足条件(直线斜率将小于 0)。当 y-0,5yqy 时,则一定有 y-0,5ypy,因为如果 ypy-0.5,则 P 点的纵坐标会比 Q 点纵坐标更小,这时直线的斜率就不满足先决条件了(直线斜率将小于 0)。所以因为直线斜率先决条件的限制,当真正直线与横坐标为 x+1的直线的交点是在 y 和 y-1 之间时,该实际交点一定更靠近(x+1,y)点,所以一定会取(x+1,y)点作为下一个像素点。再来看一下按照 Bresenham 算法计算会取哪一点。从图可知,因为 P点
5、纵坐标 yp在 y-1 和 y 之间,所以有 ypy,ypy+1,所以有 d1=yp-y0,由此可得 d=d1-d2y+1,而如果 yq1,所以直线斜率大于 1)。当 yyqy+0.5 时,则一定有 y+1ypy+1+0.5,因为如果 ypy+1+0.5,同时又有 yqy+0.5,则 yp-yq1,这时直线的斜率大于 1,不满足先决条件。所以因为直线斜率先决条件的限制,当真正直线与横坐标为 x+1 的直线的交点是在y+1 和 y+2 之间时,该实际交点一定更靠近(x+1,y+1)点,所以一定会取(x+1,y+1)点作为下一个像素点。再来看一下按照 Bresenham 算法计算会取哪一点。从图可
6、知,因为 P 点纵坐标yp在 y+1 和 y+2 之间,所以有 ypy,ypy+1,所以有 d1=yp-y0,d2=y+1-yp0,所以要取上点(x+1,y+1)作为下一个像素点,跟实际应取的点是一致的,所以在这种情况下 Bresenham 算法仍然可以正确工作。综上所述,虽然书中所说算法在当前选择的点是(x,y)时,假定了真正直线与横坐标为 x+1的直线的交点是在 y 和 y+1 之间,但只要要绘制的直线斜率满足处于 0 与 1 之间的先决条件,那么即使真正直线与横坐标为 x+1 的直线的交点不是在 y 和 y+1 之间,书上所介绍的Bresenham 算法仍然可以正确工作。习题习题 6 6
7、 6 6.推广本章第二节给出的 Bresenham 画圆算法使能够画出一个内部填充的实心圆。解答:解答:因为 Bresenham 画圆算法在画圆的时候通过绘制对称点来完成整个圆周的绘制,所以只需绘制对称点之间连线上的象素点即可完成对圆形内部的填充。推广算法的实现代码如下:/参数:(x0,y0)为圆心,R 为半径,edgeColor 为圆形边界颜色,inColor 为圆形内部填充颜色void CDraw:BresenhamFillCircle(CDC*pDC,int x0,int y0,int R,COLORREF edgeColor,COLORREF inColor)int x,y,p;int
8、 i;x=0;y=R;p=3-(R1);for(;x=y;x+)for(i=-y+y0;iSetPixel(-x+x0,i,inColor);pDC-SetPixel(x+x0,i,inColor);for(i=-y+x0;iSetPixel(i,x+y0,inColor);pDC-SetPixel(i,-x+y0,inColor);/绘制圆弧上对称的八个像素点pDC-SetPixel(x+x0,y+y0,edgeColor);pDC-SetPixel(-x+x0,y+y0,edgeColor);pDC-SetPixel(x+x0,-y+y0,edgeColor);pDC-SetPixel(-
9、x+x0,-y+y0,edgeColor);pDC-SetPixel(y+x0,x+y0,edgeColor);pDC-SetPixel(-y+x0,x+y0,edgeColor);pDC-SetPixel(y+x0,-x+y0,edgeColor);pDC-SetPixel(-y+x0,-x+y0,edgeColor);if(p 0)p+=(x2)+6);elsep+=(x-y)2)+10);y-;习题习题 8 8 8 8.本章第三节叙述了使用活跃边表的多边形扫描转换算法中 ET 表的填写方法。试写一个算法,输入多边形顶点坐标的逆时针序列,输出正确填写的 ET 表。解答:解答:下面直接给出获
10、得 ET 表的实现代码,为了构造书中所给样式的 ET 表,需要定义如下两个结构:struct EdgeNode/边节点int ymax;/最大 y 值,吊桶数据double xmin;/小 y 值端点的 x 值,吊桶数据double fm;/斜率倒数,吊桶数据EdgeNode*next;/连接下一个边;struct HeadNode/头节点int ymin;/最小 y 值,ET 表中登记项的 y 值EdgeNode*link;/连接边节点HeadNode*next;/连接下一个头节点;下面的实现代码用户获得构造完成的 ET 的头节点的头指针。其中参数 points 存放多边形的顶点的逆时针序列
11、。HeadNode*CTestAView:GetET(CArray*points)HeadNode*pHead=NULL;int ymin;/记录正在处理的当前边的最小 y 值int nymin,nymax;/记录与正在处理的当前边连接的下一边的最小 y 值和最大 y 值/循环点列表构造最初的边链表for(int i=0;iGetSize();i+)/获得当前点和下一点,构成一条边CPoint point1=(CPoint)points-GetAt(i);CPoint point2;/如果当前点已经是最后一个点,则和第一点构成一条边if(i=points-GetSize()-1)point2=
12、(CPoint)points-GetAt(0);elsepoint2=(CPoint)points-GetAt(i+1);/边平行于 x 轴,舍弃if(point1.y=point2.y)continue;/构造边结构EdgeNode*edge=new EdgeNode();edge-next=NULL;/计算斜率倒数edge-fm=(double)(point2.x-point1.x)/(point2.y-point1.y);/设置 ymin,ymax 和 xminif(point1.y point2.y)edge-ymax=point1.y;ymin=point2.y;edge-xmin=
13、point2.x;elseedge-ymax=point2.y;ymin=point1.y;edge-xmin=point1.x;CPoint point3;/获得比较局部极大极小的第三点,该点和第二点够成一条边/如果前两点已经取到顶点序列尾部,则顺序从头取点作为第三点int j=i+2;doif(jGetSize()point3=(CPoint)points-GetAt(j);elsepoint3=(CPoint)points-GetAt(j-points-GetSize();j+;while(point2.y=point3.y);if(point2.y point3.y)/计算 point
14、2 和 point3 点对应的 ymin 和 ymaxnymax=point2.y;nymin=point3.y;elsenymax=point3.y;nymin=point2.y;/下面判断局部极大极小并缩短相应的边/如果连续的两条边的 ymin 值和 ymax 值都不同/表示连接两边的节点不为局部极大或极小if(ymin!=nymin&edge-ymax!=nymax)/缩短当前边/如果当前边的 ymax 值较大,则缩短当前边的 ymin 值if(edge-ymax nymax)ymin+;edge-xmin+=edge-fm;else/否则,缩短当前边的 ymax 值edge-ymax-
15、;/以下处理将当前边加入 ET 表if(pHead=NULL)/当前没有头节点pHead=new HeadNode();pHead-ymin=ymin;pHead-link=edge;pHead-next=NULL;elseAddEdge(pHead,ymin,edge);/返回获得的 ET 表头指针return pHead;在上面的实现代码中用到了一个添加边函数,该函数作用是将处理完成的一条边加入到当前已有的 ET 表中,该函数的实现代码如下:void CTestAView:AddEdge(HeadNode*head,int ymin,EdgeNode*edge)HeadNode*temph
16、ead;while(head!=NULL)/循环头节点if(head-ymin=ymin)/找到了对应的头节点EdgeNode*p=head-link;/获得头节点对应的第一条边节点if(p-xmin edge-xmin)/新加入边的 xmin 值较小head-link=edge;edge-next=p;return;while(p-next!=NULL)/循环当前头节点下所有边节点if(p-next-xmin edge-xmin)edge-next=p-next;p-next=edge;return;p=p-next;/到这里说明新加入的边的 xmin 值比原有的都大p-next=edge;return;else if(head-ymin ymin)/当前头节点的 ymin 值大于新加入的边的 ymin/下面处理新加入头节点HeadNode*newhead=new HeadNode();newhead-ymin=head-ymin;newhead-link=head-link;newhead-next=head-next;head-ymin=ymin;head-link=edge;h
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1