<泛> C++3D數學庫設計詳解 向量篇
// 注:本內容為作者原創,禁止在其他網站複述內容以及用於商業盈利,如需引用,請標明出處:http://www.cnblogs.com/lv_anchoret/
Preface
為了支援光線追蹤的學習,決定寫一個3D泛型數學庫。
我採用的是windows平臺以及C++Template技術
我的庫檔案組織目錄如下
--lvgm
----test
------testVec.cpp
----type_vec
------lv_vec2.h
------lv_vec3.h
------type_vec.h
------vec_inout.h
----lv_precision.h
----lvgm.h
Ready
這一篇,需要您瞭解C++Template的基本語法
需要您瞭解向量的運算
該向量庫檔案解釋:
二維向量模板類
三維向量模板類
資料精度設定
本庫提供的向量相關的預設輸出形式設定檔案
該向量庫檔案暫時沒有四元組lv_vec4,我將在之後新增並單獨寫一篇進行陳述
該向量庫能為您提供的功能:
對向量內部資料方便自由地調取和設定
向量的正負
向量的加減乘除
向量的自增自減
向量的索引
向量判等
向量的賦值以及複合運算賦值
向量的範數
向量的範數平方
向量的自身單位化
返回該向量的高精度單位化向量
向量的內積和外積運算(·、×)
向量判空
Design
由於二維和三維的設計相仿,故此處以三維為例進行描述
<1>型別相關
本類中公開定義資料值型別為模板參T,範數精度型別為經外部巨集設定的型別——precision, 預設為double
設計問題:一開始我們可能想到在模板類裡面利用巨集控制資料儲存精度,但你可能會遇到問題。例如:
# ifdef HIGHPRECISION //set the high precision using norm_t = long double; # elif(defined LOWPRECISION) //set the low preciion using norm_t = float; # else using norm_t = double; //default precision # endif //set precision
假設我現在有一個int的三維向量,我想要返回一個實數精度(norm_t)的單位化向量,於是我們寫了一個成員函式vec3<norm_t> ret_unit()const,我們在主函式中需要建立一個vec3去接收ret_unit的返回值
那麼,我們兩手一攤,無可奈何你可能這樣做:
vec3<??> normal = intV.ret_unit();
你可能做不到,??可能是vec3::norm_t 嗎,顯然不是,vec3是一個模板,只能先將vec3<T>中的T特化。突然覺得,模板類中公開公佈了精度型別norm_t,但是卻用不上??
解決方案
綜合考量到其他類可能也需要精度設定,所以乾脆把這設定部分程式碼單獨出來,然後將norm_t 改名為precision,於是問題就解決了
模板類中只需要提前預處理precision檔案即可進行如下簡單定義:
using norm_t = precision;
而主函式中也方便多了
vec3<precision> normal = intV.ret_unit();
<2>引數型別
我看過glm數學庫的原始碼有一類函式是這麼實現的
template <typename T, precision P> template <typename U> GLM_FUNC_QUALIFIER tvec3<T, P> & tvec3<T, P>::operator+=(tvec3<U, P> const & v) { this->x += static_cast<T>(v.x); this->y += static_cast<T>(v.y); this->z += static_cast<T>(v.z); return *this; }
其實,意思就是它允許+=另外一種型別的向量,然後我都強轉到自身型別T之後進行運算
解決方案
個人有一拙見,我是下面這樣實現的,如果有什麼漏洞請郵件或者評論留言。
我可以通過“過載”static_cast,或者進行一些操作使得vec3模板類能夠實現類似內建整型之間的隱式自動型別轉換
那麼,我就不需要設定多個模板參在內部static_cast了。
好,我們這麼做就可以了:
template<typename E> vec3(const vec3<E>& vec); //static_cast
我在定義建構函式的時候支援其他型別的vec3,哪裡需要vec3值傳遞,我就呼叫它。
<3>對資料進行方便自由的操作
很多數學庫貌似可以直接v.x v.y ,很多C-struct設計,但作為C++黨,用C++語言寫程式碼,要嚴格遵守資料隱藏,在不失語言原則的情況下做到最方便。
1)很多庫支援 v.x = 3;
於是我定義:
inline T& x() { return _x; }
但我還是過載了常量版本
inline const T& x()const { return _x; }
我希望對內部資料的修改的禁止令可以通過引數來實現,比如:
template<typename T> inline vec3<T> operator/(const vec3<T>& v1, const vec3<T>& v2) { //the operator of / ,example 3 * 5 -> 15 , (1,2,3) * (2,3,4) -> (1/2,2/3,3/4) assert(v2.isnull()); return operator/<T, T> (v1, v2); }
所以,我僅僅去過載v.x()的const版本,而不去禁止x()可修改
2)GLSL中還支援這種騷操作:v.rgb = v.gbr; or v.rg = v1.rg
我看了glm庫,它暫時沒有實現上述的操作支援
而GLSL庫我還沒研讀
所以,憑著自身粗淺的技術,只能實現獲取資料元組,而不能實現修改:
inline vec2<T> xy() { return vec2<T>{_x, _y}; }
<4>運算子設計
按照C++operator普遍的設計原則,依舊是將單目和(複合)賦值運算子過載定義為成員函式,而將雙目運算子定義為友元或者外部函式,在本庫中採用STL設計原則,定義為名稱空間內的類外函式,為了不破壞C++類的封裝性
++、--等單目運算子請參見我的另外一篇專門解說運算子過載的文章
此處,我只陳述與vec3類相關的設計細節
關於加減法,從數學角度講,一個向量加減一個標量是非法的,所以,本庫中不支援向量和標量的加減法,對於將每一個元素加同一個值,請用偏移向量進行。
而乘法和除法則支援與標量進行運算,因為一個標量乘以一個向量,只是把向量長度延伸了,在數學上也是合法的。
除此之外,考慮到兩個vec3物件進行乘除法,如果this是int其他是另外一個是實數的話,我覺得還是進行精度提升的好,所以有專門的過載,且應用了C++11的自動追蹤返回值型別技術
關於%以及%=,從數學角度講,實數並不支援%運算,只有integer才有,而在圖形運算過程中,大多是實數,儘管本庫不全應用於圖形計算,但是%合法運算在工程專案中佔得也並不多,所以,如果需要,請自行將每一個元素進行%,庫設計中不會因為極小部分的應用而使庫變得臃腫
向量範數以及單位化(標準化)
一個型別設計點:利用使用者設定精度型別norm_t定義範數的值型別以及返回的標準化向量模板參。
關於向量單位化,我寫了兩個,一個是自身單位化,此時遵循本身型別進行,意思就是int進行單位化仍然裡面的元素是int。
另一個是返回單位化向量,這個時候是實數向量。
我想陳述的本庫相關的設計原則基本完畢。
TEST
測試效果:
△--****************** CONSTRUCTOR TEST ****************** ******* ivec3 test ********* there are two ivec3s as intV{ 1,-2,3 } and intV2{ 1, }, the value of which as follows [ 1, -2, 3 ] [ 1, 0, 0 ] there is a ivec2 : _2ivec{1,2}, and a integer 7 to construct a ivec3 as follows the vec2 in front of the integer of 7: [ 1, 2, 7 ] the number of 7 in front of vec2: [ 7, 1, 2 ] ******* fvec3 test ********** there is a fvec3 as fV{ 1.f,2.1f, }, the value of which as follows [ 1, 2.1, 0 ] there is a fvec2 : t{1.2f,}, and a value 3 to construct a ivec3 as follows f2to3 : [ 1.2, 0, 3 ] △--******************* FUNCTIONS TEST ******************** there is a ivec3{1, -2, 3} the operator + or - of ivec3 as follows: + : [ 1, -2, 3 ] - :[ -1, 2, -3 ] ----------------------------------------------------------- there is a ivec3{1, -2, 3} ++ivec3: the val of expression:[ 2, -1, 4 ] the val of ivec3:[ 2, -1, 4 ] ivec3++: the val of expression:[ 1, -2, 3 ] the val of ivec3:[ 2, -1, 4 ] the operator of -- is the same as above ----------------------------------------------------------- the operator[] of ivec3 as follows: the intV[2] is 4 ----------------------------------------------------------- there are two ivec3s as intV{ 1,-2,3 } and intV2{ 1, }, the value of which as follows intV is not equ to intV2 the operator = such that: intV2 = intV; the result as follows: intV2 is [ 2, -1, 4 ] intV is equ to intV2 there are two ivec3s as intV{ 1,-2,3 } and intV2{ 1, }, the value of which as follows the operator += such that: intV += intV2, the result of which as follows: intV is: [ 3, -1, 4 ] the operator -= such that: intV -= intV2, the result of which as follows: intV is: [ 2, -1, 4 ] the value of intV is to become the original value there are two ivec3s as intV{ 1,-2,3 } and intV2{ 2,1,3 }, the value of which as follows the operator *= such that: intV *= intV2, the result of which as follows: intV is: [ 4, -1, 12 ] the operator /= such that: intV /= intV2, the result of which as follows: intV is: [ 2, -1, 4 ] the value of intV is to become the original value ----------------------------------------------------------- the operator *= (number)such that: intV *= 5, the result of which as follows: intV is: [ 10, -5, 20 ] the operator /= (number) such that: intV /= 5, the result of which as follows: intV is: [ 2, -1, 4 ] the value of intV is to become the original value the operator + 、 -、 * 、/ (ivec3 or number) is the same as above ----------------------------------------------------------- the operator* between ivec3 and fvec3 as follows: there is a ivec3: intV{1,-2,3}, there is a fvec3: fV{1.1f,2.3f,3.8f}, and the result of ivec3*fvec3 as follows: res is: [ 1.1, -4.6, 11.4 ] the result of * is up to the higher precision of both the operator* between ivec3 and float as follows: there is a ivec3: intV{1,-2,3}, there is a float: 3.14, and the result of ivec3*3.14 as follows: res2 is: [ 3, -6, 9 ] the type of ivec3 * float is not fvec3 but ivec3, and the factor is just a factor that shouldn't change the vec's precision if you need the result's type to become fvec3,you should use static_cast<fvec3>(intV) * float res3 is: [ 3.14, -6.28, 9.42 ] the operator/ between different type is the same as above ----------------------------------------------------------- the normal() test as follows: there is a ivec3: intV{1,-2,3} the Norm of intV is: 3.74166 there is a fvec3: fV{ 1.1, 2.3, 3.5} the Norm of fV is: 4.57602 ----------------------------------------------------------- there is a ivec3: intV{0, 4, 3} the unitization of intV is: [ 0, 0.8, 0.6 ] ----------------------------------------------------------- there is a ivec3: intV{1,-2,3}, there is a fvec3: fV{1.1f,2.3f,3.8f}, and the result of ivec3·fvec3 as follows: the dotval is: 7.9 crossVec is: [ -14.5, -0.5, 4.5 ]test result
#define LOWPRECISION //開啟低精度 #define VEC3_OUT //開啟vec3輸出 #include <lvgm\lvgm.h> #define stds std:: #define enter stds endl << stds endl using lvgm::ivec2; using lvgm::ivec3; using lvgm::fvec3; using lvgm::fvec2; int main() { ivec3 intV{ 1,-2,3 }, intV2{ 1, }, null; //null.self_unitization(); ivec3 b; ivec2 _2ivec{ 1,2 }; fvec3 fV{ 1.f,2.1f, }; stds cout << "△--****************** CONSTRUCTOR TEST ******************" << enter; stds cout << " ******* ivec3 test *********" << stds endl; stds cout << "there are two ivec3s as intV{ 1,-2,3 } and intV2{ 1, }, the value of which as follows" << enter; stds cout << intV << enter; stds cout << intV2 << enter; stds cout << "there is a ivec2 : _2ivec{1,2}, and a integer 7 to construct a ivec3 as follows" << enter; ivec3 _2to3{ _2ivec, 7 }; stds cout << "the vec2 in front of the integer of 7: " << _2to3 << enter; _2to3 = ivec3{ 7, _2ivec }; stds cout << "the number of 7 in front of vec2: " << _2to3 << enter << enter; stds cout << " ******* fvec3 test **********" << enter; stds cout << "there is a fvec3 as fV{ 1.f,2.1f, }, the value of which as follows" << enter; stds cout << fV << enter; stds cout << "there is a fvec2 : t{1.2f,}, and a value 3 to construct a ivec3 as follows" << enter; fvec2 t{ 1.2f }; fvec3 f2to3{ t,3 }; stds cout << "f2to3 : " << f2to3 << enter; stds cout << "△--******************* FUNCTIONS TEST ********************" << enter; stds cout << "there is a ivec3{1, -2, 3}" << stds endl; stds cout << "the operator + or - of ivec3 as follows:" << enter; intV = +intV; stds cout << "+ : " << intV << stds endl; intV = -intV; stds cout << "- :" << intV << enter; intV = -intV; stds cout << "-----------------------------------------------------------" << enter; stds cout << "there is a ivec3{1, -2, 3}" << enter; auto re = ++intV; stds cout << "++ivec3: the val of expression:" << re << "\tthe val of ivec3:" << intV << enter; --intV; re = intV++; stds cout << "ivec3++: the val of expression:" << re << "\tthe val of ivec3:" << intV << enter; stds cout << "the operator of -- is the same as above" << enter; stds cout << "-----------------------------------------------------------" << enter; stds cout << "the operator[] of ivec3 as follows:" << enter; stds cout << "the intV[2] is " << intV[2] << stds endl; //stds cout << "the intV[4] is " << intV[4] << stds endl; stds cout << "-----------------------------------------------------------" << enter; stds cout << "there are two ivec3s as intV{ 1,-2,3 } and intV2{ 1, }, the value of which as follows" << enter; if (intV != intV2)stds cout << "intV is not equ to intV2" << enter; stds cout << "the operator = such that: intV2 = intV; the result as follows:" << stds endl; intV2 = intV; stds cout << "intV2 is " << intV2 << stds endl; if (intV2 == intV)stds cout << "intV is equ to intV2" << enter; stds cout << stds endl << "there are two ivec3s as intV{ 1,-2,3 } and intV2{ 1, }, the value of which as follows" << enter; stds cout << "the operator += such that: intV += intV2, the result of which as follows:" << enter; intV2 = { 1, }; intV += intV2; stds cout << "intV is: " << intV << enter; stds cout << "the operator -= such that: intV -= intV2, the result of which as follows:" << enter; intV -= intV2; stds cout << "intV is: " << intV << enter; stds cout << "the value of intV is to become the original value" << enter; stds cout << stds endl << "there are two ivec3s as intV{ 1,-2,3 } and intV2{ 2,1,3 }, the value of which as follows" << enter; stds cout << "the operator *= such that: intV *= intV2, the result of which as follows:" << enter; intV2 = { 2,1,3 }; intV *= intV2; stds cout << "intV is: " << intV << enter; intV /= intV2; stds cout << "the operator /= such that: intV /= intV2, the result of which as follows:" << enter; stds cout << "intV is: " << intV << enter; stds cout << "the value of intV is to become the original value" << enter; stds cout << "-----------------------------------------------------------" << enter; stds cout << "the operator *= (number)such that: intV *= 5, the result of which as follows:" << enter; intV *= 5; stds cout << "intV is: " << intV << enter; stds cout << "the operator /= (number) such that: intV /= 5, the result of which as follows:" << enter; intV /= 5; stds cout << "intV is: " << intV << enter; stds cout << "the value of intV is to become the original value" << enter; stds cout << "the operator + 、 -、 * 、/ (ivec3 or number) is the same as above" << enter; stds cout << "-----------------------------------------------------------" << enter; stds cout << "the operator* between ivec3 and fvec3 as follows:" << enter; stds cout << "there is a ivec3: intV{1,-2,3}, there is a fvec3: fV{1.1f,2.3f,3.8f}, and the result of ivec3*fvec3 as follows:" << enter; intV = { 1,-2,3 }; fV = { 1.1f,2.3f,3.8f }; auto res = intV*fV; stds cout << "res is: " << res << enter; stds cout << "the result of * is up to the higher precision of both" << enter; stds cout << "the operator* between ivec3 and float as follows:" << enter; stds cout << "there is a ivec3: intV{1,-2,3}, there is a float: 3.14, and the result of ivec3*3.14 as follows:" << enter; intV = { 1,-2,3 }; auto res2 = intV*3.14; stds cout << "res2 is: " << res2 << enter; stds cout << "the type of ivec3 * float is not fvec3 but ivec3, and the factor is just a factor that shouldn't change the vec's precision" << stds endl << "if you need the result's type to become fvec3,you should use static_cast<fvec3>(intV) * float" << enter; intV = { 1,-2,3 }; auto res3 = (static_cast<fvec3>(intV))*3.14; stds cout << "res3 is: " << res3 << enter; stds cout << "the operator/ between different type is the same as above" << enter; stds cout << "-----------------------------------------------------------" << enter; stds cout << "the normal() test as follows: " << enter; stds cout << "there is a ivec3: intV{1,-2,3}" << enter; stds cout << "the Norm of intV is: " << intV.normal() << enter; stds cout << "there is a fvec3: fV{ 1.1, 2.3, 3.5}" << enter; stds cout << "the Norm of fV is: " << fV.normal() << enter; stds cout << "-----------------------------------------------------------" << enter; stds cout << "there is a ivec3: intV{0, 4, 3}" << enter; intV = { 0,4,3 }; lvgm::vec3<lvgm::precision> normal = intV.ret_unitization(); stds cout << "the unitization of intV is: " << normal << enter; stds cout << "-----------------------------------------------------------" << enter; stds cout << "there is a ivec3: intV{1,-2,3}, there is a fvec3: fV{1.1f,2.3f,3.8f}, and the result of ivec3·fvec3 as follows:" << enter; intV = { 1,-2,3 }; fV = { 1.1f,2.3f,3.8f }; lvgm::precision dotval = lvgm::dot(intV, fV); stds cout << "the dotval is: " << dotval << enter; auto crossVec = cross(intV, fV); stds cout << "crossVec is: " << crossVec << enter; }test code
庫檔案程式碼
/// lvgm.h // ----------------------------------------------------- // [author] lv // [ time ] 2018.12 ~ 2018.12 // [brief ] include all of the mymath's head files // ----------------------------------------------------- #ifndef LVGM_H #define LVGM_H #include <lvgm\type_vec\type_vec.h> #endif //LVGM_Hlvgm.h
/// precision.h // ----------------------------------------------------- // [author] lv // [ time ] 2018.12 ~ 2018.12 // [brief ] control the precision of data // ----------------------------------------------------- #ifndef LV_PRECISION_H #define LV_PRECISION_H namespace lvgm { # ifdef HIGHPRECISION //set the high precision using precision = long double; # elif(defined LOWPRECISION) //set the low preciion using precision = float; # else using precision = double; //default precision # endif //set precision } //namespace lvgm #endif //LV_PRECISION_Hprecision.h
/// myVec2.h // ----------------------------------------------------- // [author] lv // [ time ] 2018.12 ~ 2018.12 // [brief ] the definition of two-dimensional vector // ----------------------------------------------------- #ifndef LV_VEC2_H #define LV_VEC2_H namespace lvgm { template<typename T> class vec2 { public: using value_type = T; using norm_t = precision; public: template<typename E> vec2(const vec2<E>& vec); //static_cast vec2(const T x = T(), const T y = T())noexcept; vec2(const vec2&); ~vec2() { } public: //inline get function inline T& x() { return _x; } inline T& y() { return _y; } inline T& u() { return _x; } inline T& v() { return _y; } inline T& r() { return _x; } inline T& g() { return _y; } inline T& s() { return _x; } inline T& t() { return _y; } inline vec2 xy() { return vec2<T>{_x, _y}; } inline vec2 yx() { return vec2<T>{_y, _x}; } inline vec2 rg() { return vec2<T>{_x, _y}; } inline vec2 gr() { return vec2<T>{_y, _x}; } inline vec2 uv() { return vec2<T>{_x, _y}; } inline vec2 vu() { return vec2<T>{_y, _x}; } inline vec2 st() { return vec2<T>{_x, _y}; } inline vec2 ts() { return vec2<T>{_y, _x}; } inline const T& x()const { return _x; } inline const T& y()const { return _y; } inline const T& u()const { return _x; } inline const T& v()const { return _y; } inline const T& r()const { return _x; } inline const T& g()const { return _y; } inline const T& s()const { return _x; } inline const T& t()const { return _y; } //inline operator function inline const vec2& operator+()const; inline vec2 operator-()const; inline vec2& operator++(); inline vec2& operator--(); inline const vec2 operator++(int); inline const vec2 operator--(int); inline const T& operator[](const int index)const; inline T& operator[](const int index); inline vec2& operator=(const vec2& rhs); inline vec2& operator+=(const vec2& rhs); inline vec2& operator-=(const vec2& rhs); inline vec2& operator*=(const vec2& rhs); inline vec2& operator/=(const vec2& rhs); inline vec2& operator*=(const T factor); inline vec2& operator/=(const T factor); public: //return the Norm of vec2 inline norm_t normal()const; inline norm_t squar()const; //let self become to the unit vector of vec_type inline void self_unitization(); //return a non-integer three-dimensional unit vector [the type is norm_t] inline vec2<precision> ret_unitization()const; inline bool isnull()const; private: T _x, _y; }; //constructor functions template<typename T> vec2<T>::vec2(const T x,const T y)noexcept :_x(x) ,_y(y) { } template<typename T> template<typename E> vec2<T>::vec2(const vec2<E>& rhs) :_x(static_cast<T>(rhs.x())) ,_y(static_cast<T>(rhs.y())) { } template<typename T> vec2<T>::vec2(const vec2<T>& rhs) :_x(rhs._x) ,_y(rhs._y) { } // Binary operator functions [non-mem] template<typename T> inline vec2<T> operator+(const vec2<T>& v1, const vec2<T>& v2) { return vec2<T>(v1[0] + v2[1], v1[1] + v2[1]); } template<typename T> inline vec2<T> operator-(const vec2<T>& v1, const vec2<T>& v2) { //the operator of - ,example (5,4) - (2,2) -> (3,2) return v1 + (-v2); } template<typename A, typename B> inline auto operator*(const vec2<A>& v1, const vec2<B>& v2) { //the operator of * ,example (1.1, 2.1) * (2, 3) -> (2.2, 6.3) using type = decltype(v1[0] * v2[0]); return vec2<type>((type)v1[0] * v2[0], (type)v1[1] * v2[1]); } template<typename T> inline vec2<T> operator*(const vec2<T>& v1, const vec2<T>& v2) { //the operator of * ,example (1,2) * (2,3) -> (2,6) return vec2<T>(v1[0] * v2[0], v1[1] * v2[1]); } template<typename T, typename E> inline vec2<T> operator*(const vec2<T>& v, const E factor) { return vec2<T>(v.x() * factor, v.y() * factor); } template<typename T, typename E> inline vec2<T> operator*(const E factor, const vec2<T>& v) { return vec2<T>(v.x() * factor, v.y() * factor); } template<typename A, typename B> inline auto operator/(const vec2<A>& v1, const vec2<B>& v2) { //the operator of / ,example (1.1, 2.1) * (2, 3) -> (0.55, 0.7) assert(v2.isnull()); using type = decltype(v1[0] / v2[0]); return vec2<type>((type)v1[0] / v2[0], (type)v1[1] / v2[1]); } template<typename T> inline vec2<T> operator/(const vec2<T>& v1, const vec2<T>& v2) { //the operator of / ,example 3 * 5 -> 15 , (1,2) * (2,3) -> (1/2,2/3) assert(v2.isnull()); return operator/<T, T> (v1, v2); } template<typename T, typename E> inline vec2<T> operator/(const vec2<T>& v, const E factor) { assert(factor != 0 && factor != 0.); return vec2<T>(v.x() / factor, v.y() / factor); } template<typename T> inline bool operator==(const vec2<T>& v1, const vec2<T>& v2) { return v1.x() == v2.x() && v1.y() == v2.y(); } template<typename T> inline bool operator!=(const vec2<T>& v1, const vec2<T>& v2) { return !(v1 == v2); } // Unary operator functions [mem] template<typename T> inline const vec2<T>& vec2<T>::operator+() const { //the operator of + ,example 5 -> +5, +(1,-2) -> (1,-2) return *this; } template<typename T> inline vec2<T> vec2<T>::operator-() const { //the operator of - ,example 5 -> -5, -(1,-2) -> (-1,2) return vec2<T>(-_x, -_y); } template<typename T> inline vec2<T>& vec2<T>::operator++() { ++this->_x; ++this->_y; return *this; } template<typename T> inline const vec2<T> vec2<T>::operator++(int) { vec2<T>ori(*this); ++*this; return ori; } template<typename T> inline vec2<T>& vec2<T>::operator--() { --this->_x; --this->_y; return *this; } template<typename T> inline const vec2<T> vec2<T>::operator--(int) { vec2<T>ori(*this); --*this; return ori; } template<typename T> inline const T& vec2<T>::operator[](const int index)const { if (index == 0)return _x; else if (index == 1)return _y; else throw "the index is error"; } template<typename T> inline T& vec2<T>::operator[](const int index) { if (index == 0)return _x; else if (index == 1)return _y; else throw "the index is error"; } // member functions template<typename T> inline vec2<T>& vec2<T>::operator=(const vec2<T>& rhs) { if (this != &rhs) { _x = rhs._x; _y = rhs._y; } return *this; } template<typename T> inline vec2<T>& vec2<T>::operator+=(const vec2& rhs) { this->_x += rhs._x; this->_y += rhs._y; return *this; } template<typename T> inline vec2<T>& vec2<T>::operator-=(const vec2& rhs) { return *this += (-rhs); } template<typename T> inline vec2<T>& vec2<T>::operator/=(const vec2<T>& rhs) { assert(!rhs.isnull()); this->_x /= rhs._x; this->_y /= rhs._y; return *this; } template<typename T> inline vec2<T>& vec2<T>::operator*=(const vec2<T>& rhs) { this->_x *= rhs._x; this->_y *= rhs._y; return *this; } template<typename T> inline vec2<T>& vec2<T>::operator*=(const T factor) { this->_x *= factor; this->_y *= factor; return *this; } template<typename T> inline vec2<T>& vec2<T>::operator/=(const T factor) { assert(factor != 0); this->_x /= factor; this->_y /= factor; return *this; } template<typename T> inline typename vec2<T>::norm_t vec2<T>::normal()const { return sqrt(squar()); } template<typename T> inline typename vec2<T>::norm_t vec2<T>::squar()const { return _x*_x + _y*_y; } template<typename T> inline void vec2<T>::self_unitization() { *this /= normal(); } template<typename T> inline vec2<precision> vec2<T>::ret_unitization()const { norm_t div = normal(); return vec2<norm_t>{ (norm_t)this->_x / div, (norm_t)this->_y / div, (norm_t)this->_z / div }; } template<typename T, typename E> inline auto dot(const vec2<T>& v1, const vec2<E>& v2) //-> decltype(v1.x() * v2.x() + v1.y() * v2.y() + v1.z() * v2.z()) {// x1 * x2 + y1 * y2 return v1.x() * v2.x() + v1.y() * v2.y() + v1.z() * v2.z(); } template<typename T, typename E> inline auto cross(const vec2<T> v1, const vec2<E>& v2) {// v1 × v2 return vec2<decltype(v1[1] * v2[2] - v1[2] * v2[1])> ( v1[0] * v2[1] - v1[1] * v2[0] ); } template<typename T> inline bool vec2<T>::isnull()const { return *this == vec2<T>(); } } //namespace lvgm #endif //LV_VEC2_Hlv_vec2.h
/// myVec3.h // ----------------------------------------------------- // [author] lv // [ time ] 2018.12 ~ 2018.12 // [brief ] the definition of Three-dimensional vector // ----------------------------------------------------- #ifndef LV_VEC3_H #define LV_VEC3_H namespace lvgm { template<typename T> class vec3 { public: using value_type = T; using norm_t = precision; public: template<typename E> vec3(const vec3<E>& vec); //static_cast vec3(const T e1 = T(), const T e2 = T(), const T e3 = T())noexcept; explicit vec3(const vec2<T>& v2, const T e3 = T())noexcept; explicit vec3(const T e1, const vec2<T>& v)noexcept; explicit vec3(const vec3&); ~vec3() { } public: inline T& x() { return _x; } inline T& y() { return _y; } inline T& z() { return _z; } inline T& r() { return _x; } inline T& g() { return _y; } inline T& b() { return _z; } inline vec2<T> xy() { return vec2<T>{_x, _y}; } inline vec2<T> yx() { return vec2<T>{_y, _x}; } inline vec2<T> xz() { return vec2<T>{_x, _z}; } inline vec2<T> zx() { return vec2<T>{_z, _x}; } inline vec2<T> yz() { return vec2<T>{_y, _z}; } inline vec2<T> zy() { return vec2<T>{_z, _y}; } inline vec2<T> rg() { return vec2<T>{_x, _y}; } inline vec2<T> gr() { return vec2<T>{_y, _x}; } inline vec2<T> rb() { return vec2<T>{_x, _z}; } inline vec2<T> br() { return vec2<T>{_z, _x}; } inline vec2<T> gb() { return vec2<T>{_y, _z}; } inline vec2<T> bg() { return vec2<T>{_z, _y}; } inline vec3 rgb() { return vec3{_x, _y, _z}; } inline vec3 rbg() { return vec3{_x, _z, _y}; } inline vec3 gbr() { return vec3{_y, _z, _x}; } inline vec3 grb() { return vec3{_y, _x, _z}; } inline vec3 bgr() { return vec3{_z, _y, _x}; } inline vec3 brg() { return vec3{_z, _x, _y}; } inline const T& x()const { return _x; } inline const T& y()const { return _y; } inline const T& z()const { return _z; } inline const T& r()const { return _x; } inline const T& g()const { return _y; } inline const T& b()const { return _z; } //inline oprator function inline const vec3& operator+() const; inline vec3 operator-()const; inline vec3& operator++(); inline vec3& operator--(); inline const vec3 operator++(int); inline const vec3 operator--(int); inline const T& operator[](const int index)const; inline T& operator[](const int index); inline vec3& operator=(const vec3& rhs); inline vec3& operator+=(const vec3& rhs); inline vec3& operator-=(const vec3& rhs); inline vec3& operator*=(const vec3& rhs); inline vec3& operator/=(const vec3& rhs); inline vec3& operator