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
设计问题:一开始我们可能想到在模板类里面利用宏控制数据存储精度,但你可能会遇到问题。例如:
# 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
测试效果:
test result
test code
库文件代码
lvgm.h
precision.h
lv_vec2.h
lv_vec3.h
type_vec.h
vec_inout.h
如有什么问题,请于评论区留言或者邮箱(^_^)
感谢您的阅读,生活愉快`
作者:林-兮
出处:http://www.cnblogs.com/lv-anchoret/