Целочисленный алгоритм построения эллипса
В компьютерной графике эллипс является важным геометрическим примитивом. Алгоритм Брезенхема для эллипса позволяет строить эллипс, используя только целочисленные операции, что обеспечивает высокую скорость работы и точность.
Эллипс с центром в начале координат описывается уравнением:
$\frac{x^2}{a^2} + \frac{y^2}{b^2} = 1$
где $a$ - горизонтальный радиус, $b$ - вертикальный радиус.
Умножим обе части уравнения на $a^2b^2$, чтобы избавиться от дробей:
$b^2x^2 + a^2y^2 = a^2b^2$
Определим функцию ошибки для точки $P(x_i, y_i)$:
$EllipseError(x_i, y_i) = b^2x_i^2 + a^2y_i^2 - a^2b^2$
Значение этой функции показывает, насколько точка $P$ отклоняется от идеального эллипса:
Если $EllipseError(x_i, y_i) = 0$, точка лежит точно на эллипсе
Если $EllipseError(x_i, y_i) > 0$, точка находится вне эллипса
Если $EllipseError(x_i, y_i) < 0$, точка находится внутри эллипса
В отличие от окружности, эллипс в первом квадранте строится в двух областях, разделенных точкой, где наклон касательной равен $-1$. Остальные квадранты эллипса могут быть получены последовательными отражениями.

Рассмотрим процесс построения в первой области.
Первая область начинается от точки $(a, 0)$. Движение против часовой стрелки. На каждом шаге $y$ увеличивается на 1, а $x$ может уменьшиться на 1 или остаться прежним. Движение заканчивается, когда наклон касательной достигает $-1$.
Пусть мы находимся в точке $(x, y)$. На следующем шаге мы перейдем к $y+1$, и нам нужно выбрать между точками:
- $P_g = (x, y+1)$ - движение по вертикали
- $P_d = (x-1, y+1)$ - движение по диагонали
Для выбора между этими точками сравниваем их ошибки:
$EllipseError(x, y+1) = |b^2x^2 + a^2(y+1)^2 - a^2b^2|$
$EllipseError(x-1, y+1) = |b^2(x-1)^2 + a^2(y+1)^2 - a^2b^2|$
Выбираем точку с меньшей ошибкой. Нам не нужно заново вычислять $EllipseError$ для каждой следующей точки, если мы просто отслеживаем, как меняется значение при изменении аргументов.
$EllipseError(x-1, y+1) = |EllipseError(x, y) + b^2(1 - 2x) + a^2(2y + 1)|$
$EllipseError(x, y+1) = |EllipseError(x, y) + a^2(2y + 1)|$
$$EllipseError(x-1, y+1) < EllipseError(x, y+1)$$
эквивалетно условию:
$$2 \cdot [b^2 \cdot x^2 + a^2 \cdot y^2 - b^2 \cdot a^2 + a^2 \cdot (2y + 1)] + b^2 \cdot (1 - 2x) > 0$$
Определим:
- $XChange = b^2(1 - 2x)$
- $YChange = a^2(2y + 1)$
- $EllipseError = b^2x^2 + a^2y^2 - a^2b^2$ (без модуля)
Подставляя эти определения, получаем следующее условие:
$2 \cdot EllipseError + XChange > 0$
Значения $YChange$ и $XChange$ можно быстро пересчитывать, введя константы $TwoASquare = 2a^2$ и $TwoBSquare = 2b^2$
После увеличения $y$ на 1:
$YChange_{new} = a^2(2(y+1) + 1) = a^2(2y + 2 + 1) = a^2(2y + 1) + 2a^2$
$a^2(2y + 1)$ - это текущее значение $YChange$, поэтому:
$YChange_{new} = YChange + TwoASquare$
Аналогично, после уменьшения $x$ на 1:
$XChange_{new} = XChange + TwoBSquare$
Таким образом, условие выбора точки $P_d$ (диагональной) вместо $P_g$ (вертикальной) в первой области сводится к проверке:
$2 \cdot EllipseError + XChange > 0$
Если условие истинно, то выбираем $P_d$ и уменьшаем $x$ на 1, иначе выбираем $P_g$ и оставляем $x$ без изменения.
Чтобы определить, когда заканчивается первая область и начинается вторая, используем условие наклона касательной. Наклон касательной к эллипсу:
$y' = \frac{-b^2 x}{a^2 y}$
Условие $y' = -1$ приводит к:
$b^2 x = a^2 y$
Умножаем обе части на 2:
$2 b^2 x = 2 a^2 y$
Вводим переменные:
- $StoppingX = 2 b^2 x$
- $StoppingY = 2 a^2 y$
Тогда условие продолжения в первой области:
$StoppingX \geq StoppingY$
Во второй области мы начинаем от точки $(0, b)$ и движемся по часовой стрелке. На каждом шаге мы увеличиваем $x$ на 1 и решаем, нужно ли уменьшать $y$ на 1.
Пусть мы находимся в точке $(x, y)$. На следующем шаге мы перейдем к $x+1$, и нам нужно выбрать между точками:
- $P_g = (x+1, y)$ - движение по горизонтали
- $P_d = (x+1, y-1)$ - движение по диагонали
Аналогично первой области, мы можем выразить изменение ошибки через текущие значения.
После преобразований, аналогичных выполненным для первой области, получаем условие выбора точки $P_d$ (диагональной) вместо $P_g$ (горизонтальной):
$2 \cdot EllipseError + YChange > 0$
Если условие истинно, то выбираем $P_d$ и уменьшаем $y$ на 1, иначе выбираем $P_g$ и оставляем $y$ без изменения.
Для второй области используются те же константы:
- $TwoASquare = 2a^2$
- $TwoBSquare = 2b^2$
Обновление переменных во второй области:
- При увеличении $x$ на 1 (что мы делаем на каждом шаге):
$EllipseError = EllipseError + XChange$
$XChange = XChange + TwoBSquare$
- При уменьшении $y$ на 1 (если условие выше истинно):
$EllipseError = EllipseError + YChange$
$YChange = YChange + TwoASquare$
Условие окончания второй области - это когда наклон касательной снова становится равным $-1$, что эквивалентно условию:
$StoppingX \leq StoppingY$
где $StoppingX = 2 b^2 x$ и $StoppingY = 2 a^2 y$ (как и в первой области).
