2015-01-03 6 views
3

В настоящее время я использую алгоритм рисования круга Bresenham, который рисует круги отлично, однако я бы хотел относительно быстрый и эффективный способ нарисовать круг с заданной толщиной (так как метод Брешенема только рисует толщину одного пикселя). Я понимаю, что я мог бы просто рисовать несколько кругов с разными радиусами, но я считаю, что это будет очень неэффективно (и эффективность важна, потому что это будет работать на Arduino, где каждая микросекунда драгоценна). настоящее время я использую следующий код:Круг с алгоритмом рисования толщины

void circle(byte xc, byte yc, int radius, Colour colour) { 
    int x = -radius, y = 0, err = 2 - 2 * radius; 
    while(x < 0) { 
    setPixel(xc - x, yc + y, colour); 
    setPixel(xc - y, yc - x, colour); 
    setPixel(xc + x, yc - y, colour); 
    setPixel(xc + y, yc + x, colour); 
    radius = err; 
    if(radius <= y) { 
     err += ++y * 2 + 1; 
    } 
    if(radius > x || err > y) { 
     err += ++x * 2 + 1; 
    } 
    } 
} 

Как я мог это изменить, чтобы для уточнения толщины окружности? PS Я не хочу использовать какие-либо внешние библиотеки, пожалуйста!

+1

Обычный подход состоит в том, чтобы нарисовать 2 круга (внутренний и внешний), затем заполнить пробел, используя соответствующее правило заполнения (например, http://en.wikipedia.org/wiki/Even%E2%80%93odd_rule). – oakad

+0

Да, но разве это не слишком медленно? –

+3

Вы можете использовать «сжатый» посредник: создать массив связанных списков для представления целевой поверхности (поэтому каждый связанный список представляет собой строку сканирования). Используйте алгоритм Брешенема, чтобы помещать узлы для каждого «черного» пикселя в каждый соответствующий список строк сканирования. Переместите полученную структуру с правилом заполнения, чтобы рисовать на целевой поверхности (в каждом списке будет всего 2-4 узла). – oakad

ответ

2

Если вы сканируете вдоль октантов, как описано для Midpoint circle algorithm, ваша основная координата y всегда будет увеличиваться на единицу. Затем вы можете нарисовать сразу два круга, потому что их основные координаты синхронизированы.

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

Вы сохраняете состояние с x и err для двух кругов, внутренний круг i и наружный круг o. После того, как внутренний круг достиг диагонали, внутренняя точка лежит на этой диагонали. Это означает, что вы рисуете восемь соседних октановых секторов.

Эта идея очень похожа на то, что @oakad предлагает в комментариях, но без необходимости хранить список. Алгоритм окружности Midpoint может быть медленнее, чем алгоритм Брешенема, поэтому, вероятно, есть возможности для улучшения, но низкая занимаемая память - это преимущество.

Приведенный ниже код рисует полый круг с заданными внутренними и внешними радиусами. Ширина линии равна ro - ri + 1, так что даже равные радиусы будут печатать круг шириной в один пиксель. Он ничего не напечатает, если внутренний радиус меньше внешнего.

void xLine(int x1, int x2, int y, int colour) 
{ 
    while (x1 <= x2) setPixel(x1++, y, colour); 
} 

void yLine(int x, int y1, int y2, int colour) 
{ 
    while (y1 <= y2) setPixel(x, y1++, colour); 
} 

void circle2(int xc, int yc, int inner, int outer, int colour) 
{ 
    int xo = outer; 
    int xi = inner; 
    int y = 0; 
    int erro = 1 - xo; 
    int erri = 1 - xi; 

    while(xo >= y) { 
     xLine(xc + xi, xc + xo, yc + y, colour); 
     yLine(xc + y, yc + xi, yc + xo, colour); 
     xLine(xc - xo, xc - xi, yc + y, colour); 
     yLine(xc - y, yc + xi, yc + xo, colour); 
     xLine(xc - xo, xc - xi, yc - y, colour); 
     yLine(xc - y, yc - xo, yc - xi, colour); 
     xLine(xc + xi, xc + xo, yc - y, colour); 
     yLine(xc + y, yc - xo, yc - xi, colour); 

     y++; 

     if (erro < 0) { 
      erro += 2 * y + 1; 
     } else { 
      xo--; 
      erro += 2 * (y - xo + 1); 
     } 

     if (y > inner) { 
      xi = y; 
     } else { 
      if (erri < 0) { 
       erri += 2 * y + 1; 
      } else { 
       xi--; 
       erri += 2 * (y - xi + 1); 
      } 
     } 
    } 
} 
+0

Могу ли я изменить его так, чтобы я мог указать толщину для круга, а затем извлечь из него внутренний и внешний радиусы? Поэтому замените «int inner, int outer» на «int radius, int толщиной», а затем do inner = radius-thickness? –

+0

@indeed: Да, конечно.Вы можете либо написать front-end для этой функции, либо использовать свою подпись функции и вычислить внутреннюю. Этот код относится к тому же внутреннему и внешнему, что и толщина 1, поэтому вам, вероятно, понадобится «inner = outer - толщина + 1». –

+0

ОК. спасибо за вашу помощь –

-1

Решение ниже может быть медленным, однако, это очень просто.

Сначала нарисуйте внутренние и внешние круги, используя алгоритм Брешенема. Затем проверьте состояние:

if (pow(i - centre, 2) + pow(j - centre, 2) <= pow(outern_radius,2) && 
      pow(i - centre, 2) + pow(j - centre, 2) >= pow(inner_radius,2)) 

Если это выполнено, setPixel (i, j).

+0

Если что-то не так с моим решением, прокомментируйте сначала, прежде чем проголосовать. –