class Vector3d
{
template <typename T> void mul ( const T & t, ... )
{
*this = t ( *this );
}
template <typename T> void mul ( const T & t, double d )
{
x *= d;
y *= d;
z *= d;
}
public :
double x, y, z;
Vector3d () {}
Vector3d ( double a, double b, double c ) : x ( a ), y ( b ), z ( c ) {}
Vector3d operator - () const
{
return Vector3d ( - x, - y, - z );
}
Vector3d & operator += ( const Vector3d & v )
{
x += v.x;
y += v.y;
z += v.z;
return * this;
}
Vector3d & operator -= ( const Vector3d & v )
{
x -= v.x;
y -= v.y;
z -= v.z;
return * this;
}
Vector3d & operator *= ( const Vector3d & v )
{
x *= v.x;
y *= v.y;
z *= v.z;
return * this;
}
Vector3d & operator /= ( const Vector3d & v )
{
x /= v.x;
y /= v.y;
z /= v.z;
return * this;
}
template <typename T> Vector3d & operator *= ( const T & t )
{
mul ( t, t );
return * this;
}
Vector3d & operator /= ( const double d )
{
x /= d;
y /= d;
z /= d;
return * this;
}
Vector3d & fill ( double d = 0 )
{
x = y = z = d;
return * this;
}
bool operator ! () const
{
return !x && !y && !z;
}
// Получение перпендикуляра к данному вектору
Vector3d perpendicular () const;
// Задание векторных норм
Vector3d & setNorm1 ( double p = 1 ); // единичная норма
Vector3d & setNorm2 ( double p = 1 ); // квадратичная норма
Vector3d & setNormU ( double p = 1 ); // бесконечная норма
};
Конструктор без параметров не инициализирует данные. Я видел, что некоторые люди инициализируют данные нулями, но на мой взгляд это лишняя работа. Я понимаю, что современные процессоры очень быстрые, но если что-то можно не делать, то лучше не делать. Конструктор копии, также как и оператор присваивания, здесь будут лишними, так как компилятор по умолчанию сгенерирует то же самое. Унарный минус, как и несколько последующих функций возвращает неконстантное значение, хотя я читал о других рекомендациях. А сделано это для того, чтобы к результату можно было применить функцию модифицирующую значение. Например, я могу написать следующее: const Vector3d v3 = ( v1 - v2 ).getNorm2(); +=, -=. Здесь всё понятно. Для *= есть функция с параметром типа Vector3d и шаблонная функция.
Если параметр функции может быть преобразован к типу double, то тогда вектор умножается на это число.
Иначе считается, что параметр - это функтор и вектор преобразуется им.
Функция fill заполняет вектор заданным значением ( нулевым по умолчанию ). Оператор ! для вектора здесь делает то же самое, что и для чисел, т.е. if ( ! v ) ... - выполняется для нулевого вектора, а if ( !! v ) ... - выполняется для ненулевого вектора. Функция-член perpendicular возвращает вектор единичной длины перпендикулярный данному. На самом деле таких векторов бесконечно много. Здесь какой-то один из них. Функции-члены setNorm... в случае, когда вектор ненулевой, делают соответсвующую ему векторную норму равной значению | p | ( по умолчанию 1 ). Если параметр p - отрицательный, то вектор меняет направление на противоположное. В случае, когда вектор нулевой - он остаётся нулевым. const Vector3d null3d ( 0, 0, 0 );Я давно хотел придумать более короткую запись, чем Vector3d ( 0, 0, 0 ). Наконец 15 апреля 2016 года появился null3d.
inline Vector3d operator + ( const Vector3d& a, const Vector3d& b )
{
return Vector3d ( a.x + b.x, a.y + b.y, a.z + b.z );
}
inline Vector3d operator - ( const Vector3d& a, const Vector3d& b )
{
return Vector3d ( a.x - b.x, a.y - b.y, a.z - b.z );
}
inline Vector3d operator * ( const Vector3d& a, double d )
{
return Vector3d ( a.x * d, a.y * d, a.z * d );
}
inline Vector3d operator / ( const Vector3d& a, double d )
{
return Vector3d ( a.x / d, a.y / d, a.z / d );
}
inline Vector3d operator * ( double d, const Vector3d& a )
{
return Vector3d ( a.x * d, a.y * d, a.z * d );
}
inline double operator * ( const Vector3d& a, const Vector3d& b )
{
return a.x * b.x + a.y * b.y + a.z * b.z;
}
}
inline Vector3d operator % ( const Vector3d& a, const Vector3d& b )
{
return Vector3d ( a.y * b.z - a.z * b.y,
a.z * b.x - a.x * b.z,
a.x * b.y - a.y * b.x );
}
Сумма, разность двух векторов, умножение ( с двух сторон ) и деление ( с одной стороны ) на число - обычно эти реализации споров не вызывают. Другое дело - скалярное и векторное произведения. Тут большое количество вариантов. Я видел употребление знаков &, |, ^ и разные названия функций. Мой подход - это * для скалярного и % для векторного произведения. В математике ( а работа с векторами это прежде всего математика, а потом уже программирование ) скалярное произведение обозначается, как обычное умножение для чисел - точкой. Т.к. в С++ ( и других языках ) умножение обозначается *, отсюда и выбор для скалярного произведения. Векторное произведение в математике обозначается двуми перекрещенными палочками. Отсюда и выбор знака % для обозначения векторного произведения. Одна палочка в нём есть, а вторую символизируют два кружочка. В чём ещё преимущество этих знаков перед &, |, ^ так это приоритет выполнения. В выражениях типа v1*v2 + v3*v4 скобки не нужны, а если использовать знаки &, |, ^, то у них приоритет выполнения ниже, чем у знака + и придётся использовать скобки. Помимо неудобств это противоречит математической нотации.
inline double qmod ( const Vector3d& a )
{
return a.x * a.x + a.y * a.y + a.z * a.z;
inline bool operator != ( const Vector3d & a, const Vector3d & b )
{
return a.x != b.x || a.y != b.y || a.z != b.z;
}
inline bool operator == ( const Vector3d & a, const Vector3d & b )
{
return a.x == b.x && a.y == b.y && a.z == b.z;
}
inline void reper ( const Vector3d & x, Vector3d & y, Vector3d & z )
{
y = x.perpendicular ();
z = x % y;
}
double norm1 ( const Vector3d & v ); // единичная норма
double norm2 ( const Vector3d & v ); // квадратичная норма
double normU ( const Vector3d & v ); // бесконечная норма
Функция qmod возвращает квадрат модуля вектора или другими словами квадрат длины вектора. Эта функция в отличии от norm2 не использует квадратный корень (sqrt), поэтому когда надо сравнить длины двух векторов лучше использовать эту. По поводу операторов сравнения ( ==, != ) можно сказать следующее. С ними надо обращаться разумно. Числа типа double могут незначительно отличаться по каким-то причинам и тогда сравнивать их таким образом не нужно. С другой стороны, если известно, что вектора имеют конкретные значения, то их можно сравнивать. Функция reper делает из одного вектора ( первый параметр ) ещё два взаимно перпендикулярных, т.е. мы получаем правую прямоугольную тройку векторов. Если первый вектор был единичным, то и два других тоже будут единичными. Функции norm1, norm2 и normU вычисляют соответствующие векторные нормы. Примеры использования класса Vector3d можно посмотреть в приложении DEMO. Исходники находятся в файлах vector3d.h, vector3d.cpp. Наверх
|