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. Наверх
|