中点画圆法

作者:追风剑情 发布于:2018-4-14 22:55 分类:计算机图形学

中点画圆法

利用圆的八向对称性画圆。

1111111.png

假设从P点顺时针画圆,下一个像素点选P1还是P2呢? M为P1与P2的中点。

构造函数:

222.png

对于圆上的点,F(x, y)=0;对于圆外的点,F(x, y) > 0;而对于圆内的点,F(x, y) < 0。假设M是P1和P2的中点,即M=(xi+1, yi-0.5)。那么,当F(M)<0时,M在圆内,这说明P1距离圆弧更近,应取P1作为下一个像素。而F(M)>0时,M在圆外,这说明P2距离圆弧更近,应取P2作为下一个像素。当F(M)=0时,在P1和P2之中随便取一个即可,这里约定取P2。

构造判别式

33333.png

若d<0,应取P1作为下一个像素。再下一个像素的判别式为:

4444.png

所以,沿正右方向,此时d的增量为2xi+3。

而若d>=0,应取P2作为下一个像素,再下一个像素的判别式为:

5555.png

所以,沿右下方向,此时d的增量为2(xi-yi)+5。

由于这里讨论的是按顺时针方向生成第二个八分之一圆,因此,第一个像素是(0, R),判别式d的初始值为

6666.png

代码: C#


  1. private void MidPointCircle(int R)
  2. {
  3. int x, y;
  4. double d;
  5. x = 0; y = R; d = 1.25 - R;
  6. SetPixel(x, y);
  7. while(x < y)
  8. {
  9. if (d < 0)
  10. {
  11. d += 2 * x + 3;
  12. x++;
  13. }
  14. else
  15. {
  16. d += 2 * (x - y) + 5;
  17. x++;
  18. y--;
  19. }
  20. SetPixel(x, y);
  21. }
  22. }


优化版: 消除浮点运算


  1. private void MidPointCircle(int R)
  2. {
  3. int x, y;
  4. double d;
  5. x = 0; y = R; d = 1 - R;//去掉小数部分
  6. SetPixel(x, y);
  7. while(x < y)
  8. {
  9. //由于d始终为整数,
  10. //所以d<-0.25等价于d<0
  11. //if (d < -0.25f)
  12. if (d < 0)
  13. {
  14. d += 2 * x + 3;
  15. x++;
  16. }
  17. else
  18. {
  19. d += 2 * (x - y) + 5;
  20. x++;
  21. y--;
  22. }
  23. SetPixel(x, y);
  24. }
  25. }


优化版: 消除乘法运算

通过使用增量计算技术,可以进一步改进上术算法的效率。注意到上术算法中判别式d的增量x、y的线性函数。若d<0,P1为下一个像素,d的增量为:d1=2x+3,可见,x的变化将影响d1的值。若d>=0,P2为下一个像素,d的增量为d2=2(x-y)+5,可见,x和y的变化都将影响d2的值。

每当x递增1,d1递增2,d2递增2。每当y递减1,d2递增2。所以归纳起来,若d<0,P1为下一个像素,x递增1,d1递增2,d2递增2;若d>=0,P2为下一个像素,x递增1,y递减1,d1递增2,d2递增4。由于初始像素为(0, R),所以d1的初始值为3,d2的初始值为-2R+5。再注意到乘2运算可以改用加法实现,至此可写出不含乘法、仅用整数实现的中点画圆算法。


  1. private void MidPointCircle(int R)
  2. {
  3. int x, y, delta1, delta2, d;
  4. x = 0; y = R; d = 1 - R;
  5. delta1 = 3;
  6. delta2 = 5 - R - R;
  7. SetPixel(x, y);
  8. while(x < y)
  9. {
  10. if (d < 0)
  11. {
  12. d += delta1;
  13. delta1 += 2;
  14. delta2 += 2;
  15. x++;
  16. }
  17. else
  18. {
  19. d += delta2;
  20. delta1 += 2;
  21. delta2 += 4;
  22. x++;
  23. y--;
  24. }
  25. SetPixel(x, y);
  26. }
  27. }


完整示例: C#


  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Drawing;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9. using System.Windows.Forms;
  10.  
  11. namespace MidCire
  12. {
  13. public partial class Form1 : Form
  14. {
  15. public Form1()
  16. {
  17. InitializeComponent();
  18. }
  19.  
  20. private void Form1_Load(object sender, EventArgs e)
  21. {
  22. //当窗口尺寸改变时重绘所有窗口
  23. this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw, true);
  24. }
  25.  
  26. private void Form1_Paint(object sender, PaintEventArgs e)
  27. {
  28. MidPointCircle(e.Graphics, 50);
  29. }
  30.  
  31. private void MidPointCircle(Graphics g, int R)
  32. {
  33. int x, y, delta1, delta2, d;
  34. x = 0; y = R; d = 1 - R;
  35. delta1 = 3;
  36. delta2 = 5 - R - R;
  37. SetPixel(g, x, y);
  38. while(x < y)
  39. {
  40. if (d < 0)
  41. {
  42. d += delta1;
  43. delta1 += 2;
  44. delta2 += 2;
  45. x++;
  46. }
  47. else
  48. {
  49. d += delta2;
  50. delta1 += 2;
  51. delta2 += 4;
  52. x++;
  53. y--;
  54. }
  55. SetPixel(g, x, y);
  56. }
  57. }
  58.  
  59. private void SetPixel(Graphics g, int x, int y)
  60. {
  61. int centerOffset = 50;
  62. Pen pen = new Pen(Brushes.Red);
  63. g.DrawEllipse(pen, centerOffset + x, centerOffset + y, 1, 1);
  64. }
  65. }
  66. }


运行截图

11111.png


示例:画一个完整的圆

  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Drawing;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9. using System.Windows.Forms;
  10.  
  11. namespace MidCire
  12. {
  13. public partial class Form1 : Form
  14. {
  15. public Form1()
  16. {
  17. InitializeComponent();
  18. }
  19.  
  20. private void Form1_Load(object sender, EventArgs e)
  21. {
  22. //当窗口尺寸改变时重绘所有窗口
  23. this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw, true);
  24. }
  25.  
  26. private void Form1_Paint(object sender, PaintEventArgs e)
  27. {
  28. MidPointCircle(e.Graphics, 50);
  29. }
  30.  
  31. private void MidPointCircle(Graphics g, int R)
  32. {
  33. int x, y, delta1, delta2, d;
  34. x = 0; y = R; d = 1 - R;
  35. delta1 = 3;
  36. delta2 = 5 - R - R;
  37. SetPixel(g, x, y);
  38. while(x < y)
  39. {
  40. if (d < 0)
  41. {
  42. d += delta1;
  43. delta1 += 2;
  44. delta2 += 2;
  45. x++;
  46. }
  47. else
  48. {
  49. d += delta2;
  50. delta1 += 2;
  51. delta2 += 4;
  52. x++;
  53. y--;
  54. }
  55. SetPixel(g, x, y);
  56. //利用圆的八向对称性画出其他弧线
  57. SetPixel(g, -x, y);
  58. SetPixel(g, x, -y);
  59. SetPixel(g, -x, -y);
  60. SetPixel(g, y, x);
  61. SetPixel(g, -y, x);
  62. SetPixel(g, y, -x);
  63. SetPixel(g, -y, -x);
  64. }
  65. }
  66.  
  67. private void SetPixel(Graphics g, int x, int y)
  68. {
  69. int centerOffset = 50;
  70. Pen pen = new Pen(Brushes.Red);
  71. g.DrawEllipse(pen, centerOffset + x, centerOffset + y, 1, 1);
  72. }
  73. }
  74. }

运行截图

22222.png

标签: 计算机图形学

Powered by emlog  蜀ICP备18021003号-1   sitemap

川公网安备 51019002001593号