RU: Curve creating

Геометрические примитивы

Создание класса, производного от Geom_Curve

Если мы хотим создать собственный класс кривой, наследующий свойства класса Geom_Curve, мы должны реализовать его чисто виртуальные функции. Перечислим их.

Во-первых, есть две чисто виртуальные функции, которые наследуются от класса Geom_Geometry. Это

  • Выполнение преобразование кривой как геометрического объекта, т.е. смещение, поворот, масштабирование, отражение или комбинация этих преобразований:
// Выполнение преобразования кривой
   void   Transform (const gp_Trsf &T)
  • Создание копии кривой как геометрического объекта:
// Создание копии кривой
   Handle_Geom_Geometry  Copy () const

У самого класса Geom_Curve имеются следующие чисто виртуальные функции, которые должны быть реализованы в производном классе

  • Минимальное значение параметра на кривой. Если кривая бесконечна, это может быть значение, возвращаемое процедурой RealFirst() — минимальное вещественное число. Например, для прямой минимальное значение равно -Precision::Infinite().
// Процедура возвращает минимальное значение параметра на кривой.
   Standard_Real FirstParameter() const
  • Максимальное значение параметра на кривой. Если кривая бесконечна, это может быть значение, возвращаемое процедурой RealLast() — максимальное вещественное число. Для прямой это значение равно Precision::Infinite().
// Процедура возвращает максимальное значение параметра на кривой.
   Standard_Real LastParameter() const
  • Признак замкнутости кривой. Некоторые кривые (например, окружность) всегда замкнуты, другие — всегда незамкнуты. Кривая может считаться замкнутой, если расстояние между ее крайними точками меньше gp::Resolution()
// Возвращает значение true для замкнутой кривой
   Standard_Boolean IsClosed() const
  • Признак периодичности кривой. Периодическая кривая должна быть замкнутой и расстояние между точками P(u) и P (u + T), где T — период, должно быть меньше значения gp::Resolution(). Величина периода должна вычисляться функцией Standard_Real Period () const. По умолчанию она возвращает разность между максимальным и минимальным значениями параметра.
// Возвращает значение true для периодической кривой
   Standard_Boolean IsPeriodic() const
  • Гладкость кривой. Она характеризуется значением перечисляемого типа:
   enum GeomAbs_Shape {
      GeomAbs_C0, GeomAbs_G1, GeomAbs_C1, GeomAbs_G2, GeomAbs_C2, GeomAbs_C3, GeomAbs_CN
   }

имеющими следующий смысл:
— C0 : кривая непрерывна;
— G1 : кривая имеет непрерывно изменяющуюся касательную;
— C1 : кривая имеет непрерывную первую производную;
— G2 : кривизна кривой изменяется непрерывно:
— C2 : кривая имеет непрерывную вторую производную;
— C3 : кривая имеет непрерывную третью производную;
— CN : кривая бесконечно дифференцируема.
(Не совсем понятно, в чем состоит разница между G1 и C1, а также G2 и C2.)
// Функция возвращает значение гладкости кривой
   GeomAbs_Shape Continuity() const

// Возвращает true если степень гладкости кривой не менее N
   Standard_Boolean IsCN(const Standard_Integer N) const
  • Следующий набор процедур вычисляет значение точки кривой и производных для данного значения параметра.
// Процедура вычисляет значение точки P на кривой для значения параметра U
   void D0(const Standard_Real U,gp_Pnt& P) const

// Процедура вычисляет значение точки P и вектора первой производной V1
// для значения параметра U
   void D1(const Standard_Real U,gp_Pnt& P,gp_Vec& V1) const

// Процедура вычисляет значение точки P, первой V1 и второй V2 производных
// для значения параметра U
   void D2(const Standard_Real U,gp_Pnt& P,gp_Vec& V1,gp_Vec& V2)

// Процедура вычисляет значение точки P, первой V1, второй V2 и
// третьей V3 производных  для значения параметра U
   void D3(const Standard_Real U,gp_Pnt& P,gp_Vec& V1,gp_Vec& V2,gp_Vec& V3) const = 0;

// Процедура вычисляет значение N-ой производной  для значения параметра U
   gp_Vec DN(const Standard_Real U,const Standard_Integer N)
  • Следующие две процедуры связаны с обращением параметризации. Первая меняет параметризацию на обратную, т.е. если первоначально значению параметра FirstParameter отвечала точка StartPoint, а параметру LastParameter точка EndPoint, то после обращения параметру FirstParameter отвечает точка EndPoint, а параметру LastParameter точка StartPoint. Вторая возвращает значение параметра на обращенной кривой по первоначальному параметру, так что точки me->Value(U) и me->Reversed()->Value(me->ReversedParameter(U)) совпадают.
// Изменениие направления параметризации
   void Reverse()

// Значение параметра на обращенной кривой
   Standard_Real ReversedParameter(const Standard_Real U) const

В качестве примера создадим класс, описывающий винтовую линию. Винтовая линия определяется:
— радиусом $R$;
— шагом $h$;
— ориентацией (левый, правый винт) $\alpha=\pm 1$
— локальной системой координат gp_Ax2 ax2, в которой уравнение кривой имеет вид:

(1)
\begin{align} x_l &= R \cos {\alpha \varphi}, & y_l &= R \sin {\alpha \varphi}, & z_l &= h\varphi/(2\pi) \end{align}

Соответственно, должно храниться преобразование перехода от локальных $(x_l, y_l, z_l, O_l)$ к мировым $(x_w, y_w, z_w, O_w)$ координатам: если локальная система координат имеет оси
$\mathbf{e}_x=(e_{xx},e_{xy},e_{xz})$ (gp_Dir xDir = ax2.XDirection()),
$\mathbf{e}_y=(e_{yx},e_{yy},e_{yz})$ (gp_Dir yDir = ax2.YDirection()),
$\mathbf{e}_z=(e_{zx},e_{zy},e_{zz})$ (gp_Dir zDir = ax2.Direction())
и начало координат в точке
$O=(O_x, O_y, O_z)$ (gp_Pnt origin = ax2.Axis().Location()),
то мировые координаты точки связаны с локальными преобразованием:

(2)
\begin{align} \begin{pmatrix} x_w\\ y_w\\ z_w\\ 1\\ \end{pmatrix} = \begin{pmatrix} e_{xx} & e_{yx} & e_{zx} & O_x \\ e_{xy} & e_{yy} & e_{zy} & O_y \\ e_{xz} & e_{yz} & e_{zz} & O_z \\ 0 & 0 & 0 & 1 \\ \end{pmatrix} \begin{pmatrix} x_l\\ y_l\\ z_l\\ 1\\ \end{pmatrix} \end{align}

Первая производная равна

(3)
\begin{align} \frac{d x_l}{d \varphi} &= -R\alpha \sin {\alpha \varphi}, & \frac{d y_l}{d \varphi} &= R\alpha \cos {\alpha \varphi}, & \frac{d z_l}{d \varphi} &= \frac{h}{2\pi} \end{align}

Четная (вторая и выше) производная ($k$ — четное) равна

(4)
\begin{align} \frac{d^k x_l}{d \varphi^k} &= (-1)^{k/2}R\alpha^k \cos {\alpha \varphi}, & \frac{d^k y_l}{d \varphi^k} &= (-1)^{k/2}R\alpha^k \sin {\alpha \varphi}, & \frac{d z_l}{d \varphi} &= 0 \end{align}

а нечетная (третья и выше) производная ($k$ — нечетное)

(5)
\begin{align} \frac{d^k x_l}{d \varphi^k} &= (-1)^{(k+1)/2}R\alpha^k \sin {\alpha \varphi}, & \frac{d^k y_l}{d \varphi^k} &= (-1)^{(k-1)/2}R\alpha^k \cos {\alpha \varphi}, & \frac{d z_l}{d \varphi} &= 0 \end{align}

Описание класса винтовой линии (заголовочный файл) может иметь вид:

class vkScrewLine : public Geom_Curve 
{
public:
// Конструктор / деструктор
    vkScrewLine();
    virtual ~vkScrewLine();
//
// --- Реализация абстрактных методов базового класса
// Признак замкнутости кривой
    Standard_Boolean IsClosed() const { return Standard_False; }
// Признак периодичности кривой
    Standard_Boolean IsPeriodic() const { return Standard_False; }
// Гладкость кривой
    GeomAbs_Shape Continuity() const { return GeomAbs_CN; }
    Standard_Boolean IsCN(const Standard_Integer N) const { return Standard_True; };
// Минимальное / максимальное значения параметра на кривой.
    Standard_Real FirstParameter() const;
    Standard_Real LastParameter() const;
// Уравнение кривой
   void D0(const Standard_Real U,gp_Pnt& P) const;
   void D1(const Standard_Real U,gp_Pnt& P,gp_Vec& V1) const;
   void D2(const Standard_Real U,gp_Pnt& P,gp_Vec& V1,gp_Vec& V2) const;
   void D3(const Standard_Real U,gp_Pnt& P,gp_Vec& V1,gp_Vec& V2,gp_Vec& V3) const;
   gp_Vec DN(const Standard_Real U,const Standard_Integer N) const;
// Обращение параметра
    void Reverse();
    Standard_Real ReversedParameter(const Standard_Real U) const;
// Преобразование кривой как геометрического объекта
    void Transform (const gp_Trsf& T);
// Копирование кривой как геометрического объекта
    Handle(Geom_Geometry) Copy() const;
//
// --- Задание / опрос данных класса
// Радиус
    void SetRadius(Standard_Real radius);
    Standard_Real Radius( )
        { return myRadius; }
// Шаг винта
    void SetLead(Standard_Real lead);
    Standard_Real Lead( void)
        { return myLead; }
// Ориентация (правый/левый) винта
    void SetIsRight( Standard_Boolean isRight);
    Standard_Boolean IsRight( void )
        { return myIsRight; }
// Положение локальной системы координат
    void SetAxes( gp_Ax2 axes);
    gp_Ax2 Axes( void )
        { return myAxes; }
//
    DEFINE_STANDARD_RTTI(vkScrewLine)
//
private:
    Standard_Real myRadius;        // Радиус винта
    Standard_Real myLead;        // Шаг винтовой линии
    Standard_Boolean myIsRight;    // Признак: правый, левый винт
    gp_Ax2 myAxes;            // Оси, определяющие локальную систему координат
    gp_Trsf myL2WTrsf;            // Преобразование от локальной к мировой системе координат
//
// Процедуры перехода к мировой системе координат
    void CalcL2WTransform( void );
    void L2WTransform( gp_Pnt& P ) const;
    void L2WTransform( gp_Vec& V ) const;
};

DEFINE_STANDARD_HANDLE(vkScrewLine,Geom_Curve)

Приведем теперь реализацию методов. В конструкторе устанавливаются значения по умолчаню.
IMPLEMENT_STANDARD_HANDLE(vkScrewLine,Geom_Curve)
IMPLEMENT_STANDARD_RTTIEXT(vkScrewLine,Geom_Curve)

vkScrewLine::vkScrewLine()
{
// Радиус
    myRadius = 1.;
// Шаг
    myLead = 1.;
// Направление
    myIsRight = Standard_True;
// Локальная система координат
    myAxes = gp::XOY();
}

vkScrewLine::~vkScrewLine()
{
}

Минимальное и максимальное значение параметра — два витка винта:
//
// Минимальное / максимальное значения параметра на кривой.
Standard_Real vkScrewLine::FirstParameter() const 
{ 
    Standard_Real value = 0.; 
    return value; 
}
Standard_Real vkScrewLine::LastParameter() const 
{
    Standard_Real value = 4.*M_PI; 
    return value; 
}

Вычисление значений точки кривой и производных для данного значения параметра.
//
void vkScrewLine::D0(const Standard_Real phi,gp_Pnt& P) const
{
    double alpha = myIsRight ? +1 : -1;
    double x = myRadius*cos(alpha*phi);
    double y = myRadius*sin(alpha*phi);
    double z = myLead*phi/(2.*M_PI);
//
    P.SetCoord(x,y,z);
// Переход к мировой системе координат
    L2WTransform( P );
}
//
void vkScrewLine::D1(const Standard_Real phi,gp_Pnt& P,gp_Vec& V1) const
{
    double alpha = myIsRight ? +1 : -1;
    double x = myRadius*cos(alpha*phi);
    double y = myRadius*sin(alpha*phi);
    double z = myLead*phi/(2.*M_PI);
//
    P.SetCoord(x,y,z);
// Переход к мировой системе координат
    L2WTransform( P );
//
    double x1 = -alpha*y;
    double y1 = alpha*x;
    double z1 = myLead/(2.*M_PI);
    V1.SetCoord(x1,y1,z1);
// Переход к мировой системе координат
    L2WTransform( V1 );
}
//
void vkScrewLine::D2(const Standard_Real phi,gp_Pnt& P,gp_Vec& V1,gp_Vec& V2) const
{
    double alpha = myIsRight ? +1 : -1;
    double x = myRadius*cos(alpha*phi);
    double y = myRadius*sin(alpha*phi);
    double z = myLead*phi/(2.*M_PI);
//
    P.SetCoord(x,y,z);
// Переход к мировой системе координат
    L2WTransform( P );
//
    double x1 = -alpha*y;
    double y1 = alpha*x;
    double z1 = myLead/(2.*M_PI);
    V1.SetCoord(x1,y1,z1);
// Переход к мировой системе координат
    L2WTransform( V1 );
//
    double x2 = -alpha*alpha*x;
    double y2 = -alpha*alpha*y;
    V2.SetCoord(x2,y2,0.);
// Переход к мировой системе координат
    L2WTransform( V2 );
}
//
void vkScrewLine::D3(const Standard_Real phi,gp_Pnt& P,gp_Vec& V1,gp_Vec& V2,gp_Vec& V3) const
{
    double alpha = myIsRight ? +1 : -1;
    double x = myRadius*cos(alpha*phi);
    double y = myRadius*sin(alpha*phi);
    double z = myLead*phi/(2.*M_PI);
//
    P.SetCoord(x,y,z);
// Переход к мировой системе координат
    L2WTransform( P );
//
    double x1 = -alpha*y;
    double y1 = alpha*x;
    double z1 = myLead/(2.*M_PI);
    V1.SetCoord(x1,y1,z1);
// Переход к мировой системе координат
    L2WTransform( V1 );
//
    double alpha2 = alpha*alpha;
    double x2 = -alpha2*x;
    double y2 = -alpha2*y;
    V2.SetCoord(x2,y2,0.);
// Переход к мировой системе координат
    L2WTransform( V2 );
//
    double alpha3 = pow(alpha,3);
    double x3 = alpha3*y;
    double y3 = -alpha3*x;
    V3.SetCoord(x3,y3,0.);
// Переход к мировой системе координат
    L2WTransform( V3 );
}
//
gp_Vec vkScrewLine::DN(const Standard_Real phi,const Standard_Integer N) const
{
//    OutOfRange_Raise_if( N<1,"vkScrewLine::DN :  N<1");
//
    gp_Vec VN;
    double alpha = myIsRight ? +1 : -1;
    double x = myRadius*cos(alpha*phi);
    double y = myRadius*sin(alpha*phi);
    if( N==1 ) {
        double x1 = -alpha*y;
        double y1 = alpha*x;
        double z1 = myLead/(2.*M_PI);
        VN.SetCoord(x1,y1,z1);
    } else if( N%2==0 ) {    
    // N -- четное
        double alphaN = pow(alpha,N);
        int sign = N%4==0 ? +1 : -1;
        double xN = sign*alphaN*x;
        double yN = sign*alphaN*y;
        VN.SetCoord(xN,yN,0.);
    } else {
    // N -- нечетное
        double alphaN = pow(alpha,N);
        int signX = (N+1)%4==0 ? +1 : -1;
        int signY = (N-1)%4==0 ? +1 : -1;
        double xN = signX*alphaN*y;
        double yN = signY*alphaN*x;
        VN.SetCoord(xN,yN,0.);
    }
// Переход к мировой системе координат
    L2WTransform( VN );
    return VN;
}

Процедуры обращения параметризации
//
void vkScrewLine::Reverse()
{
    gp_Dir zDir = myAxes.Direction();
// Опрос координат направления
    Standard_Real x, y, z;
    zDir.Coord(x, y, z);
    zDir.SetCoord(-x, -y, -z);
    myAxes.SetDirection(zDir);
    Standard_Real dPhi = FirstParameter() + LastParameter();
    myAxes.Rotate(myAxes.Axis(),dPhi);
    Standard_Real dZ = dPhi/(2.*M_PI);
    gp_Vec zTrans(x*dZ,y*dZ,z*dZ);
    myAxes.Translate (zTrans);
// Преобразование к мировой системе координат
    CalcL2WTransform( );
}
//
Standard_Real vkScrewLine::ReversedParameter(const Standard_Real U) const
{
    Standard_Real value;
    value = FirstParameter()+LastParameter() - U;
    return value;
}

Реализация абстрактных методов класса Geom_Geometry.
Handle(Geom_Geometry) vkScrewLine::Copy() const 
{
    Handle(vkScrewLine) L;
    L = new vkScrewLine();
//
    L->SetRadius(myRadius);
    L->SetLead(myLead);
    L->SetAxes(myAxes);
    L->SetIsRight(myIsRight);
//
    return L;
}
//
void vkScrewLine::Transform (const gp_Trsf& T) 
{ 
    myAxes.Transform (T); 
    CalcL2WTransform( );
}

Внутренние методы винтовой линии
void vkScrewLine::CalcL2WTransform( void )
{
    gp_Dir xDir = myAxes.XDirection();
    gp_Dir yDir = myAxes.YDirection();
    gp_Dir zDir = myAxes.Direction();
    gp_Pnt origin = myAxes.Axis().Location();
    myL2WTrsf.SetValues(xDir.X(),yDir.X(),zDir.X(),origin.X(),
        xDir.Y(),yDir.Y(),zDir.Y(),origin.Y(),
        xDir.Z(),yDir.Z(),zDir.Z(),origin.Z(),1.e-10,1.e-10);
}
//
void vkScrewLine::L2WTransform( gp_Pnt& P ) const
{
    P.Transform(myL2WTrsf);
}
//
void vkScrewLine::L2WTransform( gp_Vec& V ) const
{
    V.Transform(myL2WTrsf);
}
//
void vkScrewLine::SetRadius(Standard_Real radius)
{
    myRadius = radius;
    CalcL2WTransform( );
}
//
void vkScrewLine::SetLead(Standard_Real lead)
{
    myLead = lead;
    CalcL2WTransform( );
}
//
void vkScrewLine::SetIsRight(Standard_Boolean isRight)
{
    myIsRight = isRight;
}
//
void vkScrewLine::SetAxes(gp_Ax2 axes)
{
    myAxes = axes;
    CalcL2WTransform( );
}