Алгоритм брезенхема для построения отрезка c. Алгоритм Брезенхема построения окружности

Брезенхем и У на страже диагоналей

Алгоритм брезенхема для построения отрезка c. Алгоритм Брезенхема построения окружности

На что вы сейчас смотрите? Если вы не из параллельной вселенной, где все сидят за векторными мониторами, то перед вами растровое изображение. Поглядите на эту полоску: /. Если придвинуться поближе к монитору, то можно увидеть пиксельные ступеньки, которые пытаются притвориться векторной линией.

Для этой цели существует целая куча всевозможных алгоритмов растеризации, но я бы хотел рассказать об алгоритме Брезенхема и алгоритме У, которые находят приближение векторного отрезка в растровых координатах. С проблемой растеризации мне довелось столкнуться во время работы над процедурным генератором планов зданий.

Мне нужно было представить стены помещения в виде ячеек двумерного массива. Похожие задачи могут встретиться в физических расчётах, алгоритмах поиска пути или расчёте освещения, если используется разбиение пространства. Кто бы мог подумать, что знакомство с алгоритмами растеризации однажды может пригодиться?
Принцип работы алгоритма Брезенхема очень простой.

Берётся отрезок и его начальная координата x. К иксу в цикле прибавляем по единичке в сторону конца отрезка. На каждом шаге вычисляется ошибка — расстояние между реальной координатой y в этом месте и ближайшей ячейкой сетки. Если ошибка не превышает половину высоты ячейки, то она заполняется. Вот и весь алгоритм.

Это была суть алгоритма, на деле всё выглядит следующим образом. Сначала вычисляется угловой коэффициент (y1 — у0)/(x1 — x0). Значение ошибки в начальной точке отрезка (0,0) принимается равным нулю и первая ячейка заполняется. На следующем шаге к ошибке прибавляется угловой коэффициент и анализируется её значение, если ошибка меньше 0.

5, то заполняется ячейка (x0+1, у0), если больше, то заполняется ячейка (x0+1, у0+1) и из значения ошибки вычитается единица. На картинке ниже жёлтым цветом показана линия до растеризации, зелёным и красным — расстояние до ближайших ячеек.

Угловой коэффициент равняется одной шестой, на первом шаге ошибка становится равной угловому коэффициенту, что меньше 0.5, а значит ордината остаётся прежней. К середине линии ошибка пересекает рубеж, из неё вычитается единица, а новый пиксель поднимается выше. И так до конца отрезка.

Ещё один нюанс. Если проекция отрезка на ось x меньше проекции на ось y или начало и конец отрезка переставлены местами, то алгоритм не будет работать.

Чтобы этого не случилось, нужно проверять направление вектора и его наклон, а потом по необходимости менять местами координаты отрезка, поворачивать оси, и, в конечном итоге, сводить всё к какому-то одному или хотя бы двум случаям.

Главное не забывать во время рисования возвращать оси на место.

Для оптимизации расчётов, применяют трюк с умножением всех дробных переменных на dx = (x1 — x0). Тогда на каждом шаге ошибка будет изменяться на dy = (y1 — y0) вместо углового коэффициента и на dx вместо единицы. Также можно немного поменять логику, «передвинуть» ошибку так, чтобы граница была в нуле, и можно было проверять знак ошибки, это может быть быстрее.

Примерно так может выглядеть код для рисования растровой линии по алгоритму Брезенхема. Псевдокод из Википедии переделанный под C#.void BresenhamLine(int x0, int y0, int x1, int y1){ var steep = Math.Abs(y1 – y0) > Math.Abs(x1 – x0); // Проверяем рост отрезка по оси икс и по оси игрек // Отражаем линию по диагонали, если угол наклона слишком большой if (steep) { Swap(ref x0, ref y0); // Перетасовка координат вынесена в отдельную функцию для красоты Swap(ref x1, ref y1); } // Если линия растёт не слева направо, то меняем начало и конец отрезка местами if (x0 > x1) { Swap(ref x0, ref x1); Swap(ref y0, ref y1); } int dx = x1 – x0; int dy = Math.Abs(y1 – y0); int error = dx / 2; // Здесь используется оптимизация с умножением на dx, чтобы избавиться от лишних дробей int ystep = (y0 < y1) ? 1 : -1; // Выбираем направление роста координаты y int y = y0; for (int x = x0; x = y) { DrawPoint(x + x0, y + y0); DrawPoint(y + x0, x + y0); DrawPoint(-x + x0, y + y0); DrawPoint(-y + x0, x + y0); DrawPoint(-x + x0, -y + y0); DrawPoint(-y + x0, -x + y0); DrawPoint(x + x0, -y + y0); DrawPoint(y + x0, -x + y0); y++; if (radiusError < 0) { radiusError += 2 * y + 1; } else { x--; radiusError += 2 * (y - x + 1); } }} Теперь про алгоритм У Сяолиня для рисования сглаженных линий. Он отличается тем, что на каждом шаге ведётся расчёт для двух ближайших к прямой пикселей, и они закрашиваются с разной интенсивностью, в зависимости от удаленности. Точное пересечение середины пикселя даёт 100% интенсивности, если пиксель находится на расстоянии в 0.9 пикселя, то интенсивность будет 10%. Иными словами, сто процентов интенсивности делится между пикселями, которые ограничивают векторную линию с двух сторон. На картинке выше красным и зелёным цветом показаны расстояния до двух соседних пикселей. Для расчёта ошибки можно использовать переменную с плавающей запятой и брать значение ошибки из дробной части. Примерный код сглаженной линии У Сяолиня на C#.private void WuLine(int x0, int y0, int x1, int y1){ var steep = Math.Abs(y1 – y0) > Math.Abs(x1 – x0); if (steep) { Swap(ref x0, ref y0); Swap(ref x1, ref y1); } if (x0 > x1) { Swap(ref x0, ref x1); Swap(ref y0, ref y1); } DrawPoint(steep, x0, y0, 1); // Эта функция автоматом меняет координаты местами в зависимости от переменной steep DrawPoint(steep, x1, y1, 1); // Последний аргумент — интенсивность в долях единицы float dx = x1 – x0; float dy = y1 – y0; float gradient = dy / dx; float y = y0 + gradient; for (var x = x0 + 1; x

Источник: https://habr.com/post/185086/

Алгоритм Брезенхема для генерации окружности

Алгоритм брезенхема для построения отрезка c. Алгоритм Брезенхема построения окружности

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

Один из наиболее эффективных и простых для понимания алгоритмов генерации окружности принадлежит Брезенхему . Для начала заметим, что необходимо сгенерировать только одну восьмую часть окружности. Остальные ее части могут быть получены последовательными отражениями, как это показано на рис. 5.1.

Если сгенерирован первый октант (от 0 до 45° против часовой стрелки), то второй октант можно получить зеркальным отражением относительно прямой у = х, что дает в совокупности первый квадрант. Первый квадрант отражается относительно прямой х = 0 для получения соответствующей части окружности во втором квадранте.

Верхняя полуокружность отражается относительно прямой у = 0 для завершения построения. На рис. 5.1 приведены двумерные матрицы соответствующих преобразований.

Рис. 5.1. Генерация полной окружности из дуги в первом октанте.

Для вывода алгоритма рассмотрим первую четверть окружности с центром в начале координат. Заметим, что если работа алгоритма начинается в точке х = 0, у = R, то при генерации окружности по часовой стрелке в первом квадранте у является монотонно убывающей функцией аргументам (рис. 5.2).

Аналогично, если исходной точкой является у = 0, х == R, то при генерации окружности против часовой стрелки х будет монотонно убывающей функцией аргумента у. В нашем случае выбирается генерация по часовой стрелке с началом в точке х = 0, у = R.

Предполагается, что центр окружности и начальная точка находятся точно в точках растра.

Для любой заданной точки на окружности при генерации по часовой стрелке существует только три возможности выбрать следующий пиксел, наилучшим образом приближающий окружность: горизонтально вправо, по диагонали вниз и вправо, вертикально вниз. На рис. 5.3 эти направления обозначены соответственно mH, mD, mV. Алгоритм выбирает пиксел, для которого минимален квадрат расстояния между одним из этих пикселов и окружностью, т. е. минимум из

mH = |(xi + 1)2 + (yi)2 -R2|

mD = |(xi + 1)2 + (yi -1)2 -R2|

mV = |(xi )2 + (yi -1)2 -R2|

Рис. 5.2. Окружность в первом квадранте. Рис.5.3. Выбор пикселов в первом квадранте.

Вычисления можно упростить, если заметить, что в окрестности точки (xi,yi,) возможны только пять типов пересечений окружности и сетки растра, приведенных на рис. 5.4.

Рис. 5.4. Пересечение окружности и сетки растра.

Разность между квадратами расстояний от центра окружности до диагонального пиксела (xi, + 1, уi 1) и от центра до точки на окружности R2 равна

i = (xi + 1)2 + (yi -1)2 -R2

Как и в алгоритме Брезенхема для отрезка, для выбора соответствующего пиксела желательно использовать только знак ошибки, а не ее величину.

При i < 0 диагональная точка (xi, + 1, уi 1) находится внутри реальной окружности, т. е. это случаи 1 или 2 на рис. 5.4.

Ясно, что в этой ситуации следует выбрать либо пиксел (xi, + 1, уi), т. е. mH, либо пиксел (xi, + 1, уi 1), т. е. mD.

Для этого сначала рассмотрим случай 1 и проверим разность квадратов расстояний от окружности до пикселов в горизонтальном и диагональном направлениях:

 = |(xi + 1)2 + (yi )2 -R2| – |(xi + 1)2 + (yi -1)2 -R2|

При  < 0 расстояние от окружности до диагонального пиксела больше, чем до горизонтального. Напротив, если  > 0, расстояние до горизонтального пиксела больше. Таким образом,

при  0 выбираем mD в (xi, + 1, уi – 1)

При  = 0, когда расстояние от окружности до обоих пикселов одинаковы, выбираем горизонтальный шаг.

Количество вычислений, необходимых для оценки величины , можно сократить, если заметить, что в случае 1

(xi + 1)2 + (yi )2 -R2 >= 0

(xi + 1)2 + (yi -1)2 -R2 < 0

так как диагональный пиксел (xi, + 1, уi 1) всегда лежит внутри окружности, а горизонтальный (xi, + 1, уi)вне ее. Таким образом,  можно вычислить по формуле

= (xi + 1)2 + (yi )2 -R2 + (xi + 1)2 + (yi -1)2 -R2

Дополнение до полного квадрата члена (yi )2 с помощью добавления и вычитания – 2yi + 1 дает

= 2[(xi + 1)2 + (yi -1)2 -R2] + 2yi 1

В квадратных скобках стоит по определению i и его подстановка

= 2( i + yi) 1

существенно упрощает выражение.

Рассмотрим случай 2 на рис. 5.4 и заметим, что здесь должен быть выбран горизонтальный пиксел (xi, + 1, уi), так как .у является монотонно убывающей функцией. Проверка компонент  показывает, что

(xi + 1)2 + (yi )2 -R2 < 0

(xi + 1)2 + (yi -1)2 -R2 < 0

поскольку в случае 2 горизонтальный (xi, + 1, уi) и диагональный (xi, + 1, уi -1) пикселы лежат внутри окружности. Следовательно,  < 0, и при использовании того же самого критерия, что и в случае 1, выбирается пиксел (xi, + 1, уi).

Если i > 0, то диагональная точка (xi, + 1, уi -1) находится вне окружности, т. е. это случаи 3 и 4 на рис. 5.4. В данной ситуации ясно, что должен быть выбран либо пиксел (xi, + 1, уi -1), либо (xi, , уi -1).

Аналогично разбору предыдущего случая критерий выбора можно получить, рассматривая сначала случай 3 и проверяя разность между квадратами расстояний от окружности до диагонального mD и вертикального mV пикселов,

т. е. ' = |(xi + 1)2 + (yi -1)2 -R2| – |(xi)2 + (yi -1)2 -R2 |

При' 0 расстояние от окружности до диагонального пиксела больше и следует выбрать вертикальное движение к пикселу (xi, , уi -1). Таким образом,

при ' 0 выбираем mV в (xi, , уi -1)

Здесь в случае ' = 0, т. е. когда расстояния равны, выбран диагональный шаг.

Проверка компонент ' показывает, что

(xi )2 + (yi -1)2 -R2 >= 0

(xi + 1)2 + (yi -1)2 -R2 < 0

поскольку для случая 3 диагональный пиксел (xi +1, уi -1) находится вне окружности, тогда как вертикальный пиксел (xi, , уi -1) лежит внутри ее. Это позволяет записать ' в виде

' = (xi +1)2 + (yi -1)2 -R2 + (xi )2 + (yi -1)2 -R2

Дополнение до полного квадрата члена (xi)2 с помощью добавления и вычитания 2xi + 1 дает

' = 2[(xi +1)2 + (yi -1)2 -R2] – 2xi – 1

Использование определения i приводит выражение к виду

' = 2( i xi )- 1

Теперь, рассматривая случай 4, снова заметим, что следует выбрать вертикальный пиксел (xi, уi -1), так как у является монотонно убывающей функцией при возрастании х.

Проверка компонент ' для случая 4 показывает, что

(xi +1)2 + (yi -1)2 -R2 > 0

(xi )2 + (yi -1)2 -R2 > 0

поскольку оба пиксела находятся вне окружности. Следовательно, ' > 0 и при использовании критерия, разработанного для случая 3, происходит верный выбор mV.

Осталось проверить только случай 5 на рис. 5.4, который встречается, когда диагональный пиксел (xi, , уi -1) лежит на окружности, т. е. i = 0. Проверка компонент  показывает, что

(xi +1)2 + (yi)2 -R2 > 0

(xi +1)2 + (yi -1)2 -R2 = 0

Следовательно, > 0 и выбирается диагональный пиксел (xi +1 , уi -1) . Аналогичным образом оцениваем компоненты ' :

(xi +1)2 + (yi -1)2 -R2 = 0

(xi +1)2 + (yi -1)2 -R2 < 0

и '< 0, что является условием выбора правильного диагонального шага к (xi +1 , уi -1) . Таким образом, случай i = 0 подчиняется тому же критерию, что и случай i < 0 или i > 0. Подведем итог полученных результатов:

i < 0

0 выбираем пиксел (xi +1 , уi -1)mD

i > 0

' 0 выбираем пиксел (xi , уi -1)- mV

i = 0 выбираем пиксел (xi +1 , уi -1) – mD

Легко разработать простые рекуррентные соотношения для реализации пошагового алгоритма. Сначала рассмотрим горизонтальный шаг mH к пикселу (xi + 1, уi). Обозначим это новое положение пиксела как (i + 1). Тогда координаты нового пиксела и значение i равны

xi+1 = xi +1

yi+1 = yi

i+1 = (xi+1 +1)2 + (yi+1 -1)2 -R2 i + 2xi+1+ 1

Аналогично координаты нового пиксела и значение i для шага mD к пикселу (xi + 1, уi -1) таковы:

xi+1 = xi +1

yi+1 = yi -1

i+1 = i + 2xi+1 – 2yi+1 +2

То же самое для шага mVк (xi, уi -1)

xi+1 = xi

yi+1 = yi -1

i+1 = i – 2yi+1 +1

Реализация алгоритма Брезенхема на псевдокоде для окружности приводится ниже.

Пошаговый алгоритм Брезенхема для генерации окружности в первом квадранте

все переменные – целые

инициализация переменных

xi = 0

yi = R

i =2(1 – R)

Предел = 0

1 Plot (xi, yi

if yi 0then 3

if i= 0then 20

определение случая 1 или 2

2  = 2i+ 2уi – 1

if  0then 20

определение случая 4 или 5

3 = 2i+ 2хi 1

if 0 then30

выполнение шагов

шаг к mH

10 хi = хi + 1

i = i+ 2хi + 1

Не нашли то, что искали? Воспользуйтесь поиском:

Источник: https://studopedia.ru/14_5924_algoritm-brezenhema-dlya-generatsii-okruzhnosti.html

Рисование окружности по алгоритму Брезенхейма и по алгоритму Мичнера | Основы программирования

Алгоритм брезенхема для построения отрезка c. Алгоритм Брезенхема построения окружности

Построить растровое изображение окружности. Использовать в вычислениях только целые числа

Обратимся к тексту процедуры Pixel_circle, которая, как можно видеть, расставляет восемь точек вокруг точки (xc,yc) (центра нашей окружности).

Эта процедура реализует следующее: окружность обладает центром симметрии и бесконечным количеством осей симметрии.

Поэтому нет необходимости строить всю окружность, достаточно построить некоторую ее часть и последовательным применением преобразований симметрии получить из нее полную окружность. Мы станем строить 1/8 часть окружности, заключенную в ∠AOB .

Каждая точка этого фрагмента должна быть еще семь раз отображена с помощью преобразований симметрии для получения полной окружности.

Рис. 1

Кроме того, именно процедура Pixel_circle отвечает за расположение центра окружности.

Приступим к разбору ключевой идеи алгоритма. Пусть мы находимся в некоторой промежуточной фазе построения.

Мы только что поставили точку (xiyi) и теперь должны сделать выбор между точками 1(xi+1, yi-1) и 2(xi+1, y).

(Напомним, что мы строим часть окружности, заключенную в ∠AOB, следовательно, подняться выше мы не можем и спуститься вниз более чем на одну точку не можем тоже.)

Рис. 2

Реальная окружность может быть расположена относительно точек 1 и 2 одним из пяти способов 1-5. Если мы выбераем точку 1, то тем самым говорим, что (xi+1)2+(yi-1)2 >R2. Если же выбераем точку 2, то допускаем, что (xi+1)2+(yi)2 >R2. Рассмотрим две погрешности Δi1 и 0).

Вариант 3.1. |Δi1| ⇒ |Δi2| ⇒ Δi1+Δi2 < 0 ⇒ выбирается 2.

Вариант 3.2. |Δi1| < |Δi2|⇒Δi1+Δi2 > 0 ⇒ выбирается 1.

Для положения 4.

Δi1 = 0, Δi2 > 0 ⇒ Δi1+Δi2 > 0 ⇒ выбирается 1.

Для положения 5.

Δi1 > 0, Δi2 > 0 ⇒ Δi1+Δi2 > 0 ⇒ выбирается 1.

Далее получим выражение для контрольной величины Δi

Δi = Δi1+Δi2 = (xi+1)2+(yi-1)2-R2+(xi+1)2+(yi)2-R2 = 2xi2+2yi2+4xi-2yi+3-2R2.

Выражение для Δi+1 существенным образом зависит от выбора
следующей точки. Необходимо рассмотреть два случая: yi+1 = yi и
yi+1 = yi-1.

Δi+1 [при yi+1 = yi] = 2x2i+1+2y2i+1+4xi+1-2yi+1+3-2R2 = 2(xi+1)2+2yi2+4(xi+1)-2yi+3-2R2 = Δi+4xi+6.

Δi+1 [при yi+1 = yi-1] = 2x2i+1+2y2i+1+4xi+1-2yi+1+3-2R2 = 2(xi+1)2+2(yi-1)2+4(xi+1)-2(yi-1)+3-2R2 = Δi+4(xiyi)+10.

Теперь, когда получено рекуррентное выражение для Δi+1 через Δi, остается получить Δ1 (контрольную величину в начальной точке.) Она не может быть получена рекуррентно, ибо не определено предшествующее значение, зато легко может быть найдена непосредственно

x1 = 0, y1 = R ⇒ Δ11 = (0+1)2+(R-1)2-R2 = 2-2R,

Δ12 = (0+1)2+R2-R2 = 1

Δ1 = Δ11+Δ12 = 3-2R.

Таким образом, алгоритм построения окружности, реализованный в V_BRcirc, основан на последовательном выборе точек; в зависимости от знака контрольной величины Δi выбирается следующая точка и нужным образом изменяется сама контрольная величина. Процесс начинается в точке (0, r), а первая точка, которую ставит процедура Pixel_circle, имеет координаты (xcyc+r). При x = y процесс заканчивается.

#include  #define VGA_MODE 0x0013#define TEXT_MODE 0x0003typedef unsigned char byte;byte far* vga = (byte far *)0xA0000000L; void SetMode(unsigned short mode){ asm { mov ax, [mode] int 10h }} void putpixel(int x, int y, byte color){ vga[y*320+x] = color;} /*————————————————— V_Circle * Подпрограммы для генерации окружности * Pixel_circle – занесение пикселов с учетом симметрии * V_BRcirc – генерирует окружность по алгоритму * Брезенхема. * V_MIcirc – генерирует окружность по алгоритму * Мичнера. */ /*———————————————– Pixel_circle * Заносит пикселы окружности по часовой стрелке */ static void Pixel_circle (int xc,int yc,int x,int y,int pixel){ putpixel(xc+x, yc+y, pixel); putpixel(xc+y, yc+x, pixel); putpixel(xc+y, yc-x, pixel); putpixel(xc+x, yc-y, pixel); putpixel(xc-x, yc-y, pixel); putpixel(xc-y, yc-x, pixel); putpixel(xc-y, yc+x, pixel); putpixel(xc-x, yc+y, pixel);} /* Pixel_circle */ /* Генерирует 1/8 окружности по алгоритму Брезенхема * * Процедура может строить 1/4 окружности. * Для этого надо цикл while заменить на for (;;) * и после Pixel_circle проверять достижение конца по условию * if (y

Источник: http://www.opita.net/node/132

Мед-Центр Здоровье
Добавить комментарий