在 C++ 中,向量类型通常由库提供。C++ 标准库中的
GLM(OpenGL Mathematics):是一个专门用于 OpenGL 开发的数学库,提供了矩阵、向量、四元数等数学类型,支持常见的数学运算和变换。
Eigen:是一个用于线性代数计算的 C++ 模板库,提供了矩阵、向量、四元数等数学类型,支持高效的数学计算和线性代数运算。
Boost.Geometry:是 Boost 库中的一个几何库,提供了向量、点、线段、射线等几何类型,支持各种几何运算和算法。
这些库都需要在代码中引用相应的头文件和库文件,具体的引用方式和编译方式可能有所不同,需要根据具体的库文档进行设置和配置。
Eigen官方网站:
http://eigen.tuxfamily.org/index.php?title=Main_Page
在VS2017中使用Eigen的两种方式:
https://blog.csdn.net/weixin_45590473/article/details/122857933
https://blog.csdn.net/weixin_42655134/article/details/107414089
通过以下案例,体会Eigen库在C++中的应用
1、在C++中,使用Eigen模板库,用空间中的三个点建立一个新坐标系,通过转移矩阵计算这三个点在新坐标系中的坐标?
思路如下:
a、通过这三个点计算新坐标系的三个轴向量
b、新坐标系的三个轴向量构成的矩阵求逆可以得到转移矩阵
c、用转移矩阵计算新坐标系下点的坐标。
1.1、为什么新坐标系下三个轴向量求逆就是转移矩阵?
在三维空间中,一个坐标系可以由三个不共线的向量构成,分别代表该坐标系的三个轴。对于一个从旧坐标系变换到新坐标系的变换矩阵,我们可以将其表示为一个3x3的矩阵,其中每一列代表新坐标系的一个轴向量在旧坐标系下的坐标表示。
假设我们已经确定了新坐标系的三个轴向量x_axis_new、y_axis_new和z_axis_new,那么我们就可以将它们构成一个3x3的矩阵,记作rotation,即:
Eigen::Matrix3d rotation;
rotation << x_axis_new, y_axis_new, z_axis_new;
由于rotation的每一列都代表新坐标系的一个轴向量在旧坐标系下的坐标表示,因此rotation的逆矩阵就代表了旧坐标系的三个轴向量在新坐标系下的坐标表示。这样,我们就可以将一个向量从旧坐标系变换到新坐标系中,方法是将这个向量表示为一个列向量,然后乘以rotation的逆矩阵,即:
Eigen::Vector3d v_old(x_old, y_old, z_old);
Eigen::Vector3d v_new = rotation.inverse() * v_old;
因此,我们可以使用rotation的逆矩阵作为转移矩阵,将向量从旧坐标系变换到新坐标系中。
1.2、求原坐标系中的点在新坐标系下的坐标,应该用哪种方式?
(1)、求原坐标系中的点在新坐标系下的坐标,可以使用以下两种方式实现:
使用solve()函数:这种方式将新坐标系的三个轴向量作为矩阵的列向量,将原始坐标系中的点作为向量b,解决线性方程组Ax=b,其中x就是点在新坐标系中的坐标。具体地,代码如下:
Eigen::Matrix3d transMat;
transMat.col(0) = axis1_new;
transMat.col(1) = axis2_new;
transMat.col(2) = axis3_new;
Eigen::Vector3d p_old; // 原始坐标系中的点
Eigen::Vector3d origin; // 新坐标系的原点
Eigen::Vector3d p_new = transMat.solve(p_old - origin);
(2)、使用矩阵乘法:这种方式使用一个4x4的变换矩阵T来表示坐标变换,其中前三行表示新坐标系的三个轴向量,最后一行表示新坐标系的原点。将原始坐标系中的点作为一个4维齐次坐标表示,通过矩阵乘法计算得到点在新坐标系中的4维齐次坐标,然后将齐次坐标除以第四个分量得到点在新坐标系中的三维坐标。具体地,代码如下:
Eigen::Matrix4d transform;
transform << axis1_new, 0,
axis2_new, 0,
axis3_new, 0,
origin_new, 1;
Eigen::Vector4d p_old_homog(p_old(0), p_old(1), p_old(2), 1);
Eigen::Vector4d p_new_homog = transform * p_old_homog;
Eigen::Vector3d p_new = p_new_homog.head<3>() / p_new_homog(3);
这两种方式本质上是等价的,但是实现方式略有不同。使用solve()函数更加直观,但是需要显式地解决线性方程组,可能更加耗时。使用矩阵乘法的方式可能更加高效,但需要注意实现中的齐次坐标转换。具体实现方式应根据具体情况选择。
方式(2)中的思路和思路来源参考2中的代码类似
1.3
Eigen::Vector3d newP2 = transMat.solve(p2 - origin);
solve函数的使用和下面这句代码有什么不同吗
Eigen::Vector3d p2_new = transform * (p2_old - origin_new);
在Eigen中,solve()函数和使用矩阵乘法都可以用于计算一个向量相对于某个新坐标系的坐标。但是,它们的实现方式有所不同。
solve()函数用于解决线性方程组Ax=b,其中A是一个矩阵,b是一个向量,x是一个未知向量,用于计算向量x的值。在Eigen中,solve()函数的调用格式为x = A.solve(b)。
在这个问题中,我们可以将转移矩阵transMat看作是一个线性变换,它将原始坐标系中的向量变换为新坐标系中的向量。那么,如果我们想要计算向量p2相对于新坐标系的坐标,我们可以将其表示为线性方程组Ax=b,其中A是转移矩阵,b是原始向量p2,x是新坐标系中的向量p2_new。因此,我们可以使用solve()函数来计算p2_new的值,如下所示:
Eigen::Vector3d p2_new = transMat.solve(p2 - origin);
另一方面,矩阵乘法可以将一个向量映射到另一个向量,这是另一种实现向量变换的方法。在Eigen中,我们可以使用矩阵乘法来计算一个向量相对于某个新坐标系的坐标。具体地,我们可以使用一个矩阵transform来表示坐标变换,使用向量p2_old来表示原始坐标系中的向量,使用向量origin_new来表示新坐标系的原点。那么,我们可以使用以下代码来计算p2_new的值:
Eigen::Vector3d p2_new = transform * (p2_old - origin_new);
总体上说,solve()函数和矩阵乘法都可以用于计算向量相对于某个新坐标系的坐标,但它们的实现方式有所不同。solve()函数用于解决线性方程组,而矩阵乘法则将向量映射到新坐标系中。在实践中,使用哪种方法取决于具体的应用场景和需求。
1.4、思路来源参考
https://stackoverflow.com/questions/49769459/convert-points-on-a-3d-plane-to-2d-coordinates/49771112#49771112
https://stackoverflow.com/questions/68506084/i-have-a-plane-between-3-points-and-want-to-get-a-transformation-matrix-which-al
https://stackoverflow.com/questions/52154004/problem-superimposing-and-aligning-3d-triangles/52163563#52163563
https://math.stackexchange.com/questions/1983054/using-3-points-create-new-coordinate-system-and-create-array-of-points-on-xy
1.5、右手定则确定空间坐标系
http://www.gaosan.com/gaokao/262408.html
2、代码实践
#include
#include
using namespace Eigen;
using namespace std;
using Eigen::Matrix3d;
// 三个点的坐标信息
//Vector3d p1(1.0, 2.0, 3.0);
//Vector3d p2(4.0, 5.0, 6.0);
//Vector3d p3(7.0, 8.0, 9.0);
Vector3d p1(1.0, 0.0, 0.0);
Vector3d p2(0.0, 1.0, 0.0);
Vector3d p3(0.0, 0.0, 1.0);
Vector3d p4(0.5, 0.5, 0.0);
int main()
{
//求新坐标的三个轴向量
Eigen::Matrix3d transMat;
transMat.col(0) = (p2 - p1).normalized();
std::cout << transMat.col(0) << std::endl;
transMat.col(2) = transMat.col(0).cross((p3 - p1)).normalized();
std::cout << transMat.col(2) << std::endl;
transMat.col(1) = transMat.col(2).cross(transMat.col(0));
std::cout << transMat.col(1) << std::endl;
//求转移矩阵,新坐标的三个轴向量求逆就是转移矩阵
Eigen::Matrix3d transform1 = transMat.inverse();
//求点在新坐标系下的坐标
Eigen::Vector3d origin = p1;
Eigen::Vector3d newP1 = Eigen::Vector3d::Zero();
Eigen::Vector3d newP2 = transform1 * (p2 - origin);
Eigen::Vector3d newP3 = transform1 * (p3 - origin);
Eigen::Vector3d newP4 = transform1 * (p4 - origin);
std::cout << newP1 << std::endl;
std::cout << newP2 << std::endl;
std::cout << newP3 << std::endl;
std::cout << newP4 << std::endl;
}
3、经验证,该代码可以求出原坐标系中的点在新坐标系下的坐标。