快速算法绘制实心圆?(fast algorithm for drawing filled circl

2019-06-27 03:21发布

我使用布氏圈算法的快速圆图。 不过,我也想(在用户的要求)绘制一个实心圆。

是否有这样做的一种快速有效的方法是什么? 沿着布氏同一行的东西?

我使用的语言是C.

Answer 1:

读过关于布氏(也“中点”)圈算法的维基百科页面 ,它会出现,这样做最简单的事情是修改自己的行动,从而代替

setPixel(x0 + x, y0 + y);
setPixel(x0 - x, y0 + y);

和类似的,每次你做,而不是

lineFrom(x0 - x, y0 + y, x0 + x, y0 + y);

也就是说,对于每对点(具有相同的y )是布氏你将有你的情节 ,而是你用线连接起来



Answer 2:

只需使用蛮力。 这种方法在几个像素过多迭代,但只使用整数乘法和加法。 您完全避免了布氏的复杂性和开方的可能的瓶颈。

for(int y=-radius; y<=radius; y++)
    for(int x=-radius; x<=radius; x++)
        if(x*x+y*y <= radius*radius)
            setpixel(origin.x+x, origin.y+y);


Answer 3:

这里有一个C#粗略的指南(不应该是很难获得对C正确的想法) - 这是“原始”的形式,而无需使用布氏消除重复平方根。

Bitmap bmp = new Bitmap(200, 200);

int r = 50; // radius
int ox = 100, oy = 100; // origin

for (int x = -r; x < r ; x++)
{
    int height = (int)Math.Sqrt(r * r - x * x);

    for (int y = -height; y < height; y++)
        bmp.SetPixel(x + ox, y + oy, Color.Red);
}

bmp.Save(@"c:\users\dearwicker\Desktop\circle.bmp");


Answer 4:

您可以使用此:

void DrawFilledCircle(int x0, int y0, int radius)
{
    int x = radius;
    int y = 0;
    int xChange = 1 - (radius << 1);
    int yChange = 0;
    int radiusError = 0;

    while (x >= y)
    {
        for (int i = x0 - x; i <= x0 + x; i++)
        {
            SetPixel(i, y0 + y);
            SetPixel(i, y0 - y);
        }
        for (int i = x0 - y; i <= x0 + y; i++)
        {
            SetPixel(i, y0 + x);
            SetPixel(i, y0 - x);
        }

        y++;
        radiusError += yChange;
        yChange += 2;
        if (((radiusError << 1) + xChange) > 0)
        {
            x--;
            radiusError += xChange;
            xChange += 2;
        }
    }
}


Answer 5:

我喜欢palm3D的答案。 对于被蛮力,这是一个令人惊讶的快速的解决方案。 有没有平方根或三角函数慢下来。 它的一个弱点是嵌套循环。

这个转换到一个循环使得这个功能几乎快一倍。

int r2 = r * r;
int area = r2 << 2;
int rr = r << 1;

for (int i = 0; i < area; i++)
{
    int tx = (i % rr) - r;
    int ty = (i / rr) - r;

    if (tx * tx + ty * ty <= r2)
        SetPixel(x + tx, y + ty, c);
}

此单回路溶液对手一个画线解决方案的效率。

            int r2 = r * r;
            for (int cy = -r; cy <= r; cy++)
            {
                int cx = (int)(Math.Sqrt(r2 - cy * cy) + 0.5);
                int cyy = cy + y;

                lineDDA(x - cx, cyy, x + cx, cyy, c);
            }


Answer 6:

以下是我正在做它:
我使用的是两个位精度的定点值(我们不得不管理半分和半分平方值)
正如在前面的回答mentionned,我还使用平方值,而不是平方根。
首先,我在我的检测中圈的1/8部分圈的边界限制。 我使用这些点的对称的画圆的4“边界”。 然后,我画的圆圈内的广场。

不同于中点画圆algorith,这个人会甚至工作直径(与实数直径也有一些小的变化)。

请原谅我,如果我的解释是不明确的,我是法国人;)

void DrawFilledCircle(int circleDiameter, int circlePosX, int circlePosY)
{
    const int FULL = (1 << 2);
    const int HALF = (FULL >> 1);

    int size = (circleDiameter << 2);// fixed point value for size
    int ray = (size >> 1);
    int dY2;
    int ray2 = ray * ray;
    int posmin,posmax;
    int Y,X;
    int x = ((circleDiameter&1)==1) ? ray : ray - HALF;
    int y = HALF;
    circlePosX -= (circleDiameter>>1);
    circlePosY -= (circleDiameter>>1);

    for (;; y+=FULL)
    {
        dY2 = (ray - y) * (ray - y);

        for (;; x-=FULL)
        {
            if (dY2 + (ray - x) * (ray - x) <= ray2) continue;

            if (x < y)
            {
                Y = (y >> 2);
                posmin = Y;
                posmax = circleDiameter - Y;

                // Draw inside square and leave
                while (Y < posmax)
                {
                    for (X = posmin; X < posmax; X++)
                        setPixel(circlePosX+X, circlePosY+Y);
                    Y++;
                }
                // Just for a better understanding, the while loop does the same thing as:
                // DrawSquare(circlePosX+Y, circlePosY+Y, circleDiameter - 2*Y);
                return;
            }

            // Draw the 4 borders
            X = (x >> 2) + 1;
            Y = y >> 2;
            posmax = circleDiameter - X;
            int mirrorY = circleDiameter - Y - 1;

            while (X < posmax)
            {
                setPixel(circlePosX+X, circlePosY+Y);
                setPixel(circlePosX+X, circlePosY+mirrorY);
                setPixel(circlePosX+Y, circlePosY+X);
                setPixel(circlePosX+mirrorY, circlePosY+X);
                X++;
            }
            // Just for a better understanding, the while loop does the same thing as:
            // int lineSize = circleDiameter - X*2;
            // Upper border:
            // DrawHorizontalLine(circlePosX+X, circlePosY+Y, lineSize);
            // Lower border:
            // DrawHorizontalLine(circlePosX+X, circlePosY+mirrorY, lineSize);
            // Left border:
            // DrawVerticalLine(circlePosX+Y, circlePosY+X, lineSize);
            // Right border:
            // DrawVerticalLine(circlePosX+mirrorY, circlePosY+X, lineSize);

            break;
        }
    }
}

void DrawSquare(int x, int y, int size)
{
    for( int i=0 ; i<size ; i++ )
        DrawHorizontalLine(x, y+i, size);
}

void DrawHorizontalLine(int x, int y, int width)
{
    for(int i=0 ; i<width ; i++ )
        SetPixel(x+i, y);
}

void DrawVerticalLine(int x, int y, int height)
{
    for(int i=0 ; i<height ; i++ )
        SetPixel(x, y+i);
}

使用非整数的直径,则可以增加固定点的精度或使用双值。 它甚至应该有可能使某种依赖于DY2 +的区别抗混叠(雷 - X)的*(雷 - x)和ray2(dx²+dy²和R 2)



Answer 7:

如果你想有一个快速的算法,考虑制定有N边的多边形,较高的为N,则更为精确的将是圈。



Answer 8:

palm3D的蛮力算法,我认为是一个很好的起点。 此方法使用相同的前提下,但它包括一对夫妇的方式来跳过检查大多数像素。

首先,这里的代码:

int largestX = circle.radius;
for (int y = 0; y <= radius; ++y) {
    for (int x = largestX; x >= 0; --x) {
        if ((x * x) + (y * y) <= (circle.radius * circle.radius)) {
            drawLine(circle.center.x - x, circle.center.x + x, circle.center.y + y);
            drawLine(circle.center.x - x, circle.center.x + x, circle.center.y - y);
            largestX = x;
            break; // go to next y coordinate
        }
    }
}

接下来,解释。

首先要注意的事情是,如果你发现的最小x坐标为圆一个给定水平线内,你马上知道最大x坐标。 这是因为圆的对称性。 如果最小x坐标为提前圆的边框左侧的10个像素,则最大x是落后一圈的边框右侧10个像素。

从高的x值进行迭代,以低的x值的原因是,最小x值将以较小的迭代中找到。 这是因为最小的x值更接近比中心边框左边的圆圈大部分线路的X坐标,由于圈被向外弯曲,就因为看到这个图片接下来的事情要注意的是,由于圈也是对称的垂直,每次找到行给你一个免费的第二线绘制,每次发现在圆的上方半年线的时候,你会得到一个在底部一半半径-Y y坐标。 因此,当任何线被发现,2,可以得出并需要遍历仅y值的上半部分。

需要注意的最后一件事是,是,如果你从y值是在圆心开始,然后朝上方y的移动,然后最小x值每下一行必须更接近中心x坐标圆比最后一行。 这也是由于圆弯曲向中心x值越接近,你去了一圈。 这里是怎么说的情况下的视觉。

综上所述:

  1. 如果您发现一行的最小x坐标,你得到最大的x坐标是免费的。
  2. 你会发现上绘制圆的上半部分的每一行给你圆上免费的底部半年线。
  3. 每一最小x坐标必须更接近圆的中心比从中心y坐标顶端迭代当先前x坐标每一行。

您还可以存储的值(radius * radius) ,并且还(y * y)而不是多次计算它们。



Answer 9:

我只想生成点的列表,然后用一个多边形绘图功能的呈现。



文章来源: fast algorithm for drawing filled circles?