• <泛> 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 <lvgmlvgm.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 << "	the val of ivec3:" << intV << enter;
        --intV;
        re = intV++;
        stds cout << "ivec3++:    the val of expression:" << re << "	the 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	ype_vec	ype_vec.h>
    
    #endif  //LVGM_H
    lvgm.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_H
    precision.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[0], 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()
                {// x1 * x2 + y1 * y2
                return v1.x() * v2.x() + v1.y() * v2.y();
                }
    
        template<typename T, typename E>
            inline auto cross(const vec2<T> v1, const vec2<E>& v2)
                {// v1 × v2
                return    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_H
    lv_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*=(const T factor);
    
            inline vec3& operator/=(const T factor);
            
        public:
            //return the Norm of vec3
            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 vec3<precision> ret_unitization()const;
    
            inline bool isnull()const;
    
        private:
            T _x, _y, _z;
    
            };
    
    //constructor functions
    
        template<typename T>
            template<typename E>
            vec3<T>::vec3(const vec3<E>& vec)
                :_x(static_cast<T>(vec.x()))
                ,_y(static_cast<T>(vec.y()))
                ,_z(static_cast<T>(vec.z()))
            {    }
        
        template<typename T>
            vec3<T>::vec3(const T e1, const T e2, const T e3)noexcept
                :_x{e1}
                ,_y{e2}
                ,_z{e3}
            {    }
    
        template<typename T>
            vec3<T>::vec3(const vec2<T>& v, const T e3)noexcept
                :_x(v.x())
                ,_y(v.y())
                ,_z(e3)
            {    }
    
        template<typename T>
            vec3<T>::vec3(const T e, const vec2<T>& v)noexcept
                :_x(e)
                ,_y(v.x())
                ,_z(v.y())
            {    }
    
        template<typename T>
            vec3<T>::vec3(const vec3<T>& rhs)
                :_x{rhs._x}
                ,_y{rhs._y}
                ,_z{rhs._z}
            {    }
    
    // Binary operator functions [non-mem]
        template<typename T>
            vec3<T> operator+(const vec3<T>& v1, const vec3<T>& v2)
                {
                //the operator of + ,example  (5,4,3) + (2,-2,1) -> (7,2,4)  
                return vec3<T>(v1[0] + v2[0], v1[1] + v2[1], v1[2] + v2[2]);
                }
    
        template<typename T>
            inline vec3<T> operator-(const vec3<T>& v1, const vec3<T>& v2)
                {
                //the operator of - ,example  (5,4,3) - (2,2,1) -> (3,2,2)  
                return v1 + (-v2);
                }
    
        template<typename A, typename B>
            inline auto operator*(const vec3<A>& v1, const vec3<B>& v2)
                {
                //the operator of * ,example  (1.1, 2.1, 3.1) * (2, 3, 4) -> (2.2, 6.3, 12.4)
                using type = decltype(v1[0] * v2[0]);
                return vec3<type>((type)v1[0] * v2[0], (type)v1[1] * v2[1], (type)v1[2] * v2[2]);
                }
    
        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) -> (2,6,12)
                return vec3<T>(v1[0] * v2[0], v1[1] * v2[1], v1[2] * v2[2]);
                }
    
        template<typename T, typename E>
            inline vec3<T> operator*(const vec3<T>& v, const E factor)
                {
                return vec3<T>(v.x() * factor, v.y() * factor, v.z() * factor);
                }
    
        template<typename T, typename E>
            inline vec3<T> operator*(const E factor, const vec3<T>& v)
                {
                return vec3<T>(v.x() * factor, v.y() * factor, v.z() * factor);
                }
    
        template<typename A, typename B>
            inline auto operator/(const vec3<A>& v1, const vec3<B>& v2)
                {
                //the operator of / ,example  (1.1, 2.1, 3.2) * (2, 3, 4) -> (0.55, 0.7, 0.8)
                assert(v2.isnull());
                using type = decltype(v1[0] / v2[0]);
                return vec3<type>((type)v1[0] / v2[0], (type)v1[1] / v2[1], (type)v1[2] / v2[2]);
                }
            
        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);
                }
    
        template<typename T, typename E>
            inline vec3<T> operator/(const vec3<T>& v, const E factor)
                {
                assert(factor != 0 && factor != 0.);
                return vec3<T>(v.x() / factor, v.y() / factor, v.z() / factor);
                }
    
        template<typename T>
            inline bool operator==(const vec3<T>& v1, const vec3<T>& v2)
                {
                return v1.x() == v2.x() && v1.y() == v2.y() && v1.z() == v2.z();
                }
    
        template<typename T>
            inline bool operator!=(const vec3<T>& v1, vec3<T>& v2)
                {
                return !(v1 == v2);
                }
    
    // Unary operator functions [mem]
        template<typename T>
            inline const vec3<T>& vec3<T>::operator+() const 
                {
                //the operator of + ,example 5 -> +5,  +(1,-2,3) -> (1,-2,3)
                return *this; 
                }
    
        template<typename T>
            inline vec3<T> vec3<T>::operator-() const
                {
                //the operator of - ,example 5 -> -5,  -(1,-2,3) -> (-1,2,-3)
                return vec3<T>(-_x, -_y, -_z);
                }
    
        template<typename T>
            inline vec3<T>& vec3<T>::operator++()
                {
                ++this->_x;
                ++this->_y;
                ++this->_z;
                return *this;
                }
    
        template<typename T>
            inline const vec3<T> vec3<T>::operator++(int)
                {
                vec3<T>ori(*this);
                ++*this;
                return ori;
                }
    
        template<typename T>
            inline vec3<T>& vec3<T>::operator--()
                {
                --this->_x;
                --this->_y;
                --this->_z;
                return *this;
                }
    
        template<typename T>
            inline const vec3<T> vec3<T>::operator--(int)
                {
                vec3<T>ori(*this);
                --*this;
                return ori;
                }
    
        template<typename T>
            inline const T& vec3<T>::operator[](const int index)const
                {
                if (index == 0)return _x;
                else if (index == 1)return _y;
                else if (index == 2)return _z;
                else throw "the index is error";
                }
    
        template<typename T>
            inline T& vec3<T>::operator[](const int index)
                {
                if (index == 0)return _x;
                else if (index == 1)return _y;
                else if (index == 2)return _z;
                else throw "the index is error";
                }
    
    // member functions
        template<typename T>
            inline vec3<T>& vec3<T>::operator=(const vec3<T>& rhs)
                {
                if (this != &rhs)
                    {
                    _x = rhs._x;
                    _y = rhs._y;
                    _z = rhs._z;
                    }
                return *this;
                }
    
        template<typename T>
            inline vec3<T>& vec3<T>::operator+=(const vec3& rhs)
                {
                this->_x += rhs._x;
                this->_y += rhs._y;
                this->_z += rhs._z;
                return *this;
                }
    
        template<typename T>
            inline vec3<T>& vec3<T>::operator-=(const vec3& rhs)
                {
                this->_x -= rhs._x;
                this->_y -= rhs._y;
                this->_z -= rhs._z;
                return *this;
                }
    
        template<typename T>
            inline vec3<T>& vec3<T>::operator/=(const vec3<T>& rhs)
                {
                assert(!rhs.isnull());
                this->_x /= rhs._x;
                this->_y /= rhs._y;
                this->_z /= rhs._z;
                return *this;
                }
    
        template<typename T>
            inline vec3<T>& vec3<T>::operator*=(const vec3<T>& rhs)
                {
                this->_x *= rhs._x;
                this->_y *= rhs._y;
                this->_z *= rhs._z;
                return *this;
                }
    
        template<typename T>
            inline vec3<T>& vec3<T>::operator*=(const T factor)
                {
                this->_x *= factor;
                this->_y *= factor;
                this->_z *= factor;
                return *this;
                }
    
        template<typename T>
            inline vec3<T>& vec3<T>::operator/=(const T factor)
                {
                assert(factor != 0);
                this->_x /= factor;
                this->_y /= factor;
                this->_z /= factor;
                return *this;
                }
    
        template<typename T>
            inline typename vec3<T>::norm_t vec3<T>::normal()const
                {
                return sqrt(squar());
                }
    
        template<typename T>
            inline typename vec3<T>::norm_t vec3<T>::squar()const
                {
                return _x*_x + _y*_y + _z*_z;
                }
    
        template<typename T>
            inline void vec3<T>::self_unitization()
                {
                *this /= normal();
                }
    
        template<typename T>
            inline vec3<precision> vec3<T>::ret_unitization()const
                {
                norm_t div = normal();
                return vec3<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 vec3<T>& v1, const vec3<E>& v2) //-> decltype(v1.x() * v2.x() + v1.y() * v2.y() + v1.z() * v2.z())
                {// x1 * x2 + y1 * y2 + z1 * z2
                return v1.x() * v2.x() + v1.y() * v2.y() + v1.z() * v2.z();
                }
    
        template<typename T, typename E>
            inline auto cross(const vec3<T> v1, const vec3<E>& v2)
                {// v1 × v2
                return vec3<decltype(v1[1] * v2[2] - v1[2] * v2[1])>
                    (
                    v1[1] * v2[2] - v1[2] * v2[1],
                    v1[2] * v2[0] - v1[0] * v2[2],
                    v1[0] * v2[1] - v1[1] * v2[0]
                    );
                }
    
        template<typename T>
            inline bool vec3<T>::isnull()const
                {
                return *this == vec3<T>();
                }
    
    }    //namespace lvgm
    
    #endif    //LV_VEC3_H
    lv_vec3.h
    /// all vectors are in here
    
    // -----------------------------------------------------
    // [author]        lv
    // [ time ]        2018.12 ~ 2018.12
    // [brief ]        all vectors are in here
    // -----------------------------------------------------
    
    
    #pragma once
    
    #include <iostream>
    #include <cmath>
    #include <cassert>
    #include <lvgmlv_precision.h>
    
    #include "lv_vec2.h"
    #include "lv_vec3.h"
    #include "vec_inout.h"
    
    namespace lvgm
    {
        template<typename T> class vec2;
    
        template<typename T> class vec3;
    
        template<typename T> class vec4;
    
    
        typedef vec2<bool> bvec2;
    
        typedef vec2<char> cvec2;
    
        typedef vec2<short> svec2;
    
        typedef vec2<int> ivec2;
    
        typedef vec2<float> fvec2;
    
        typedef vec2<double> dvec2;
    
        typedef vec2<long double> ldvec2;
    
    
        typedef vec3<bool> bvec3;
    
        typedef vec3<char> cvec3;
    
        typedef vec3<short> svec3;
    
        typedef vec3<int> ivec3;
    
        typedef vec3<float> fvec3;
    
        typedef vec3<double> dvec3;
    
        typedef vec3<long double> ldvec3;
    
    
        typedef vec4<bool> bvec4;
    
        typedef vec4<char> cvec4;
    
        typedef vec4<short> svec4;
    
        typedef vec4<int> ivec4;
    
        typedef vec4<float> fvec4;
    
        typedef vec4<double> dvec4;
    
        typedef vec4<long double> ldvec4;
    }
    type_vec.h
    ///vec_inout.h
    
    // -----------------------------------------------------
    // [author]        lv
    // [ time ]        2018.12 ~ 2018.12
    // [brief ]        control the iostream of vec
    // -----------------------------------------------------
    
    
    #pragma once
    
    
    # ifdef    VEC_OUT
    
        template<typename T>
            std::ostream& operator<<(std::ostream& cout, const lvgm::vec2<T>& v)
                {
                cout << "[ " << v.x() << ", " << v.y() << " ]";
                return cout;
                }
    
        template<typename T>
            std::ostream& operator<<(std::ostream& cout, const lvgm::vec3<T>& v)
                {
                cout << "[ " << v.x() << ", " << v.y() << ", " << v.z() << " ]";
                return cout;
                }
    
        template<typename T>
            std::ostream& operator<<(std::ostream& cout, const lvgm::vec4<T>& v)
            {
                cout << "[ " << v.x() << ", " << v.y() << ", " << v.z() << v.w() << " ]";
                return cout;
            }
    
    #endif
    
    # ifdef VEC2_OUT 
    
        template<typename T>
            std::ostream& operator<<(std::ostream& cout, const lvgm::vec2<T>& v)
                {
                cout << "[ " << v.x() << ", " << v.y() << " ]";
                return cout;
                }
    
    #endif
    
    # ifdef VEC3_OUT 
    
        template<typename T>
            std::ostream& operator<<(std::ostream& cout, const lvgm::vec3<T>& v)
                {
                cout << "[ " << v.x() << ", " << v.y() << ", " << v.z() << " ]";
                return cout;
                }
    
    #endif
    
    # ifdef VEC4_OUT 
    
        template<typename T>
            std::ostream& operator<<(std::ostream& cout, const lvgm::vec4<T>& v)
                {
                cout << "[ " << v.x() << ", " << v.y() << ", " << v.z() << v.w() << " ]";
                return cout;
                }
    
    #endif
    vec_inout.h

    如有什么问题,请于评论区留言或者邮箱(^_^)

    感谢您的阅读,生活愉快`

  • 相关阅读:
    主键、外键
    SpringBoot定时任务Scheduled
    启动报DataSource错误
    SpringBoot整合aop
    元数据MetaData(五)
    普通结果集ResultSet和离线结果集RowSet(四)
    Statements、PreparedStatement及CallableStatement(三)
    JDBC数据库连接(二)
    JDBC简介(一)
    【Oracle】常用函数
  • 原文地址:https://www.cnblogs.com/lv-anchoret/p/10163085.html
Copyright © 2020-2023  润新知