《实时控制软件设计》作业一:Eigen库的使用

gougouzhang 2019-06-21

作业内容及要求这里
代码repo

1. 定义数据类型

题目要求实现对平面图形的一系列图形学操作。平面图形可以表示为其所有顶点的有序连线,对图形的操作可以分解为对其所有顶点依次应用同一操作。
以“顶点序列表示图形”的思路,定义GeoElement类型,用于表示包括点、线、多边形的平面图形元素,使用STL中的动态数组vector类实现。其定义如下:

typedef std::vector<Vector2d> GeoElement;

测试使用GeoElement创建一条直线并输出:

//commit 6372dba /main.cpp
#include <iostream>
#include <vector>
#include <Eigen/Dense>
using namespace Eigen;

int main() {
    typedef std::vector<Vector2d> GeoElement;
    GeoElement line;
    line.push_back(Vector2d(2, 5));
    line.push_back(Vector2d(3, 1));
    std::cout<<"line:"<<std::endl<<"("<<RowVector2d(line[0])<<"), ("<<RowVector2d(line[1])<<")"<<std::endl;
    return 0;
}

输出结果:

line:
(2 5), (3 1)

2.实现move指令

move指令,即图形的平移操作,是最简单的图形操作,也是图形绕任意点旋转的基础。
在这一步,我们创建了GeoUtils类,将各个操作指令作为其静态函数,并将上一步定义的GeoElement类型也作为其成员,增强代码的组织。
move指令的实现如下:

//commit bd6457b /GeoUtils.cpp
GeoUtils::GeoElement GeoUtils::move(Vector2d movement, GeoUtils::GeoElement elmt)
{
    GeoUtils::GeoElement res;
    GeoUtils::GeoElement::iterator t;
    for(t=elmt.begin(); t!=elmt.end(); t++)
    {
        res.push_back(*t+movement);
    }
    return res;
}

实现的方式就是将elmt中的每个点依次与移动向量做加法,得到的即是移动后的图形中对应点的坐标。
测试代码如下:

//commit 9d4515e /main.cpp
#include <iostream>
#include <vector>
#include <Eigen/Dense>
#include "GeoUtils.h"
using namespace Eigen;

int main() {
    GeoUtils::GeoElement triangle;
    triangle.push_back(Vector2d(2, 5));
    triangle.push_back(Vector2d(3, 1));
    triangle.push_back(Vector2d(2, 9));
    Vector2d movement(3, -5);
    std::cout<<"original triangle:"<<std::endl;
    GeoUtils::printElement(triangle);
    std::cout<<"movement vector:"<<std::endl<<movement<<std::endl;
    std::cout<<"moved triangle:"<<std::endl;
    GeoUtils::printElement(GeoUtils::move(movement, triangle));
    return 0;
}

输出结果:

original triangle:
(2 5) (3 1) (2 9) 
movement vector:
 3
-5
moved triangle:
(5 0) ( 6 -4) (5 4)

3.实现rotate指令

要求中提到rotate指令的功能是绕原点逆时针旋转。点绕原点逆时针旋转的变换如下:
$$ \begin{bmatrix} x' \\ y' \end{bmatrix} = \begin{bmatrix} cos\theta & -sin\theta \\ sin\theta & cos\theta \end{bmatrix} \begin{bmatrix} x \\ y \end{bmatrix} $$
其中(x,y)为变换前的坐标,(x',y')为变换后的坐标,θ为旋转的角度。
只需将图形逐点按此式变换即可:

//commit 7c2c3f8 /GeoUtils.cpp
GeoUtils::GeoElement GeoUtils::rotate(double theta, GeoElement elmt)
{
    const double PI=3.14159265;
    double rad=theta*PI/180;
    Matrix2d R;
    R<<cos(rad), -sin(rad), sin(rad), cos(rad);
    GeoElement res;
    GeoElement::iterator t;
    for(t=elmt.begin(); t!=elmt.end(); t++)
    {
        res.push_back(R*(*t));
    }
    return res;
}

测试代码:

//commit 7c2c3f8 /main.cpp
#include <iostream>
#include <vector>
#include <Eigen/Dense>
#include "GeoUtils.h"
using namespace Eigen;

int main() {
    GeoUtils::GeoElement triangle;
    triangle.push_back(Vector2d(2, 5));
    triangle.push_back(Vector2d(3, 1));
    triangle.push_back(Vector2d(2, 9));
    double angle=90;
    std::cout<<"original triangle:"<<std::endl;
    GeoUtils::printElement(triangle);
    std::cout<<"rotate angle: "<<angle<<std::endl;
    std::cout<<"rotated triangle:"<<std::endl;
    GeoUtils::printElement(GeoUtils::rotate(angle, triangle));
    return 0;
}

输出结果:

original triangle:
(2 5) (3 1) (2 9) 
rotate angle: 90
rotated triangle:
(-5  2) (-1  3) (-9  2)

moverotate命令组合即可实现绕任意点旋转的变换,命名为rotatep(rotating about a point)。实现如下:

GeoUtils::GeoElement GeoUtils::rotatep(double theta, Vector2d point, GeoElement elmt)
{
    return move(point, rotate(theta, move(-point, elmt)));
}

测试代码及结果不再赘述。

TODO:

  1. 读取和解析用户指令

  2. 设计文件格式并实现解析器

  3. GUI的开发