Растровая развёртка эллипса

Выполнил студент третьего курса

отделения прикладной математики

Шнуровский Виктор

 

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

Уравнение эллипса имеет вид . Перепишем его следующим образом: b2x2+a2y2=a2b2

Рассмотрим точку с абсциссой xm=. Касательная к эллипсу в этой точке будет составлять угол 45º. Тогда эллипс в первой четверти можно построить в два этапа: сперва при изменении абсциссы от 0 до xm, затем от a до xm.

Алгоритм Брезенхема пошагово генерирует очередные точки эллипса, выбирая на каждом шаге для занесения пикселя точку растра Pi(xi,  yi), ближайшую к истинному эллипсу, так чтобы ошибка ε(Pi) = b2xi2+a2yi2 - a2b2 была минимальной.

Рассмотрим генерацию части эллипса, лежащей в первой четверти, по часовой стрелке при изменении абсциссы от 0 до xm.

Проанализируем возможные варианты занесения i-й точки, после занесения i–1 -й.

При генерации эллипса в рассматриваемой области после занесения точки P(xi-1, yi-1) следующая точка может быть (см. рис) либо S (xi-1+1, yi-1) - перемещение по горизонтали, либо T (xi-1 + 1, yi-1 - 1) - перемещение по диагонали. Для этих возможных точек вычислим и сравним абсолютные значения разностей квадратов расстояний от центра эллипса до точки и эллипса:

|Si| = |(xi-1+1)2 b2 + yi-12a2 – a2b2|

|Ti| = |(xi-1+1)2 b2 + (yi-1 - 1)2a2 – a2b2|

Выбирается и заноситься та точка, для которой это значение минимально. Для определения того, какой пиксель выбрать, составим разность

di = |Si| - |Ti|= |(xi-1+1)2 b2 + yi-12a2 – a2b2| - |(xi-1+1)2 b2 + (yi-1 - 1)2a2 – a2b2|

И будем выбирать Ti если di > 0 и Si если di0.

Попробуем избавиться от модулей. Возможны 3 случая:

1.     Si > 0, а Ti < 0. Тогда di = Si + Ti.

2.     Si > 0, а Ti > 0. Тогда надо будет выбрать точку Ti. Если рассмотрим di = Si + Ti, то оно, очевидно, будет положительным, что соответствует точке Ti

3.     Si < 0, а Ti < 0. Тогда надо будет выбрать точку Si. Если рассмотрим di = Si + Ti, то оно, очевидно, будет отрицательным, что соответствует точке Si

Одним словом, знак выражения di = Si + Ti определяет следующую точку однозначно. Преобразуем его следующим образом:

di = Si + Ti = 2(xi-1+1)2 b2 + 2yi-12a2 +  a2 - 2yi-1 a2 - 2a2b2 =

= 2xi-12b2 + 4xi-1b2 + 2b2 + 2yi-12a2 + a2 - 2yi-1 a2 - 2a2b2 (*)

Выведем рекуррентную формулу для di. Для этого рассмотрим разность di+1 - di.

di+1 - di = 2xi2b2 + 4xib2 + 2b2 + 2yi2a2 + a2 - 2yi a2 - 2a2b2 – (2xi-12b2 + 4xi-1b2 + 2b2 +

2yi-12a2 + a2 - 2yi-1 a2 - 2a2b2)

Так как xi = xi-1 + 1, то di+1 - di = 4b2xi-1 + 6b2 + 2a2(yi - yi-1) (yi + yi-1+1)

Если на i-ом шаге выбрана точка S, то yi = yi-1 и di+1= di + 4b2xi-1 + 6b2; а если точка P, то yi = yi-1 – 1 и di+1= di + 4b2xi-1 + 6b2 - 4 a2 yi-1

Подставляя в формулу (*) начальные значения x = 0 и у = b, получаем, что d1 = 2b2a2 - 2b a2

Таким образом, дуга эллипса при изменении абсциссы от 0 до xm построена. Построение оставшейся части эллипса в первой четверти ведётся аналогично, только меняется направление просмотра и все х заменяются на у.

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

 

procedure Pixel4(xc, yc, x, y : integer);

begin

  PutPixel(xc + x, yc + y, cc);

  PutPixel(xc + x, yc - y, cc);

  PutPixel(xc - x, yc + y, cc);

  PutPixel(xc - x, yc - y, cc)

end;

 

procedure Ellipse(xc, yc : integer; XRadius, YRadius : Word);

var

  x, y : integer;

  xr2, yr2, d : longint;

  xm : double;

begin

  xr2 := sqr(XRadius) shl 1; yr2 := sqr(YRadius) shl 1;

  x := 0; y := YRadius; d := (yr2 - xr2 * y) + xr2 shr 1 ;

  xm := xr2 / sqrt((xr2 + yr2) shl 1) - 1;

  Pixel4(xc, yc, x, y);

  while x < xm do begin

    if d > 0 then begin

      dec(y);

      d := d + yr2 * (x shl 1 + 3) - xr2 * y shl 1    

    end

    else

      d := d + yr2 * (x shl 1 + 3);

    inc(x);

    Pixel4(xc, yc, x, y)

  end;

  d := (xr2 - yr2 * XRadius) + yr2 shr 1; x := XRadius; y := 0;

  Pixel4(xc, yc, x, y);

  xm := xm + 2;

  while x > xm do begin

    if d > 0 then begin

      dec(x);

      d := d + xr2 * (y shl 1 + 3) - yr2 * x shl 1

    end

    else

      d := d + xr2 * (y  shl 1 + 3);

    inc(y);

    Pixel4(xc, yc, x, y)

  end

end;

 

Проведённые исследования показывают, что приведённая выше процедура в 2.3 раза быстрее аналогичной из модуля Graph.