netlib.narod.ru | < Назад | Оглавление | Далее > |
Порой полезно знать формулы, по которым графическая система рассчитывает визуализацию кривых. Например, это может понадобиться, чтобы расположить другие графические элементы (скажем, символы текста) в соответствии с нарисованной системой кривой. Кроме того, вывод формул для расчета кривых — неплохое упражнение (не с Луны же они берутся!).
Кривая Безье — это полином третьего порядка. Как и все полиномы третьего порядка, кривая Безье уникально определяется четырьмя точками. Мы обозначили эти точки p0 (начальная), p1, p2 (две управляющие) и p3 (конечная). Их можно обозначить и так: (x0, y0), (x1, y1), (x2, y2) и (x3, y3).
Полином третьего порядка, задающий координаты точек в двумерном пространстве, можно записать в форме параметрических уравнений общего вида:
x(t) = ax · t3 + bx · t2 + cx · t + dx
y(t) = ay · t3 + by · t2 + cy · t + dy
где ax, bx, cx, dx, ay, by, cy и dy — константы, a t меняется от 0 до 1. Любая кривая Безье уникально определяется этими 8 константами. Их значения зависят от координат четырех точек, задающих кривую. Цель этого упражнения — вывести уравнения для расчета восьми констант по заданным координатам четырех точек.
Наше первое допущение для вывода этих уравнений в том, что кривая Безье начинается в точке с координатами (x0, y0) при t = 0:
x(0) = x0
y(0) = y0
Даже такое простое допущение позволяет продвинуться в выводе уравнений для констант. Подставив в параметрические уравнения t = 0 получим:
x(0) = dx
y(0) = dy
Это означает, что две из констант — это просто координаты начальной точки:
dx = x0 (1a)
dy = y0 (1b)
Второе допущение, касающееся кривой Безье: она заканчивается в точке с координатами (x3, y3) при t = 1:
x(1) = x3
y(1) = y3
Подставив в параметрические уравнения 1 вместо t, получаем:
x(1) = ax + bx + cx + dx
y(1) = ay + by + cy + dy
что означает наличие следующей связи между константами и координатами конечной точки:
ax + bx + cx + dx = x3 (2a)
ay + by + cy + dy = y3 (2b)
Остальные допущения касаются первых производных параметрических уравнений, описывающих угол наклона кривой. Первую производную параметрического уравнения общего вида, задающего полином третьего порядка как функцию переменной t, можно записать так:
x'(t) = 3ax · t2 + 2bx · t + cx
y'(t) = 3ay · t2 + 2by · t + cy
Нас, в частности, интересует угол наклона кривой в конечных точках. Как известно, прямая, проведенная из начальной точки через первую управляющую точку, проходит по касательной к кривой Безье и направлена в ту же сторону, что и кривая. Обычно эту прямую задают параметрическими уравнениями:
x(t) = (x1 – x0) t + x0
y(t) = (y1 – y0) t + y0
где t изменяется от 0 до 1. Но можно задать ее и иначе:
x(t) = 3(x1 – x0) t + x0
y(t) = 3(y1 – y0) t + y0
где t изменяется от 0 до 1/3. Почему именно 1/3? Дело в том, что длина той части кривой Безье, по касательной к которой проходит прямая, проведенная из точки p0 через p1 направленная в ту же сторону, что и кривая, равна примерно 1/3 от общей длины кривой. Первые производные модифицированных параметрических уравнений можно записать так:
x'(t) = 3(x1 – x0)
y'(t) = 3(y1 – y0)
Если нужно рассчитать по этим уравнениям угол наклона кривой Безье при t = 0, то:
x'(0) = 3(x1 – x0)
y'(0) = 3(y1 – y0)
Подставив t = 0 в уравнение первой производной полинома третьего порядка, получим:
x'(0) = cx
y'(0) = cy
Это позволяет записать равенство:
cx = 3(x1 – x0) (3a)
cy = 3(y1 – y0) (3b)
Последнее допущение таково: прямая, проведенная из второй управляющей точки через конечную, является касательной к кривой Безье в ее конечной точке и направлена в ту же сторону, что и кривая. Иначе говоря:
x'(1) = 3(x3 – x2)
y'(1) = 3(y3 – y2)
Из уравнений общего вида следует, что:
x'(1) = 3ax + 2bx + cx
y'(1) = 3ay + 2by + cy
поэтому:
3ax + 2bx + cx = 3(x3 – x2) (4a)
3ay + 2by + cy = 3(y3 – y2) (4b)
Выражения 1а, 2а, 3а и 4а дают четыре уравнения с четырьмя неизвестными, которые можно решить относительно ax, bx, cx и dx, выразив их через x0, x1, x2 и x3. Выполнив ряд алгебраических преобразований, получаем:
ax = –x0 + 3x1 – 3x2 + x3
bx = 3x0 – 6x1 + 3x2
cx = 3x0 + 3x1
dx = x0
Уравнения 1b, 2b, 3b и 4b позволяют сделать то же самое для коэффициентов y. После этого можно подставить константы обратно в параметрическое уравнение общего вида для полинома третьего порядка:
x(t) = (–x0 + 3x1 – 3x2 + x3) · t3 + (3x0 – 6x1 + 3x2) · t2 + (3x0 + 3x1) · t + x0
y(t) = (–y0 + 3y1 – 3y2 + y3) · t3 + (3y0 – 6y1 + 3y2) · t2 + (3y0 + 3y1) · t + y0
В сущности, на этом можно было бы закончить. Но лучше раскрыть скобки и привести подобные слагаемые. В итоге получатся более элегантные параметрические уравнения, с которыми проще работать:
x(t) = (1 – t)3 · x0 + 3t(1 – t)2 · x1 + 3t2(1 – t) · x2 + t3 · x3
y(t) = (1 – t)3 · y0 + 3t(1 – t)2 · y1 + 3t2(1 – t) · y2 + t3 · y3
Такими уравнениями обычно задают кривые Безье.
В следующей программе класс BezierManual переопределяет класс Bezier из программы Bezier, показанной выше, и рисует вторую кривую Безье. Но на этот раз кривая рассчитывается «вручную» при помощи параметрических уравнений, которые мы только что вывели.
BezierManual.cs
//--------------------------------------------- // BezierManual.cs (C) 2001 by Charles Petzold //--------------------------------------------- using System; using System.Drawing; using System.Windows.Forms; class BezierManual: Bezier { public new static void Main() { Application.Run(new BezierManual()); } public BezierManual() { Text = "Bezier Curve \"Manually\" Drawn"; } protected override void OnPaint(PaintEventArgs pea) { base.OnPaint(pea); BezierSpline(pea.Graphics, Pens.Red, apt); } void BezierSpline(Graphics grfx, Pen pen, Point[] aptDefine) { Point[] apt = new Point[100]; for (int i = 0; i < apt.Length; i++) { float t = (float) i / (apt.Length - 1); float x = (1 - t) * (1 - t) * (1 - t) * aptDefine[0].X + 3 * t * (1 - t) * (1 - t) * aptDefine[1].X + 3 * t * t * (1 - t) * aptDefine[2].X + t * t * t * aptDefine[3].X; float y = (1 - t) * (1 - t) * (1 - t) * aptDefine[0].Y + 3 * t * (1 - t) * (1 - t) * aptDefine[1].Y + 3 * t * t * (1 - t) * aptDefine[2].Y + t * t * t * aptDefine[3].Y; apt[i] = new Point((int) Math.Round(x), (int) Math.Round(y)); } grfx.DrawLines(pen, apt); } }
Метод OnPaint в программе BezierManual вызывает одноименный метод из базового класса (которым является класс Bezier), а затем — метод BezierSpline из своего класса. Определение метода BezierSpline не отличается от DrawBeziers за исключением использования объекта Graphics в качестве первого аргумента и возможности обработки лишь одной кривой Безье. Этот метод задействует массив из 100 структур Point, каждая из которых рассчитывается на основе полученных выше параметрических уравнений, после чего рисует кривую как составную линию. Программа рисует кривую Безье, рассчитанную «вручную», красным цветом, что позволяет сравнить ее с аналогичной кривой, нарисованной Windows Forms. Кривые, конечно, отличаются, но не более чем на 1 пиксел.
netlib.narod.ru | < Назад | Оглавление | Далее > |