Геометрические примитивы
Кривая Безье
Кривая Безье аппросксимирует (сглаживает) набор опорных точек. Чтобы дать наглядное представление об алгоритме постороения кривой, рассмотрим вначале подробно частные случаи двух, трех и четырех точек.
В случае двух точек $\mathbf{r}_0$, $\mathbf{r}_1$ построение кривой тривиально: можно только соединить их отрезком и вычислять промежуточное значение по формуле линейной интерполяции. Введем параметр $t$, изменяющийся в диапазоне от нуля до единицы, и запишем $\mathbf{b}^{(1)}(t) = (1-t) \mathbf{r}_0 + t \mathbf{r}_1$. Здесь $\mathbf{b}^{(1)}(t)$ — уравнение искомой кривой, кривой Безье первой степени. Коэффициенты ее обозначим через $B_0^{(1)}(t) = (1-t)$ и $B_1^{(1)}(t) = t$. Отметим выполнение условия $B_0^{(1)}(t) + B_1^{(1)}(t) = 1$.
Рассмотрим теперь случай трех опорных точек $\mathbf{r}_0$, $\mathbf{r}_1$, $\mathbf{r}_2$.
Как и выше, введем параметр $t$, изменяющийся в диапазоне от нуля до единицы, и по точкам $\mathbf{r}_0$, $\mathbf{r}_1$ построим точку $\mathbf{b}_0^{(1)}(t) = (1-t)\mathbf{r}_0+t\mathbf{r}_1$ (смотри рисунок). Аналогичную точку построим на отрезке $(\mathbf{r}_1, \mathbf{r}_2)$: $\mathbf{b}_1^{(1)}(t) = (1-t)\mathbf{r}_1+t\mathbf{r}_2$. Теперь проинтерполируем полученные точки: $\mathbf{b}_0^{(2)}(t) = (1-t)\mathbf{b}_0^{(1)} (t)+t\mathbf{b}_1^{(1)}(t)$. В результате мы получили параболу вида
(1)— кривую Безье второй степени. Введем следующие обозначения для коэффициентов этой параболы $B_0^{(2)}(t) = (1-t)^2$, $B_1^{(2)}(t) = 2t(1-t)$, $B_2^{(2)}(t) = t^2$.
Многочлены $B_i^{(2)}(t)$ возникают при разложении квадрата единицы $1^2 = ((1-t)+t)^2=B_0^{(2)}(t)+B_1^{(2)}(t)+B_2^{(2)}(t)$ и называются многочленами Бернштейна степени 2.
Найдем производную построенной кривой. Дифференцируя, получим
(2)так что при $t=0$ производная направлена по вектору $\mathbf{r}_1-\mathbf{r}_0$, а при $t=1$ производная имеет направление вектора $\mathbf{r}_2-\mathbf{r}_1$.
Суммируем описанные выше свойства:
- коэффициенты параболы (функции влияния опорных точек) при $0 \leq t \leq 1$ неотрицательны: $B_i^{(2)}(t)\geq 0$. Вследствие этого кривая $\mathbf{b}^{(2)}(t)$, целиком лежит внутри треугольника, образованного точками $\mathbf{r}_0$, $\mathbf{r}_1$, $\mathbf{r}_2$;
- сумма коэффициентов равна единице: $B_0^{(2)}(t)+B_1^{(2)}(t)+B_2^{(2)}(t) = 1$;
- кривая проходит через конечные точки $\mathbf{r}_0$ и $\mathbf{r}_2$: $\mathbf{b}^{(2)}(0)=\mathbf{r}_0$ и $\mathbf{b}^{(2)}(1)=\mathbf{r}_2$;
- в конечных точках кривая касается опорной ломаной: ${\mathbf{b}'}^{(2)}(0)= 2(\mathbf{r}_1-\mathbf{r}_0)$ и ${\mathbf{b}'}^{(2)}(1)=2(\mathbf{r}_2-\mathbf{r}_1)$.
В случае задания четырех опорных точек $\mathbf{r}_0$, $\mathbf{r}_1$, $\mathbf{r}_2$, $\mathbf{r}_3$ поступим аналогично. Последовательно строим многочлены первой $\mathbf{b}_i^{(1)}(t)$, второй $\mathbf{b}_i^{(2)}(t)$ и третьей $\mathbf{b}_i^{(3)}(t)$ степени:
(3)Кривая $\mathbf{b}_0^{(3)}(t)$ и есть искомый сглаживающий многочлен (кривая Безье) третьей степени: $\mathbf{b}^{(3)}(t) = \sum_{i=0}^3B_i^{(3)}(t)\mathbf{r}_i$ где
(4)— многочлены Бернштейна третьей степени. Можно показать, что при $t=0$ производная кривой Безье третьей степени направлена по вектору $\mathbf{r}_1-\mathbf{r}_0$, а при $t=1$ производная имеет направление вектора $\mathbf{r}_3-\mathbf{r}_2$.
Выписанные выше выражения естественно обобщаются на случай $n+1$ опорных точек. При этом выражение для кривой имеет вид: $\mathbf{b}^{(n)}(t) = \sum_{i=0}^n B_i^{(n)}(t)\mathbf{r}_i$, $0 \le t \le 1$, где коэффициенты, многочлены Бернштейна, неотрицательны и в сумме равны единице в каждой точке интервала. Кривая проходит через конечные точки $\mathbf{r}_0$ и $\mathbf{r}_n$: $\mathbf{b}^{(n)}(0)=\mathbf{r}_0$ и $\mathbf{b}^{(n)}(1)=\mathbf{r}_n$. В конечных точках кривая касается опорной ломаной.
Кроме описанной выше обычной кривой Безье иногда использют рациональную кривую, которая определяется следующим образом. Пусть заданы $n+1$ опорных точек $\mathbf{r}_0$, $\mathbf{r}_1, \ldots$, $\mathbf{r}_n$ и столько же положительных чисел $w_i$ — весовых коэффициентов. Рациональной кривой Безье $n$-ой степени называется кривая вида
(5)Рациональная кривая есть отношение двух обычных кривых Безье: числитель строится по опорным точкам $w_i\mathbf{r}_i$, а знаменатель по $w_i$. В случае одинаковых весовых коэффициентов рациональная кривая Безье превращается в обычную. Весовые коэффициенты дают дополнительные степени свободы для изменения поведения кривой: увеличивая вес $w_i$ вы приближаете кривую к опорной точке $\mathbf{r}_i$.
Класс Geom_BezierCurve
Класс предназначен для описания как обычной, так и рациональной кривой Безье. Кривая Безье определяется набором опорных точек (poles, control points) и набором весов (weight) в случае рациональной кривой.
Мы начнем с примеров построения кривой, а описание процедур класса приведем ниже. Построим кривую и проверим ее параметры.
// Задаем опорные точки
TColgp_Array1OfPnt points(1,4);
points.SetValue(1, gp_Pnt( 0.5, 0.0, 0.0 ) );
points.SetValue(2, gp_Pnt( 0.0, 1.0, 0.0 ) );
points.SetValue(3, gp_Pnt( 1.0, 1.0, 0.0 ) );
points.SetValue(4, gp_Pnt( 1.0, 0.0, 0.0 ) );
// Строим кривую
Geom_BezierCurve bezier(points);
// Проверим, что значение параметра вдоль
// кривой изменяется от 0 до 1
Standard_Real t;
t = bezier.FirstParameter(); // t = 0.0
t = bezier.LastParameter(); // t = 1.0
// Проверим, что начальная и конечная точки
// кривой совпадают с опорными
gp_Pnt pnt;
pnt = bezier.StartPoint(); // ( 0.5, 0.0, 0.0 )
pnt = bezier.EndPoint(); // ( 1.0, 0.0, 0.0 )
// То же самое можно сделать, указав параметр
bezier.D0(0.,pnt); // pnt = ( 0.5, 0.0, 0.0 )
bezier.D0(1.,pnt); // pnt = ( 1.0, 0.0, 0.0 )

//
// Вычислим теперь касательные в начальной и конечной точках.
// Проверим, что они направлены по отрезкам опорной ломаной
gp_Vec tang;
bezier.D1(0.0,pnt,tang); // tang = (-1.5, 3.0, 0.0)
bezier.D1(1.0,pnt,tang); // tang = ( 0.0,-3.0, 0.0)
Можно показать, что рациональная кривая Безье второй степени есть кривая второго порядка, лежащая в плоскости, проходящей через опорные точки. В частности, уравнение окружности можно предстасить в виде отношения многочленов
(6)Построим с помощью кривой Безье четверть единичной окружности
TColgp_Array1OfPnt points(1,3);
TColStd_Array1OfReal weights(1,3);
// Участок окружности. Опорные точки и веса
points.SetValue(1, gp_Pnt(1.,0.,0.));
points.SetValue(2, gp_Pnt(1.,1.,0.));
points.SetValue(3, gp_Pnt(0.,1.,0.));
weights.SetValue(1,1.);
weights.SetValue(2,1.);
weights.SetValue(3,2.);
//
Geom_BezierCurve circle(points,weights);
// Проверим расстояние точки на кривой до начала координат
gp_Pnt p;
circle.D0(0.5,p);
double dist = p.Distance(gp::Origin()); // dist = 1.
Увеличим степень построенной кривой — участка окружности. Обратим внимание на то, что что форма кривой при этом не изменяется (в дааном случае кривая остается окружностью), но опорные точки и веса пересчитываются.
// ..... продолжение
circle.Increase(3); // Кривая третьей степени
// Опросим новые опорные точки и веса. Их теперь четыре
TColgp_Array1OfPnt newPoints(1,4);
TColStd_Array1OfReal newWeights(1,4);
int nb = circle.NbPoles(); // nb = 4
circle.Poles(newPoints); // (1.0, 0.0), (1.0, 2./3.), (0.5, 1.0) (0.0, 1.0)
circle.Weights(newWeights); // 1.0, 1.0, 1 1/3, 2.0
circle.D0(0.5,p);
dist = p.Distance(gp::Origin()); // dist = 1.
Переходим к описанию процедур класса Geom_BezierCurve.
- Конструкторы
// Создается обычная (не рациональная) кривая Безье на основе массива опорных точек CurvePoles.
// Число опорных точек должно быть не меньше двух и не больше, чем MaxDegree + 1 (=26)
Geom_BezierCurve(const TColgp_Array1OfPnt& CurvePoles)
// Создается рациональная кривая Безье на основе массива опорных точек CurvePoles и массива весов PoleWeights.
// Если значения весов равны, строится обычная кривая.
// Длины массивов должны совпадать, вес не может быть нулевым,
// число опорных точек должно быть не меньше двух и не больше, чем MaxDegree + 1 (=26)
Geom_BezierCurve( const TColgp_Array1OfPnt& CurvePoles, const TColStd_Array1OfReal& PoleWeights )
// Создается новый объект -- копия данной кривой
Handle_Geom_Geometry Copy()
- Опрос значений точек на кривой и производных
// Значение точки, соответствующей параметру U. Значение параметра может лежать вне диапазона (0., 1.)
void D0(const Standard_Real U,gp_Pnt& P)
// Значение точки и первой производной, соответствующих параметру U
void D1(const Standard_Real U,gp_Pnt& P, gp_Vec& V1)
// Значение точки, первой и второй производных, соответствующих параметру U
void D2(const Standard_Real U,gp_Pnt& P,gp_Vec& V1,gp_Vec& V2)
// Значение точки, первой, второй и третьей производных, соответствующих параметру U
void D3(const Standard_Real U,gp_Pnt& P,gp_Vec& V1,gp_Vec& V2,gp_Vec& V3)
// Значение производной порядка N, соответствующей параметру U
gp_Vec DN(const Standard_Real U,const Standard_Integer N)
- Опрос параметров кривой
// Возвращается значение максмальной степени (числа опорных точек) кривой Безье. Это 25
Standard_Integer MaxDegree()
// Возвращается минимальное значение параметра кривой Безье. Это 0.0
Standard_Real FirstParameter()
// Возвращается максимальное значение параметра кривой Безье. Это 1.0
Standard_Real LastParameter()
// Возвращается точка кривой для минимального значения параметра (0.). Это первая опорная точка
gp_Pnt StartPoint()
// Возвращается точка кривой для максимального значения параметра (1.). Это последня опорная точка
gp_Pnt EndPoint()
// Возвращается число опорных точек
Standard_Integer NbPoles()
// Возвращается степень кривой (она на единицу меньшн числа опорных точек)
Standard_Integer Degree()
// Возвращается опорная точка номер Index. Обращаем внимание на то, что нумерация ведется с единицы.
gp_Pnt Pole(const Standard_Integer Index)
// Возвращается массив значений опорных точек. Длина массива должна быть равна их числу, т.е. NbPoles
void Poles(TColgp_Array1OfPnt& P)
// Взвращается значение веса номер Index (нумерация ведется с единицы).
Standard_Real Weight(const Standard_Integer Index)
// Возвращается массив значений весов. Длина массива должна быть равна их числу, т.е. NbPoles
void Weights(TColStd_Array1OfReal& W)
// Возвращается True, если первая и последняя опорные точки совпадают
Standard_Boolean IsClosed()
// Возвращается false, если значения всех весов совпадают
Standard_Boolean IsRational()
- Задание/изменение параметров кривой
// Задание нового значения опорной точки номер index. Если кривая рациональная, вес точки не меняется.
void SetPole(const Standard_Integer Index,const gp_Pnt& P)
// Задание нового значения веса номер index.
void SetWeight(const Standard_Integer Index, const Standard_Real Weight)
// Задание новых значений опорной точки и веса номераIndex.
void SetPole(const Standard_Integer Index, const gp_Pnt& P,const Standard_Real Weight)
// Увеличение степени кривой Безье. При этом вид кривой не изменяется, а опорные точки и веса пересчитываются.
void Increase(const Standard_Integer Degree)
// Вставка новой опорной точки после точки с номером Index.
// Если кривая рациональная, вес точки полагается равным единице.
// В результате вставки степень кривой увеличивается на единицу.
void InsertPoleAfter(const Standard_Integer Index,const gp_Pnt& P)
// Вставка новой опорной точки перед точкой с номером Index.
void InsertPoleBefore(const Standard_Integer Index,const gp_Pnt& P)
// Вставка новой опорной точки P с весом Weight после точки с номером Index.
void InsertPoleAfter( const Standard_Integer Index, const gp_Pnt& P, const Standard_Real Weight) ;
// Вставка новой опорной точки P с весом Weight перед точкой с номером Index.
void InsertPoleBefore( const Standard_Integer Index, const gp_Pnt& P, const Standard_Real Weight) ;
// Уничтожение опорной точки с номером Index. В результате степень кривой уменьшается на единицу.
void RemovePole(const Standard_Integer Index)
// Изменение параметризации кривой на обратную: Value (NewU) = Value (1 - OldU)
void Reverse()
// Возвращение значения параметра при обратной параметризации: возвращается 1-U
Standard_Real ReversedParameter(const Standard_Real U)
// Применение преобразования T к кривой
void Transform(const gp_Trsf& T)