Подтвердить что ты не робот

Алгоритм окружности для заполненных кругов

Алгоритм окружности окружности можно использовать растеризацию границы круга. Тем не менее, я хочу, чтобы круг был заполнен, без рисования пикселей несколько раз (это очень важно).

Этот ответ дает модификацию алгоритма, который дает заполненный круг, но некоторые пиксели посещаются несколько раз: быстрый алгоритм для рисования заполненных кругов?

Q: Как растрировать круг без рисования пикселей несколько раз? Обратите внимание, что оперативная память очень ограничена!

Update:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CircleTest
{
    class Program
    {
        static void Main(string[] args)
        {
            byte[,] buffer = new byte[50, 50];
            circle(buffer, 25, 25, 20);

            for (int y = 0; y < 50; ++y)
            {
                for (int x = 0; x < 50; ++x)
                    Console.Write(buffer[y, x].ToString());

                Console.WriteLine();
            }
        }

        // 'cx' and 'cy' denote the offset of the circle center from the origin.
        static void circle(byte[,] buffer, int cx, int cy, int radius)
        {
            int error = -radius;
            int x = radius;
            int y = 0;

            // The following while loop may altered to 'while (x > y)' for a
            // performance benefit, as long as a call to 'plot4points' follows
            // the body of the loop. This allows for the elimination of the
            // '(x != y)' test in 'plot8points', providing a further benefit.
            //
            // For the sake of clarity, this is not shown here.
            while (x >= y)
            {
                plot8points(buffer, cx, cy, x, y);

                error += y;
                ++y;
                error += y;

                // The following test may be implemented in assembly language in
                // most machines by testing the carry flag after adding 'y' to
                // the value of 'error' in the previous step, since 'error'
                // nominally has a negative value.
                if (error >= 0)
                {
                    error -= x;
                    --x;
                    error -= x;
                }
            }
        }

        static void plot8points(byte[,] buffer, int cx, int cy, int x, int y)
        {
            plot4points(buffer, cx, cy, x, y);
            if (x != y) plot4points(buffer, cx, cy, y, x);
        }

        // The '(x != 0 && y != 0)' test in the last line of this function
        // may be omitted for a performance benefit if the radius of the
        // circle is known to be non-zero.
        static void plot4points(byte[,] buffer, int cx, int cy, int x, int y)
        {
#if false // Outlined circle are indeed plotted correctly!
            setPixel(buffer, cx + x, cy + y);
            if (x != 0) setPixel(buffer, cx - x, cy + y);
            if (y != 0) setPixel(buffer, cx + x, cy - y);
            if (x != 0 && y != 0) setPixel(buffer, cx - x, cy - y);
#else // But the filled version plots some pixels multiple times...
            horizontalLine(buffer, cx - x, cy + y, cx + x);
            //if (x != 0) setPixel(buffer, cx - x, cy + y);
            //if (y != 0) setPixel(buffer, cx + x, cy - y);
            //if (x != 0 && y != 0) setPixel(buffer, cx - x, cy - y);
#endif
        }

        static void setPixel(byte[,] buffer, int x, int y)
        {
            buffer[y, x]++;
        }

        static void horizontalLine(byte[,] buffer, int x0, int y0, int x1)
        {
            for (int x = x0; x <= x1; ++x)
                setPixel(buffer, x, y0);
        }
    }
}

Здесь соответствующий результат:

00000111111111111111111111111111111111111111110000
00000111111111111111111111111111111111111111110000
00000111111111111111111111111111111111111111110000
00000111111111111111111111111111111111111111110000
00000111111111111111111111111111111111111111110000
00000011111111111111111111111111111111111111100000
00000011111111111111111111111111111111111111100000
00000011111111111111111111111111111111111111100000
00000001111111111111111111111111111111111111000000
00000001111111111111111111111111111111111111000000
00000000111111111111111111111111111111111110000000
00000000111111111111111111111111111111111110000000
00000000011111111111111111111111111111111100000000
00000000001111111111111111111111111111111000000000
00000000000111111111111111111111111111110000000000
00000000000011111111111111111111111111100000000000
00000000000001111111111111111111111111000000000000
00000000000000122222222222222222222210000000000000
00000000000000001222222222222222221000000000000000
00000000000000000012333333333332100000000000000000
00000000000000000000012345432100000000000000000000
00000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000

Нижние пиксели отображаются слишком много раз. Что мне здесь не хватает?

Обновление # 2: Это решение работает:

static void circle(byte[,] buffer, int cx, int cy, int radius)
{
    int error = -radius;
    int x = radius;
    int y = 0;

    while (x >= y)
    {
        int lastY = y;

        error += y;
        ++y;
        error += y;

        plot4points(buffer, cx, cy, x, lastY);

        if (error >= 0)
        {
            if (x != lastY)
                plot4points(buffer, cx, cy, lastY, x);

            error -= x;
            --x;
            error -= x;
        }
    }
}

static void plot4points(byte[,] buffer, int cx, int cy, int x, int y)
{
    horizontalLine(buffer, cx - x, cy + y, cx + x);
    if (y != 0)
        horizontalLine(buffer, cx - x, cy - y, cx + x);
}    
4b9b3361

Ответ 1

Ответ на другой вопрос в порядке. Однако, поскольку это создает путаницу, я немного объясню это.

Алгоритм, который вы видите в Википедии, в основном находит x и y 1/8 круга (углы 0 до pi/4), а затем рисует 8 точек, которые являются его зеркалами. Например:

    (o-y,o+x) x         x (o+y,o+x)

(o-x,o+y) x                  x (o+x,o+y) <-- compute x,y

                   o

(o-x,o-y) x                  x (o+x,o-y)

    (o-y,o-x) x         x (o+y,o-x)

Что предлагает другое решение, что имеет смысл, если вы внимательно посмотрите на эту картину, вместо того, чтобы нарисовать 8 точек, нарисуйте 4 горизонтальные линии:

    (o-y,o+x) x---------x (o+y,o+x)

(o-x,o+y) x-----------------x (o+x,o+y) <-- compute x,y

                   o

(o-x,o-y) x-----------------x (o+x,o-y)

    (o-y,o-x) x---------x (o+y,o-x)

Теперь, если вы вычислите (x,y) для углов в [0, pi/4] и нарисуйте эти 4 строки для каждой вычисленной точки, вы нарисуете много горизонтальных линий, заполняющих круг, без пересечения другой линии.

Update

Причина, по которой вы получаете перекрывающиеся линии в нижней части круга, состоит в том, что координаты (x,y) округлены, поэтому в этих местах (x,y) перемещаются по горизонтали.

Если вы посмотрите на эту картинку в википедии:

enter image description here

Вы заметите, что в верхней части круга некоторые пиксели выравниваются по горизонтали. Рисование горизонтальных линий, происходящих из этих точек, перекрывается.

Если вы этого не хотите, решение довольно простое. Вы должны сохранить предыдущий x, который вы нарисовали (поскольку верх и низ являются зеркалами оригинала (x,y), вы должны сохранить предыдущий x, который представляет y этих строк) и только рисовать горизонтальные линии, если это изменения стоимости. Если это не так, значит, вы находитесь в одной строке.

Учитывая тот факт, что вы сначала столкнетесь с самыми внутренними точками, вы должны нарисовать линии для предыдущей точки, только новая точка имеет разные x (конечно, последняя строка всегда нарисована). В качестве альтернативы вы можете начать рисовать с угла PI/4 до 0 вместо 0 на PI/4 и сначала будете сталкиваться с внешними точками, поэтому вы рисуете линии каждый раз, когда вы видите новый x.

Ответ 2

Мне нужно было сделать это, вот код, который я придумал для этого. На визуальном изображении изображены пиксели, где число - это порядок, в котором пройдены пиксели, а зеленые числа представляют пиксели, которые рисуются с использованием отражения завершения столбца, используя симметрию, как показано в коде.

enter image description here

void drawFilledMidpointCircleSinglePixelVisit( int centerX, int centerY, int radius )   
{
    int x = radius;
    int y = 0;
    int radiusError = 1 - x;

    while (x >= y)  // iterate to the circle diagonal
    {

        // use symmetry to draw the two horizontal lines at this Y with a special case to draw
        // only one line at the centerY where y == 0
        int startX = -x + centerX;
        int endX = x + centerX;         
        drawHorizontalLine( startX, endX, y + centerY );
        if (y != 0)
        {
            drawHorizontalLine( startX, endX, -y + centerY );
        }

        // move Y one line
        y++;

        // calculate or maintain new x
        if (radiusError<0)
        {
            radiusError += 2 * y + 1;
        } 
        else 
        {
            // we're about to move x over one, this means we completed a column of X values, use
            // symmetry to draw those complete columns as horizontal lines at the top and bottom of the circle
            // beyond the diagonal of the main loop
            if (x >= y)
            {
                startX = -y + 1 + centerX;
                endX = y - 1 + centerX;
                drawHorizontalLine( startX, endX,  x + centerY );
                drawHorizontalLine( startX, endX, -x + centerY );
            }
            x--;
            radiusError += 2 * (y - x + 1);
        }

    }

}

Ответ 3

Я придумал алгоритм, который рисует уже заполненный круг.
Он выполняет итерацию по пикселям, на которые будет нарисован круг, и ничего больше.
Отсюда все это о скорости функции draw-pixel.

Здесь *.gif, который демонстрирует, что делает алгоритм!

Что касается алгоритма здесь, то код:

    //The center of the circle and its radius.
    int x = 100;
    int y = 100;
    int r = 50;
    //This here is sin(45) but i just hard-coded it.
    float sinus = 0.70710678118;
    //This is the distance on the axis from sin(90) to sin(45). 
    int range = r/(2*sinus);
    for(int i = r ; i >= range ; --i)
    {
        int j = sqrt(r*r - i*i);
        for(int k = -j ; k <= j ; k++)
        {
            //We draw all the 4 sides at the same time.
            PutPixel(x-k,y+i);
            PutPixel(x-k,y-i);
            PutPixel(x+i,y+k);
            PutPixel(x-i,y-k);
        }
    }
    //To fill the circle we draw the circumscribed square.
    range = r*sinus;
    for(int i = x - range + 1 ; i < x + range ; i++)
    {
        for(int j = y - range + 1 ; j < y + range ; j++)
        {
            PutPixel(i,j);
        }
    }

Надеюсь, это поможет... некоторым новым пользователям... извините за некропостинг.
~ Shmiggy

Ответ 4

Я хотел прокомментировать ваше обновление № 2: Это решение работает: (но, я думаю, мне нужно больше репутации в первую очередь...), что в решении есть небольшая ошибка, совпадающая при рисовании небольших кругов. Если вы установите радиус равным 1, вы получите

00000
00000
01110
00100
00000

Чтобы исправить это все, что вам нужно сделать, это изменить условную проверку в plot4points из

if (x != 0 && y != 0)

к

if (y != 0)

Я тестировал это на маленьких и больших кругах, чтобы каждый пиксель по-прежнему был назначен только один раз. Кажется, отлично работает. Я думаю, что x!= 0 не понадобилось. Сохраните крошечную производительность.

Ответ 5

Обновление # 2

 if (error >= 0)
 {
    if (x != lastY) 
       plot4points(buffer, cx, cy, lastY, x);

к

 if (error >= 0)
 {     
    plot4points(buffer, cx, cy, lastY, x);

Версия Circle And FillCircle:

Const
  Vypln13:Boolean=False;  // Fill Object


//Draw a circle at (cx,cy)
Procedure Circle(cx: integer; cy: integer; radius: integer );
Var
   error,x,y: integer;
Begin  
   error := -radius;
   x := radius;
   y := 0;

   while (x >= y) do
   Begin

     Draw4Pixel(cx,cy, x, y);
     if ( Not Vypln13 And ( x <> y) ) Then Draw4Pixel(cx,cy, y, x);

     error := error + y;
     y := y + 1;
     error := error + y;

     if (error >= 0) Then
     Begin

       if ( Vypln13) then Draw4Pixel(cx, cy, y - 1, x);

       error := error - x;
       x := x - 1;
       error := error - x;
     End;
   End;
End;


Procedure Draw4Pixel(cx,cy,dx,dy: integer);
Begin
  if ( (dx = 0) And (dy = 0) ) then
  begin
    PutPixel (cx , cy , Color13);
    exit;
  End;

  IF Vypln13 Then
  Begin
    HorizontLine (cx - dx,  cx + dx, cy + dy, Color13);
    if ( dy = 0 ) then exit;
    HorizontLine (cx - dx,  cx + dx, cy - dy, Color13);
    exit;
  end;

  PutPixel (cx + dx, cy + dy, Color13);
  if ( dx <> 0 ) then
  begin
    PutPixel (cx - dx, cy + dy, Color13);
    if ( dy = 0 ) then exit;
    PutPixel (cx + dx, cy - dy, Color13);
  End;
  PutPixel (cx - dx, cy - dy, Color13);

End;