综述

在上一篇文章我们介绍了利用类库来完成一个机器人绘制的过程,这里我们一起来看一下怎样直接利用直线和圆弧生成算法来进行图形的绘制。 P.S. 本篇文章针对《计算机图形学》张彩明 版来探讨学习。关于书中的详细算法不会再赘述。 P.P.S. 本篇文章算法扩展思路及代码实现为博主原创内容,如存在纰漏和错误,希望大家指正。

直线生成算法

1.DDA 算法

DDA 算法是最基本的一种直线生成算法了,代码实现简单,不过缺点是计算量比较大,画一个点要两次加法,两次取整运算。另外,DDA 算法还包括了除法运算。不仅算法复杂,而且硬件实现上有一定的难度。优点就是程序简单易懂,在这里实现如下

123456789101112131415161718192021
//画直线的DDA算法void dda(int x1,int y1,int x2,int y2){int k,i;float x,y,dx,dy;//使k等于横纵坐标差值的较大者 k = abs(x2-x1);if(abs(y2-y1)>k)       k = abs(y2-y1);//直线被划分为每一小段的长度   dx = float(x2-x1)/k; dy = float(y2-y1)/k; x = float(x1);   y = float(y1);for(i = 0;i//此处先加0.5再取整的作用是四舍五入       gl_Point((int)(x+0.5),(int)(y+0.5));//x和y分别增加相应的单位      x = x+dx;       y = y+dy;   }}

解释一下这里的 gl_Point 方法,这个方法并不是直接调用类库的方法,而是我们自己来实现的画点的方法。

123456
//画点void gl_Point(int x,int y){ glBegin(GL_POINTS);   glVertex2i(x,y);  glEnd();}

用来画点的话,我们必须要在 glBegin 方法传入 GL_POINTS 参数,然后利用类库中画点的方法来绘制点。

2. 正负法

在教材中只讨论了斜率在 0-1 之间的情况,代码的实现也是仅仅只有 0-1 一种情况,对于斜率大于 1,斜率在 - 1 和 0 之间以及斜率小于 - 1 的情况没有加以讨论。如果我们直接拿来教材中的代码来用,我们会发现只能绘制出 0-1 斜率的直线,对于其他的情况,均绘制错误。所以我们需要分四种情况来讨论,直线方程 F (x,y)=ax+by+c=0,其中 a=ys-ye,b=xe-xs 设直线的斜率为 k,讨论分类如下 (1)k∈[0,1) 此时有 a<0,b>0 d=F (M)=F (x+1,y+0.5)=a (x+1)+b (y+0.5)+c 当 d>=0 时,Q 在 M 点下方,取右下方的点,d1=F (x+2,y+0.5)=a (x+2)+b (y+0.5)+c=d+a 当 d<0 时,Q 在 M 点上方,取右上方的点,d2=F (x+2,y+1.5)=a (x+2)+b (y+1.5)+c=d+a+b 此时 d 的初始值 d0=F (xs+1,ys+0.5)=a+0.5b **(2)k∈[1,+∞]** 此时有 a<0,b>0 d=F (M)=F (x+0.5,y+1)=a (x+0.5)+b (y+1)+c 当 d>=0 时,Q 在 M 点右侧,取右上方的点,d1=F (x+1.5,y+2)=a (x+1.5)+b (y+2)+c=a+b+d 当 d<0 时,Q 在 M 点左侧,取左上方的点,d2=F (x+0.5,y+2)=b+d 此时 d 的初始值 d0=F (xs+0.5,ys+1)=0.5a+b **(3)k∈[-1,0)** 此时有 a>0,b>0 d=F (M)=F (x+1,y-0.5)=a (x+1)+b (y-0.5)+c 当 d>=0 时,Q 在 M 点下方,取右下方的点,d1=F (x+2,y-1.5)=a (x+2)+b (y-1.5)+c=a-b+d 当 d<0 时,Q 在 M 点上方,取右上方的点,d2=F (x+2,y-0.5)=a (x+2)+b (y-0.5)+c=a+d 此时 d 的初始值 d0=F (xs+1,ys-0.5)=a-0.5b **(4)k∈[-∞,-1)** 此时有 a>0,b>0 d=F (M)=F (x+0.5,y-1)=a (x+0.5)+b (y-1)+c 当 d>=0 时,Q 在 M 点左方,取左下方的点,d1=F (x+0.5,y-2)=a (x+0.5)+b (y-2)+c=d-b 当 d<0 时,Q 在 M 点右方,取右下方的点,d2=F (x+1.5,y-2)=a (x+1.5)+b (y-2)+c=a-b+d 此时 d 的初始值 d0=F (xs+0.5,ys-1)=0.5a-b 注意:上面的 a 和 b 的符号,是在默认起点在终点的左侧来看待的 所以,如果我们传入参数时,第二个点在第一个点的左侧时,我们可能就不会得到正确的结果。所以当我们发现第二个点不在第一个点右侧时,就需要把二者的横纵坐标交换。代码实现如下,此实现仅供参考,未经优化。

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
//画直线的正负法void midPointLine(int xs,int ys,int xe,int ye){if(xs>xe){        swap(xs,xe);      swap(ys,ye);  } float a,b,dt1,dt2,d,x,y;  a=ys-ye; b=xe-xs; float k =(float)(ye-ys)/(xe-xs);if(k>=0&&k<1){        d=2*a+b;        dt1=2*a;     dt2=2*(a+b);    }else if(k>=1){       d=a+2*b;        dt1=2*(a+b);        dt2=2*b; }else if(k<0&&k>=-1){      d=2*a-b;     dt1=2*(a-b);     dt2=2*a; }else if(k      d=a-2*b;     dt1=-2*b;        dt2=2*(a-b); }x=xs;y=ys; gl_Point(x,y);if(k>=0&&k<1){while(xif(d<0){x++;y++;d+=dt2;            }else{x++;d+=dt1;         }         gl_Point(x,y);        } }else if(k>=1){while(yif(d<0){             y++;d+=dt2;           }else{                y++;x++;d+=dt1;         }         gl_Point(x,y);        } }else if(k<0&&k>=-1){while(xif(d<0){x++;d+=dt2;         }else{x++;y--;d+=dt1;         }         gl_Point(x,y);        } }else if(k      while(y>ye){if(d<0){                y--;x++;d+=dt2;           }else{                y--;d+=dt1;         }         gl_Point(x,y);        } }}

在一开始我们用到了 swap 方法,是用来交换两个数字的,实现如下

123456
//交换两个数字void swap(int &x1,int &x2){int temp = x2;    x2=x1;   x1=temp;}

以上便是正负法的实现,代码仅供参考。

3.Bresenham 算法

在教材中,同样是只针对斜率在 0-1 之间讨论。对于教材中的程序,我们也只能绘制斜率为 0-1 的直线,所以我们需要对另外三种情况进行扩充。分类讨论如下 (1)k∈[0,1) 即教材中的讲解方法 (2)k∈[1,+∞] 需要把 x 和 y 互换即可 (3)k∈[-1,0) x 不变,y 换为 - y (4)k∈[-∞,-1) x 换为 - y,y 换为 x 程序实现如下

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
//画直线的Bresenham方法void bresen(int x1,int y1,int x2,int y2){if(x2      swap(x1,x2);      swap(y1,y2);  }float dx=x2-x1;float dy=y2-y1;float k=dy/dx;float m,e;int i;float x=x1,y=y1;if(k>=0&&k<1){//斜率为0到1       m=dy/dx;     e=m-0.5;for(i=0;i         gl_Point(x,y);if(e>=0){               y+=1;e-=1;         }         x+=1;e+=m;        } }else if(k>=1){//x换为y,y换为x        m=dx/dy;     e=m-0.5;for(i=0;i         gl_Point(x,y);if(e>=0){               x+=1;e-=1;         }         y+=1;e+=m;        } }else if(k<0&&k>=-1){//x不变,y换为-y        m=-dy/dx;        e=m+0.5;for(i=0;i            gl_Point(x,y);if(e<=0){               y-=1;e+=1;         }         x+=1;e-=m;     } }else{//将x换为-y,y换为x       m=-dx/dy;        e=m+0.5;for(i=0;i            gl_Point(x,y);if(e<=0){               x+=1;e+=1;            }         y-=1;e-=m;      } }}

以上便是 Bresenham 算法,经测试通过。

圆弧生成算法

1. 正负法

教材中只讨论了圆弧在第一象限的情况,不过有趣的是,圆是具有对称性的,在绘制圆形时,我们如果把 x 换为 - x,就可以绘制第二象限的图形,把 y 换为 - y,就可以绘制第四象限的图形,代码也不需要改动很多。只需要在 gl_Point 上面下功夫即可。另外,教材中圆弧生成算法中没有指定圆的中心点的坐标,我们可以把它当做参数来传递进来,然后传入 gl_Point 绘图函数即可,相当方便。注:此方法不需要再繁琐地分类讨论。在这里给出博主写出的两种方法,一种是如上所介绍的思路,利用对称性,另一种是分类讨论的思想。 (1)利用对称性实现

12345678910111213141516171819202122232425262728293031
//正负法画圆void pnarcArc(int radius,int centerX,int centerY,int area){int x,y,f;   x=0;y=0+radius;f=0;while(y>0){     switch(area){     case 1:           gl_Point(x+centerX,y+centerY);          break;        case 2:           gl_Point(-x+centerX,y+centerY);         break;        case 3:           gl_Point(-x+centerX,-y+centerY);            break;        case 4:           gl_Point(x+centerX,-y+centerY);         break;        }if(f>0){          f=f-2*y+1;          y=y-1;       }else{            f=f+2*x+1;         x=x+1;      } }if(y==centerY){        gl_Point(x,y);    }}

(2)分类讨论思想

123456789101112131415161718192021
//正负法画圆3void pnarcArc(int radius,int centerX,int centerY,int area){int x,y,f;int tag[4]={1,1,-1,-1};int tagX[4]={1,-1,-1,1};int tagY[4]={-1,-1,1,1};    x=0;y=tag[area-1]*radius;f=0;while(tag[area-1]*y>0){        gl_Point(x+centerX,y+centerY);if(f>0){           f=f+tagY[area-1]*2*y+1;            y=y+tagY[area-1];       }else{            f=f+tagX[area-1]*2*x+1;            x=x+tagX[area-1];       } }if(y==centerY){        gl_Point(x,y);    }}

在上面的代码中,我们传入了 centerX 和 centerY 以及 area 参数。其中 centerX 和 centerY 是圆弧中心点的坐标,area 是所在的象限,传入的参数需要是 1,2,3,4 中的一个数字,如果传入其他数字则不会绘制出任何图形。注:此方法只能一次性绘制一个四分之一圆弧,局限性比较大,如果要融入弧度,改动量比较大。

2.Bresenham 算法

和上面方法类似,我们的实现同样非常简单,即使教材中只讨论了八分之一圆,我们可以利用对称的思想来实现画圆。另外我们添加了圆弧中心点坐标已经所在的区块。从(π/4,π/2)这个区块开始,编号为 1,角度为 45°,顺时针旋转(0,π/4)的编号为 2,以此类推,参数变量为 area 代码实现如下

12345678910111213141516171819202122232425262728293031323334353637383940414243
//Bresenham画圆算法void bresenhamArc(int R,int centerX,int centerY,int area){int x,y,d;   x=0;y=R;d=3-2*R;while(x      switch(area){     case 1:           gl_Point(x+centerX,y+centerY);          break;        case 2:           gl_Point(y+centerX,x+centerY);          break;        case 3:           gl_Point(y+centerX,-x+centerY);         break;        case 4:           gl_Point(x+centerX,-y+centerY);         break;        case 5:           gl_Point(-x+centerX,-y+centerY);            break;        case 6:           gl_Point(-y+centerX,-x+centerY);            break;        case 7:           gl_Point(-y+centerX,x+centerY);         break;        case 8:           gl_Point(-x+centerX,y+centerY);         break;        }if(d<0){          d=d+4*x+6;     }else{            d=d+4*(x-y)+10;            y=y-1;       }     x=x+1;  }if(x==y){      gl_Point(x,y);    }}

此段代码亲测可用,仅供参考。

终极目标

上一节我们实现了用类库的方法和 sin,cos 方法来定位坐标绘制机器人,在这一节我们就利用上述的直线和圆弧生成算法,对上一篇中的机器人进行绘制。在这里只贴出最核心的部分,那就是绘画的函数了,只是简单地传入坐标点然后调用刚才实现的一些方法,比较繁琐,但是比较简单,核心代码如下

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
//myDisplay函数用来画图void myDisplay(void){//清除。GL_COLOR_BUFFER_BIT表示清除颜色,glClear函数还可以清除其它的东西 glClear(GL_COLOR_BUFFER_BIT);//设置黑色颜色 glColor3f (0.0f, 0.0f, 0.0f);  //中心的圆圈,四个象限,半径为10   pnarcArc(10,0,0,1);   pnarcArc(10,0,0,2);   pnarcArc(10,0,0,3);   pnarcArc(10,0,0,4);//肚子中三角形,利用了DDA绘制   dda(-30,-15,30,-15);  dda(-30,-15,0,28);    dda(0,28,30,-15);//肚子,四条直线 midPointLine(-60,60,60,60);   midPointLine(-60,-60,60,-60); midPointLine(-74,46,-74,-46); midPointLine(74,46,74,-46);//肚子,四个半圆   pnarcArc(14,60,46,1); pnarcArc(14,-60,46,2);    pnarcArc(14,-60,-46,3);   pnarcArc(14,60,-46,4);//脖子,两条直线   bresen(-25,76,-25,60);    bresen(26,76,25,60);//脸,四条直线   midPointLine(-54,150,54,150); midPointLine(-54,76,54,76);   midPointLine(-64,140,-64,86); midPointLine(64,140,64,86);//脸,四个圆弧    pnarcArc(10,54,140,1);    pnarcArc(10,-54,140,2);   pnarcArc(10,-54,86,3);    pnarcArc(10,54,86,4);//眼睛,两个正圆 pnarcArc(10,-30,111,1);   pnarcArc(10,-30,111,2);   pnarcArc(10,-30,111,3);   pnarcArc(10,-30,111,4);   pnarcArc(10,30,111,1);    pnarcArc(10,30,111,2);    pnarcArc(10,30,111,3);    pnarcArc(10,30,111,4);//嘴巴,两个八分之一圆 bresenhamArc(20,0,111,4); bresenhamArc(20,0,111,5);//天线 dda(-35,150,-35,173); dda(35,150,35,173);//右耳朵,四条直线  bresen(88,98,88,131); bresen(67,98,67,131); bresen(70,95,85,95);  bresen(70,134,85,134);//右耳朵,四个圆弧   pnarcArc(3,85,131,1); pnarcArc(3,70,131,2); pnarcArc(3,70,98,3);  pnarcArc(3,85,98,4);//左耳朵,四条直线 bresen(-88,98,-88,131);   bresen(-67,98,-67,131);   bresen(-70,95,-85,95);    bresen(-70,134,-85,134);//左耳朵,四个圆弧 pnarcArc(3,-70,131,1);    pnarcArc(3,-85,131,2);    pnarcArc(3,-85,98,3); pnarcArc(3,-70,98,4);//左胳膊衔接处 bresen(-73,25,-80,25);    bresen(-73,43,-80,43);//右胳膊衔接处    bresen(73,25,80,25);  bresen(73,43,80,43);//左大臂 dda(-108,45,-108,0);  dda(-81,45,-81,0);    dda(-108,45,-81,45);  dda(-108,0,-81,0);//右大臂   dda(108,45,108,0);    dda(81,45,81,0);  dda(108,45,81,45);    dda(108,0,81,0);//左中臂 bresen(-101,0,-101,-4);   bresen(-88,0,-88,-4);//右中臂    bresen(101,0,101,-4); bresen(88,0,88,-4);//左小臂  dda(-108,-4,-108,-37);    dda(-81,-4,-81,-37);  dda(-108,-4,-81,-4);  dda(-108,-37,-81,-37);//右小臂   dda(108,-4,108,-37);  dda(81,-4,81,-37);    dda(108,-4,81,-4);    dda(108,-37,81,-37);//左手  pnarcArc(10,-95,-47,1);   pnarcArc(10,-95,-47,2);   pnarcArc(10,-95,-47,3);   pnarcArc(10,-95,-47,4);//右手   pnarcArc(10,95,-47,1);    pnarcArc(10,95,-47,2);    pnarcArc(10,95,-47,3);    pnarcArc(10,95,-47,4);//左腿衔接处 dda(-43,-62,-43,-69); dda(-25,-62,-25,-69);//右腿衔接处  dda(43,-62,43,-69);   dda(25,-62,25,-69);//左大腿,四条直线  bresen(-47,-69,-21,-69);  bresen(-47,-117,-21,-117);    bresen(-51,-70,-51,-113); bresen(-17,-70,-17,-113);//左大腿,四条圆弧    pnarcArc(4,-21,-73,1);    pnarcArc(4,-47,-73,2);    pnarcArc(4,-47,-113,3);   pnarcArc(4,-21,-113,4);//右大腿,四条直线  bresen(47,-69,21,-69);    bresen(47,-117,21,-117);  bresen(51,-70,51,-113);   bresen(17,-70,17,-113);//右大腿,四条圆弧  pnarcArc(4,47,-73,1); pnarcArc(4,21,-73,2); pnarcArc(4,21,-113,3);    pnarcArc(4,47,-113,4);//左脚踝   dda(-43,-118,-43,-125);   dda(-25,-118,-25,-125);//右腿衔接处    dda(43,-118,43,-125); dda(25,-118,25,-125);//左脚 bresen(-59,-125,-8,-125); bresen(-59,-137,-8,-137); bresen(-59,-125,-59,-137);    bresen(-8,-125,-8,-137);//右脚  bresen(59,-125,8,-125);   bresen(59,-137,8,-137);   bresen(59,-125,59,-137);  bresen(8,-125,8,-137);//刷新机器人 glFlush();}

其他的部分不再赘述,都十分基础。

运行结果

来源:https://cuiqingcai.com/1613.html

直线宽度2 points wide_OpenGL 绘图实例二之直线和圆弧的绘制相关推荐

  1. Win32绘图总结篇(点、直线、折线、贝塞尔曲线、矩形、椭圆、圆弧、弓形、扇形、多边形等)

    前言: 想在窗口上绘制出各种各样的图形,Windows给我们提供了大量的API函数,这些绘图函数种类很多,基本上能满足我们的绘图需求.要绘制出漂亮的图形,这时需要用到画笔和画刷了,简单来说,画笔是用来 ...

  2. python海龟绘图画圣诞帽男人_python海龟绘图实例教程

    本文以实例形式介绍了python turtle模块即海龟绘图的使用方法,对于需要进行图形编程的朋友相信会有一定的借鉴价值. python turtle模块简介: python2.6版本中引入的一个简单 ...

  3. python海龟绘图画玫瑰花_python海龟绘图实例教程

    本文以实例形式介绍了python turtle模块即海龟绘图的使用方法,对于需要进行图形编程的朋友相信会有一定的借鉴价值. python turtle模块简介: python2.6版本中引入的一个简单 ...

  4. python海龟绘图代码大全-python海龟绘图实例教程

    本文以实例形式介绍了python turtle模块即海龟绘图的使用方法,对于需要进行图形编程的朋友相信会有一定的借鉴价值. python turtle模块简介: python2.6版本中引入的一个简单 ...

  5. MATLAB 的绘图:二维和三维

    本节介绍MATLAB 的两种基本绘图功能:二维平面图形和三维立体图形. 5.1 二维平面图形 5.1.1 基本图形函数 plot 是绘制二维图形的最基本函数,它是针对向量或矩阵的列来绘制曲线的.也就是 ...

  6. python海龟绘图教程自学网_python海龟绘图实例教程

    本文以实例形式介绍了python turtle模块即海龟绘图的使用方法,对于需要进行图形编程的朋友相信会有一定的借鉴价值. python turtle模块简介: python2.6版本中引入的一个简单 ...

  7. 学习matlab(八)——绘图(二维)

    目录 (1)plot函数 (2)Subplot函数 (3)叠加图绘制 (4)其它功能 (5)绘制直线 (6)极坐标绘图 (7)对数和半对数坐标系绘图 (8)双纵轴坐标 (9)窗口和标注 (10)获取和 ...

  8. gnuplot画图命令_Gnuplot科学绘图(二十六)——image 绘图

    Gnuplot科学绘图系列内容Gnuplot科学绘图(二十一)--简单 3D 函数绘图Gnuplot科学绘图(二十二)--数据文件存储格式Gnuplot科学绘图(二十三)-- 3D 数据曲面绘图及边框 ...

  9. python窗口图形界面编程上传图片_python GUI编程(Tkinter) 创建子窗口及在窗口上用图片绘图实例...

    注意主窗口一定要为tk.Tk(),在主窗口上通过button的点击相应子函数创建子窗口,注意此时创建出来的窗口必须是Toplevel,否则出错. 至于用图片在窗口上绘图,则按代码所示即可. # -*- ...

最新文章

  1. 如何蒸螃蟹?教你蒸螃蟹3个小窍门
  2. BZOJ.4199.[NOI2015]品酒大会(后缀自动机 树形DP)
  3. float.equals_Java Float类equals()方法与示例
  4. c语言写程序轮询是什么意思,单片机轮询按键程序
  5. python发布_python网站发布
  6. 代码规范 设计模式落地之路
  7. 嵌入式Linux系统编程学习之三十一线程的属性
  8. Java中的抽象函数与C++中的虚函数
  9. PC-用Windows XP自带的组策略加固操作系统
  10. 一款跑在云上的定制容器专属 OS
  11. 中科大EPC课程爬取
  12. Excel 的进阶学习
  13. 如何跨网络远程操作另一台计算机,如何远程控制另一台电脑?
  14. 深入浅出了解几种简单设计模式
  15. 数据分析模型篇—安索夫矩阵
  16. java 中vo、po、dto、bo、pojo、entity、mode如何区分
  17. 白皮书:柬埔寨通过区块链支付实现无美元未来
  18. Python的基本操作
  19. CV中一些常见的特征点
  20. 大数据离线流程(小练习)

热门文章

  1. JS中的call、apply、bind方法详解
  2. 访谈|在网络世界捕获威胁的猎人
  3. Hibernate学习(4)- Hibernate对象的生命周期
  4. 七 递归与二分法、匿名函数、内置函数
  5. Mysql字符串字段判断是否包含某个字符串的3种方法
  6. CentOs7 修改rpm安装背景图
  7. 反垃圾邮件网关市场分析
  8. 如何选择阿里云服务器配置?
  9. linux下查看Mysql默认编码、修改默认编码
  10. 在64-bit机器上运行32-big的应用程序,需要安装ia32-libs库