【问题标题】:Draw filled circle by pixels from center in C++?在 C++ 中从中心按像素绘制实心圆?
【发布时间】:2017-02-19 15:11:28
【问题描述】:

我想逐行(例如顺时针方向)绘制圆,从圆的中心逐个像素。但要避免重绘像素(这很慢)。

想象一下,这就像“雷达”,每回合只更新一次。

没有 RAM 来保存所有填充像素的数组(最大先前线点),也没有 GPU 或高级库(通过函数 DrawPoint(x,y) 绘图)。

我有画线和点的功能:

void DrawLineFromCenterXYToAngle(int centerX, int centerY, float angle)
{
 .... instead of angle it is possible to find points of the circle and iterate it
 .... find line points by Bresenham's line algorithm
 {
  DrawPoint(int x, int y);
 }
}

void DrawPoint(int x, int y)
{
  PixelDraw_Slow(x,y)=GetColor_VerySlow(x,y);  
}

现在我迭代角度并非常缓慢地得到圆,因为中心的像素重绘了很多次。并且需要优化。

如果这样更快,形状可能不是完美的圆形。

【问题讨论】:

  • 你必须使用一些库,因为标准 c++ 没有 GUI 的概念。由于我们不知道您使用的是什么库,因此我们很难提供帮助。
  • 也许您应该展示您的迭代方式,以便我们帮助您实施。
  • 这是微控制器规则,它没有帮助。例如 void Adafruit_GFX_AS::drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t 颜色) { int16_t 陡峭 = abs(y1 - y0) > abs(x1 - x0);如果(陡峭){交换(x0,y0);交换(x1,y1); } if (x0 > x1) { 交换(x0, x1);交换(y0,y1); } int16_t dx, dy; dx = x1 - x0; dy = abs(y1 - y0); int16_t err = dx / 2; int16_tystep;如果 (y0
  • 好的,我会做干净的工作代码

标签: c++ algorithm plot graphics rasterizing


【解决方案1】:

有用于圆形的 Bresenham 绘图算法。

Example with code

int r = 200, ymax, x, y, yp, xp, d;
ymax = r / sqrt(2);
x = r; /* start at the bottom of the first octant */
/* d measures whether the midpoint of two pixel locations
is inside or outside the ideal circle. positive means outside */
d = -1*r; /* to be precise, this should be r^2 - (r - 0.5)^2 */
xp = r; yp = -1; /* these hold the old values across iterations */
for(y=0; y<=ymax; y++) {
  plot_8_ways(x,y);
  if (d > 0) { /* midpoint is outside circle, go NW */
    x--;
    d += (2*yp - 2*xp + 3);
  }
  else { /* midpoint is inside circle, go N */
    d += (2*yp + 1);
  }
  yp = y;
  xp = x;
}

【讨论】:

    【解决方案2】:

    没有 RAM 来保存所有填充像素的数组(最大先前线点),也没有 GPU 或高级库(通过函数 DrawPoint(x,y) 绘图)。

    如果你可以存储一行点,那么你有足够的内存来跟踪所有填充的像素,如果你有效地表示它。

    假设您的雷达扫描从 12 点钟或 6 点钟位置开始(顺时针或逆时针扫描都没有关系),那么在绘制圆期间的任何时间,一条垂直线只会与填充像素块相交一次(即进入一次并退出一次)。因此,您可以通过存储每列像素的垂直最小和最大填充像素 y 坐标来跟踪它们。如果您从 3 点钟或 9 点钟位置开始,则可以存储每行的最小和最大 x。

    你可以用几个数组来做:

    int minY[X_RANGE];
    int maxY[X_RANGE];
    

    X_RANGE 是像素的最大列数。这只需要和你的圆圈一样大,而不是整个屏幕,因为你可以根据圆圈边界框的左 X 来偏移它。同样,Y_RANGE 是最大行数。

    开始渲染时,初始化数组以表示空列:

    void InitColumnArrays()
    {
        for(int x = 0; x < X_RANGE; x++)
        {
            minY[x] = Y_RANGE - 1;
            maxY[x] = 0;
        }
    }
    

    那么当你渲染一个像素的时候,只要通过检查数组来检查它是否已经被渲染了:

    void DrawPointCheckingRanges(int x, int y)
    {
        // TODO: range check values
    
        // check if point has already been drawn and draw it and extend ranges if not:
        if((y < minY[x]) || (y > maxY[x]))
        {
            DrawPoint(x + offsetX, y + offsetX); 
            if(y < minY[x])
                minY[x] = y;
            if(y > maxY[x])
                maxY[x] = y;
        }
    }
    

    offsetX 和 offsetY 是如上所述的可选偏移量。

    您可以将它与其他答案中的 Bresenham 圆绘图算法结合使用,这将有效地为您提供线条的端点。

    上面的代码假设 DrawPoint 与这些边界检查和数组读写相比是慢的。

    【讨论】:

      猜你喜欢
      • 2019-01-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-10
      • 1970-01-01
      • 2011-01-31
      相关资源
      最近更新 更多