fast algorithm for drawing filled circles?

前端 未结 10 1111
清歌不尽
清歌不尽 2020-12-02 07:28

I am using Bresenham\'s circle algorithm for fast circle drawing. However, I also want to (at the request of the user) draw a filled circle.

Is there a fast and effi

相关标签:
10条回答
  • 2020-12-02 08:01

    I like palm3D's answer. For being brute force, this is an amazingly fast solution. There are no square root or trigonometric functions to slow it down. Its one weakness is the nested loop.

    Converting this to a single loop makes this function almost twice as fast.

    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);
    }
    

    This single loop solution rivals the efficiency of a line drawing solution.

                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);
                }
    
    0 讨论(0)
  • 2020-12-02 08:03

    If you want a fast algorithm, consider drawing a polygon with N sides, the higher is N, the more precise will be the circle.

    0 讨论(0)
  • 2020-12-02 08:06

    You can use this:

    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;
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-02 08:12

    palm3D's brute-force algorithm I found to be a good starting point. This method uses the same premise, however it includes a couple of ways to skip checking most of the pixels.

    First, here's the code:

    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
            }
        }
    }
    

    Next, the explanation.

    The first thing to note is that if you find the minimum x coordinate that is within the circle for a given horizontal line, you immediately know the maximum x coordinate. This is due to the symmetry of the circle. If the minimum x coordinate is 10 pixels ahead of the left of the bounding box of the circle, then the maximum x is 10 pixels behind the right of the bounding box of the circle.

    The reason to iterate from high x values to low x values, is that the minimum x value will be found with less iterations. This is because the minimum x value is closer to the left of the bounding box than the centre x coordinate of the circle for most lines, due to the circle being curved outwards, as seen on this image The next thing to note is that since the circle is also symmetric vertically, each line you find gives you a free second line to draw, each time you find a line in the top half of the circle, you get one on the bottom half at the radius-y y coordinate. Therefore, when any line is found, two can be drawn and only the top half of the y values needs to be iterated over.

    The last thing to note is that is that if you start from a y value that is at the centre of the circle and then move towards the top for y, then the minimum x value for each next line must be closer to the centre x coordinate of the circle than the last line. This is also due to the circle curving closer towards the centre x value as you go up the circle. Here is a visual on how that is the case.

    In summary:

    1. If you find the minimum x coordinate of a line, you get the maximum x coordinate for free.
    2. Every line you find to draw on the top half of the circle gives you a line on the bottom half of the circle for free.
    3. Every minimum x coordinate has to be closer to the centre of the circle than the previous x coordinate for each line when iterating from the centre y coordinate to the top.

    You can also store the value of (radius * radius), and also (y * y) instead of calculating them multiple times.

    0 讨论(0)
  • 2020-12-02 08:13

    Having read the Wikipedia page on Bresenham's (also 'Midpoint') circle algorithm, it would appear that the easiest thing to do would be to modify its actions, such that instead of

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

    and similar, each time you instead do

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

    That is, for each pair of points (with the same y) that Bresenham would you have you plot, you instead connect with a line.

    0 讨论(0)
  • 2020-12-02 08:16

    I would just generate a list of points and then use a polygon draw function for the rendering.

    0 讨论(0)
提交回复
热议问题