C++3D数学库设计详解 向量篇

Preface

我采用的是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

设计问题:一开始我们可能想到在模板类里面利用宏控制数据存储精度,但你可能会遇到问题。例如:

C++3D数学库设计详解 向量篇

# 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

C++3D数学库设计详解 向量篇

假设我现在有一个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数学库的源码有一类函数是这么实现的

C++3D数学库设计详解 向量篇

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

C++3D数学库设计详解 向量篇

其实,意思就是它允许+=另外一种类型的向量,然后我都强转到自身类型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; }

我希望对内部数据的修改的禁止令可以通过参数来实现,比如:

C++3D数学库设计详解 向量篇

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

C++3D数学库设计详解 向量篇

所以,我仅仅去重载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

测试效果:

C++3D数学库设计详解 向量篇

test result

C++3D数学库设计详解 向量篇

test code

库文件代码

C++3D数学库设计详解 向量篇

lvgm.h

C++3D数学库设计详解 向量篇

precision.h

C++3D数学库设计详解 向量篇

lv_vec2.h

C++3D数学库设计详解 向量篇

lv_vec3.h

C++3D数学库设计详解 向量篇

type_vec.h

C++3D数学库设计详解 向量篇

vec_inout.h

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

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

作者:林-兮

出处:http://www.cnblogs.com/lv-anchoret/

C++3D数学库设计详解 向量篇

相关推荐