C++3D math library design explains the beauty of vector chapter

Keywords: Windows

Preface

To support Ray tracing I decided to write a 3D generic math library.

I use windows platform and C++Template technology

  

My library file organization directory is as follows

  --lvgm

  ----test

  ------testVec.cpp

  ----type_vec

  ------lv_vec2.h

  ------lv_vec3.h

  ------type_vec.h

  ------vec_inout.h

  ----lv_precision.h

  ----lvgm.h

Ready

In this article, you need to understand the basic syntax of C++Template

You need to understand the operation of vectors

The vector library file explains:

Two dimensional vector template class

3D vector template class

Data accuracy setting

The default output format setting file related to vectors provided by this library

The vector library file does not have a quadruple lv_vec4 at present. I will add and write a separate statement later

The vector library can provide you with the following functions:

Convenient and free retrieval and setting of vector internal data

Positive and negative of vector

Addition, subtraction, multiplication and division of vectors

The increasing and decreasing of vectors

Index of vector

Vector judgment

Vector assignment and compound operation assignment

Norm of vector

Norm square of vector

Self unit of vector

Returns the high precision unit vector of the vector

Inner product and outer product operation of vector (·, ×)

Vector null

Design

As the two-dimensional and three-dimensional designs are similar, three-dimensional design is taken as an example to describe

<1> Type related

In this class, it is publicly defined that the data value type is template parameter T, and the precision type of the norm is the type set by the external macro -- precision, which is double by default

Design problem: at first, we may think of using macros to control data storage precision in template classes, but you may encounter problems. For example:

#    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

Let's say that I have a three-dimensional vector of int now, and I want to return a unit vector of real number precision (norm? T), so we wrote a member function vec3 < norm? T > RET? Unit() const, and we need to create a vec3 in the main function to receive the return value of RET? Unit

Well, we have two hands together. What can you do

 vec3<??> normal = intV.ret_unit();

You may not be able to do it,?? Can it be vec3::norm_t? Obviously not. Vec3 is a template. You can only specialize t in vec3 < T >. Suddenly, I feel that the precision type norm? Is publicly published in the template class, but it can't be used??

Solutions

Considering that other classes may also need precision settings, it's better to separate this part of the setup code, and then rename norm_ltprecision, so the problem is solved

In the template class, you only need to preprocess the precision file in advance to make the following simple definitions:

using norm_t = precision;

And it's much more convenient in the main function

 vec3<precision> normal = intV.ret_unit();

<2> Parameter type

I've seen the source code of glm math library. There is a kind of function implemented in this way

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;
    }

Actually, it means that it allows + = another type of vector, and then I have to switch to my own type T to perform the operation

Solutions

I have a humble opinion. I implemented it as follows. If there is any loophole, please email or comment.

I can "overload" static_cast, or do something to enable vec3 template class to implement implicit automatic type conversion similar to built-in integer

Then, I don't need to set multiple template parameters in the internal static [cast].

OK, let's do this:

  template<typename E>
        vec3(const vec3<E>& vec);        //static_cast

When I define a constructor, I support other types of vec3, which I call whenever I need vec3 value passing.

<3> Easy and free operation of data

Many math libraries seem to be able to directly design v.x v.y and many c-structs. However, as a C + + party, when writing code in C + + language, it is necessary to strictly abide by data hiding, and it is most convenient to do so without losing the principle of language.

1) Many libraries support v.x = 3;

So I define:

  inline T& x()        { return _x; }

But I've overloaded the constant version

 inline const T& x()const    { return _x; }

I hope that the prohibition of internal data modification can be implemented through parameters, such as:

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);
            }

So, I just overload the const version of v.x(), instead of forbidding x() to be modifiable

2) This operation is also supported in GLSL: v.rgb = v.gbr; or v.rg = v1.rg

I saw the glm library, which does not implement the above operation support temporarily

And I haven't studied GLSL yet

Therefore, with its own superficial technology, it can only obtain data tuples, but not modify them:

inline vec2<T> xy() { return vec2<T>{_x, _y}; }

<4> Operator design

According to the general design principle of C++operator, the overload of monocular and (compound) assignment operator is still defined as member function, while the binocular operator is defined as friend or external function. In this library, STL design principle is adopted, which is defined as out of class function in namespace, so as not to destroy the encapsulation of C + + class

++For monocular operators such as,,, please refer to another special explanation Operator overloading Articles

Here, I only state the design details related to vec3

As for addition and subtraction, mathematically speaking, it is illegal to add or subtract a scalar from a vector. Therefore, vector and scalar addition and subtraction are not supported in this library. For adding the same value to each element, please use offset vector.
The multiplication and division rule supports the operation with scalar, because a scalar multiplied by a vector only extends the length of the vector, which is also mathematically legal.

In addition, considering the multiplication and division of two vec3 objects, if this is int and the other is real, I think it's better to improve the precision, so there is a special overload, and C++11's automatic tracking return value type technology is applied

As for% and% =, from the mathematical point of view, real numbers do not support% operation, but only integer. In the process of graphic operation, most of them are real numbers. Although this library is not fully used for graphic calculation, the% legal operation does not account for much in engineering projects. Therefore, if you need to, please carry out% for each element by yourself. In the library design, it will not be caused by the application of a small part The library has become bloated

Vector norm and unitization (Standardization)

A type design point: the value type of norm and the returned normalized vector template parameter are defined by the user set precision type norm.

I've written two about vector unitization. One is self unitization. At this time, it follows its own type, which means that the element in the unit of int is still int.

The other is to return the unit vector, which is the real vector.

I want to state the basic design principles related to this library.

TEST

Test effect:

△--****************** 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 / / enable low precision
#Define vec3? Out / / enable vec3 output

#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

Library file 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_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 <lvgm\lv_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

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

82 original articles published, 158 praised, 20000 visitors+
Private letter follow

Posted by CBI Web on Sun, 08 Mar 2020 21:33:34 -0700