作业一Readme

2018-09-24  本文已影响0人  正经龙

1. 实现大致框架,实现下图继承关系的几个类并给对应类写好空过程

继承关系

各类继承关系如下所示

Spline

class Spline
{
public:
    Spline();
    ~Spline();
    // 用于画图的FOR VISUALIZATION
    virtual void Paint(ArgParser *args);
    virtual void addControlPoint(int pt, float x, float y);
    virtual void set(int i, Vec3f v);
    // 用于实现样条类型转换的FOR CONVERTING BETWEEN SPLINE TYPES
    virtual void OutputBezier(FILE *file) {};
    virtual void OutputBSpline(FILE *file) {};
    // 用于得到控制点的FOR CONTROL POINT PICKING
    virtual int getNumVertices();
    virtual Vec3f getVertex(int i);

    // 用于编辑操作的FOR EDITING OPERATIONS
    virtual void moveControlPoint(int selectedPoint, float x, float y);
    virtual void ddControlPoint(int selectedPoint, float x, float y);
    virtual void deleteControlPoint(int selectedPoint);

    // 用于产生三角形的FOR GENERATING TRIANGLES
    virtual TriangleMesh* OutputTriangles(ArgParser *args);
    /////////

};

BezierCurve

class BezierCurve:public Curve
{

public:
    BezierCurve();
    BezierCurve(int array);
    void Paint(ArgParser *args);//绘制函数
    void GetCnk(GLint n, GLint* c);//获取参数1,3,3,1
    void GetPointPr(GLint *c, GLfloat t, Point3D *Pt, int ControlN, Point3D *ControlP);//获取当前点的坐标
    void OutputBSpline(FILE *file);
    void OutputBezier(FILE *file);
    void addControlPoint(int pt, float x, float y);
    Point3D* caluate(Point3D * pt);//传入四个点,传出四个点
    TriangleMesh* OutputTriangles(ArgParser *args);
    ~BezierCurve();
};

BSplineCurve

{
public:
    BSplineCurve();
    BSplineCurve(int array);
    void Paint(ArgParser *args);
    void GetPointPr(GLfloat t,Point3D& Pt, int ControlN, Point3D* ControlP);
    void addControlPoint(int pt, float x, float y);
    void OutputBSpline(FILE *file);
    void OutputBezier(FILE *file);
    TriangleMesh* OutputTriangles(ArgParser *args);
    Point3D * caluate(Point3D * pt);
    ~BSplineCurve();
};

SurfaceOfRevolution

class SurfaceOfRevolution : public Surface
{
private:
    Curve * myCurve;
public:
    SurfaceOfRevolution();
    SurfaceOfRevolution(Curve* curve);
    ~SurfaceOfRevolution();
    virtual void OutputBezier(FILE *file);
    virtual void OutputBSpline(FILE *file);
    void moveControlPoint(int selectedPoint, float x, float y);
    void addControlPoint(int pt, float x, float y);
    void deleteControlPoint(int selectedPoint);
    void Paint(ArgParser *args);//绘制函数
    int getNumVertices();
    Vec3f getVertex(int i);
    TriangleMesh* OutputTriangles(ArgParser *args);
};

BzeierPatch

class BezierPatch :
    public Surface
{
    Point3D Point[16];
public:
    BezierPatch();
    virtual void set(int c, Vec3f v);
    ~BezierPatch();
    void Paint(ArgParser *args);//绘制函数
    void GetCnk(GLint n, GLint* c);//获取参数1,3,3,1
    void GetPointPr(GLint *c, GLfloat t, Point3D *Pt, int ControlN, Point3D *ControlP);//获取当前点的坐标
    void OutputBSpline(FILE *file);
    void OutputBezier(FILE *file);
    GLfloat** mxn(GLfloat ** m1, GLfloat ** m2, int m, int n, int p);
    Point3D* caluate(Point3D * pt);//传入四个点,传出四个点
    TriangleMesh* OutputTriangles(ArgParser *args);
};

1. 遇到的问题

vs2017版本不兼容问题

右键工程->属性->常规->附加包含目录
修改成刚刚的工程目录

添加链接目录

然后将头文件与源文件拷贝到工程中,就完成了第一步

2. 实现Bezier 与 BSpline曲线的绘制

实现思路:由于首先让我们绘制的是四个控制点的Bezier与BSpline的曲线,但是这里看了一下接下来的题目,对于接下来的多Bezier与多BSpline控制点进行了兼容。

对于Bezier曲线来说曲线的生成首先指定其控制点,然后根据其控制点的坐标与Bezier矩阵进行矩阵相乘,得出一个曲线对应公式,然后根据传进来的参数值进行曲线分割。

Bezier 绘制

首先在set函数里面保存控制点信息

void Curve::set(int i, Vec3f v)
{
    GLfloat x = 0.0, y = 0.0, z = 0.0;
    v.Get(x,y,z);
    Point3D PointADD;
    PointADD.x = x;
    PointADD.y = y;
    PointADD.z = z;
    Point.push_back(PointADD);
    num++;
}

由于Curve是Bezier与BSpline的父类,所以对于两者实现相同的函数都写在Curve中。这里的Point3D是自定义的数据类型,与文件中的Vec3f保留数据相同,都是三个GLfloat数据点

实现Paint函数
void BezierCurve::Paint(ArgParser *args) {
    Vec3f myVec[4];
    for (int n = 0; n < (num-1)/3; n++) {
        for (int four = 0; four < 4; four++) {
            myVec[four].Set( Point[n * 3 + four].x, Point[n * 3 + four].y, Point[n * 3 + four].z);
        }
    glPointSize(5);
    glLineWidth(5);
    glBegin(GL_LINES);
    glColor3f(0, 0, 1);
    GLfloat x[4] = { 0,0,0,0 };
    GLfloat y[4] = { 0,0,0,0 };
    GLfloat z[4] = { 0,0,0,0 };
    for (int i = 0; i < 4; i++) {
        myVec[i].Get(x[i], y[i], z[i]);
    }
    for (int i = 0; i < 3; i++) {
        glVertex3f(x[i], y[i], z[i]);
        glVertex3f(x[i + 1], y[i + 1], z[i + 1]);
    }
    glEnd();
    glColor3f(0.0, 1, 0.0);
    GLint *c;
    Point3D Pt(0, 0, 0);
    GLint ControlN = 4;
    c = new GLint[ControlN];
    GetCnk(ControlN - 1, c);
    Point3D* ControlP = new Point3D[4];
    for (int i = 0; i < 4; i++) {
        ControlP[i].x = x[i];
        ControlP[i].y = y[i];
        ControlP[i].z = z[i];
    }
    glBegin(GL_LINES);
    for (int i = 0; i <args->curve_tessellation; i++) {
        GetPointPr(c, (GLfloat)i / (GLfloat)args->curve_tessellation, &Pt, 4, ControlP);
        glVertex3f(Pt.x, Pt.y, Pt.z);
        GetPointPr(c, (GLfloat)(i + 1) / (GLfloat)args->curve_tessellation, &Pt, 4, ControlP);
        glVertex3f(Pt.x, Pt.y, Pt.z);
    }
    glEnd();
    glColor3f(1, 0, 0);
    glBegin(GL_POINTS);
    for (int i = 0; i < 4; i++) {
        glVertex3f(x[i], y[i], z[i]);
    }
    glEnd();
    delete[] ControlP;
    }
    
}

核心就是绘制曲线部分,通过获取四个控制点,与Bezier控制点相乘,得到一个曲线公式,利用杨辉三角求出(1-t)与(t)的参数关系,这里由于是四个控制点所以是1331,最后通过指定由t段直线描绘曲线来绘制t次直线,从而得到曲线。

BSpline Paint

BSpline曲线与Bzier曲线生成几乎相同,要注意的只有一点,就是Bezier的多点绘制方式是1234\2345\3456这种一次向后推移一位的绘制方式,除此之外就没有什么区别了,利用BSpline矩阵与四个控制点生成对应的曲线,然后根据传进来的参数确定曲线上近似点的位置,最后绘制出曲线。

效果图

Bezier


Bezier

BSpline


BSpline

3.Bezier与BSpline的转换

根据公式G*Bezier T = G BSpline * T
假设T相等
要求G(BSpline),则公式变成
G x Bezeir x BSpline﹣¹
由于G已知,Bezier 已知,只要求出BSpline的逆矩阵就可得到对应的控制点。
这里求逆用的是伴随矩阵与行列式求逆
给出求伴随矩阵的代码。通过两层循环得到每一行的每一个先行数,剩下两层循环用于求出剩下3乘3矩阵对应的行列式的值

    //求伴随矩阵
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            for (int x = 0; x < 4; x++) {
                for (int y = 0; y < 4; y++) {
                    if (x == i || y == j) {
                        continue;
                    }
                    else {
                        small[x > i ? x - 1 : x][y > j ? y - 1 : y] = BSplineNumber[x][y];
                    }
                }
            }
            GLfloat addNum = small[0][0] * small[1][1] * small[2][2] + small[0][1] * small[1][2] * small[2][0] +
                small[0][2] * small[1][0] * small[2][1];
            GLfloat jianNum = small[0][0] * small[1][2] * small[2][1] + small[0][1] * small[1][0] * small[2][2] + small[0][2] * small[1][1] * small[2][0];
            GLfloat help = (addNum - jianNum);
            A_[j][i] = pow(-1, j + i)*help;
        }
    }
最后通过矩阵相乘得到最后的点矩阵,给出矩阵相乘代码
GLfloat** Curve::mxn(GLfloat ** m1, GLfloat ** m2, int m, int n, int p)//矩阵相乘
{
    GLfloat **result = new GLfloat*[m];
    for (int i = 0; i < m; i++) {
        result[i] = new GLfloat[p];
    }
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < p; j++) {
            result[i][j] = 0;
            for (int z = 0; z < n; z++) {
                result[i][j] += m1[i][z] * m2[z][j];
            }
        }
    }
    return result;
}

最后实现写入文件就完成了第三部分任务
写入文件就是根据老师的文件规格写入对应的控制点的数据就行。
这里给出Bezier转BSpline的写入

这里的caluatef返回对应的BSpline的四个控制点
void BezierCurve::OutputBSpline(FILE * file)
{

    fprintf(file, "%s %s ", "bspline", "num_vertices");
    fprintf(file, "%d ", 4);
    Point3D* pt=caluate(&Point[0]);
    for (int i = 0; i < 4; i++) {
        fprintf(file, "%f %f %f ", (float)pt[i].x, (float)pt[i].y, (float)pt[i].z);
    }
}

效果图

Bezier转BSpline BSpline转Bezier

4. 实现多点曲线绘制

由于在之前实现了对多点的兼容,所以就不副贴代码,对于之前的Paint函数的相关部分做一展示

for (int n = 0; n < (num-1)/3; n++) {
        for (int four = 0; four < 4; four++) {
            myVec[four].Set( Point[n * 3 + four].x, Point[n * 3 + four].y, Point[n * 3 + four].z);
        }

Bezier是1234、4567、789 10 进行控制点复制,所以初始点坐标n = (num-1)/3
每次根据这个控制点获取四个控制点,然后与初始只有四个点的绘制过程相同,最后绘制出对应的多段曲线

BSpline对应的控制点公式为n = num - 3;

效果图

image.png
image.png
image.png

5. 实现控制点编辑功能

实现思路:通过点击选定对应控制点,利用向量存储控制点
添加点:在选定线上与点击位置求垂足,确定垂足坐标,插入到向量中形成这条线的两点中间,然后调用重绘函数,重新绘制
void BSplineCurve::addControlPoint(int pt, float x, float y)
{
    Point3D newPoint(x, y, 0);
    Point.insert(Point.begin() + pt, newPoint);
    num++;
}
删除点:删除对应选重点即在控制点向量中删除对应点坐标,也就是调用erase删除指定下标的向量。
void Curve::deleteControlPoint(int selectedPoint)
{
    if (num > 4) {
        Point.erase(Point.begin() + selectedPoint);
        num--;
    }
}
移动点:鼠标点击选定,记录坐标覆盖对应控制点坐标位置,并在拖动中保持重绘
void Curve::moveControlPoint(int selectedPoint, float x, float y)
{
    Point[selectedPoint].x = x;
    Point[selectedPoint].y = y;
}

效果图(由于Bezier不可添加控制点,这里采用BSpline演示)

移动点
添加点

6.实现旋转曲线的绘制

实现思路:在SurfaceOfRevolution中实现绘制函数,在Paint函数中调用对应的Curve子类绘制函数,同时实现各种包括添加点,移动点,删除点的函数,仅仅通过初始化的时候传进来的Curve指针就可以调用Curve对应的函数,SurfaceOfRevolution仅仅只是调用一下。
关键:实现OutputTriangles,初始化TriangleNet,第一个参数是所画的笔数,第二个参数是画几笔,框架会自动连接好每一笔形成一个几何体
利用Bezier或者BSpline对应的计算曲线函数求出当前曲线在空间中的对应坐标,然后通过指定的描述曲面个数来确定每次绕y轴旋转的角度,最后将每次求出来的点的坐标绕轴旋转对应次数,并且使用SetVertex存入即可
TriangleMesh * BezierPatch::OutputTriangles(ArgParser * args)
{
    //获取点的个数与分割个数
    int patch_tessellation = args->patch_tessellation;
    int num_vertices = 16;
    //获取面的分割个数
    int _v_tess = patch_tessellation;
    Vec3f** myVec3f = new Vec3f*[patch_tessellation];
    for (int i = 0; i < patch_tessellation; i++) {
        myVec3f[i] = new Vec3f[patch_tessellation];
    }
    Vec3f *helpVec = new Vec3f[4 * patch_tessellation];
    TriangleNet *myMesh = new TriangleNet(patch_tessellation, _v_tess);
    GLint *c;
    Point3D* Pt = new  Point3D;
    GLint ControlN = 4;
    c = new GLint[ControlN];
    GetCnk(ControlN - 1, c);
    Point3D* ControlP = new Point3D[4];
    for (int i = 0; i < 4; i++) {
        for (int n = 0; n < 4; n++) {
            ControlP[n].x = Point[i*4 + n].x;
            ControlP[n].y = Point[i*4 + n].y;
            ControlP[n].z = Point[i*4 + n].z;
        }
        for (int j = 0; j < patch_tessellation; j++) {
            GetPointPr(c, (GLfloat)j / (GLfloat)patch_tessellation, Pt, 4, ControlP);
            helpVec[i*patch_tessellation+j].Set(Pt->x, Pt->y, Pt->z);
        }
    }
    for (int i = 0; i < patch_tessellation; i++) {
        
        for (int n = 0; n < 4; n++) {
            helpVec[i + patch_tessellation * n].Get(ControlP[n].x, ControlP[n].y, ControlP[n].z);
        }
        for (int j = 0; j < patch_tessellation; j++) {
            GetPointPr(c, (GLfloat)j / (GLfloat)patch_tessellation, Pt, 4, ControlP);
            myVec3f[i][j].Set(Pt->x, Pt->y, Pt->z);
        }
    }
    for (int i = 0; i < patch_tessellation*patch_tessellation; i++) {
        myMesh->SetVertex(i / patch_tessellation, i%patch_tessellation, myVec3f[i / patch_tessellation][i%patch_tessellation]);
    }
    for (int i = 0; i < patch_tessellation; i++) {
        delete[] myVec3f[i];
    }
    delete[] myVec3f;
    return myMesh;
}

效果图

image.png image.png

7. 实现自定义绘制

实现思路:由于在surfaceOfRevolution中实现了对控制点的各种点击事件,所以就可以对传进来的曲线进行自定义的移动,然后保存到obj文件中,最后可以绘制出自定义的图形
由于各种点击触发的响应事件已经在Curve中实现过了,所以SurfaceOfRevolution中仅仅只需要调用一下就可以了
void SurfaceOfRevolution::OutputBezier(FILE * file)
{
    myCurve->OutputBezier(file);
}

void SurfaceOfRevolution::OutputBSpline(FILE * file)
{
    myCurve->OutputBSpline(file);
}

void SurfaceOfRevolution::moveControlPoint(int selectedPoint, float x, float y)
{
    myCurve->moveControlPoint(selectedPoint, x, y);
}

void SurfaceOfRevolution::addControlPoint(int pt, float x, float y)
{
    myCurve->addControlPoint(pt, x, y);
}

void SurfaceOfRevolution::deleteControlPoint(int selectedPoint)
{
    myCurve->deleteControlPoint(selectedPoint);
}

void SurfaceOfRevolution::Paint(ArgParser * args)
{
    myCurve->Paint(args);
}

int SurfaceOfRevolution::getNumVertices()
{
    return myCurve->getNumVertices();
}

Vec3f SurfaceOfRevolution::getVertex(int i)
{
    return myCurve->getVertex(i);
}

TriangleMesh * SurfaceOfRevolution::OutputTriangles(ArgParser * args)
{
    TriangleMesh *myTriangleMesh;
    myTriangleMesh=myCurve->OutputTriangles(args);
    return myTriangleMesh;
}

效果图

罐子
罐子

8. 实现4乘4格网绘制曲面

实现思路:
  1. 声明16个大小的控制点数组,装入控制点
  2. 读入要求绘制的曲线层数,初始化对应的TriangleNet,第一个参数为一条曲线所需要的点的个数,第二个参数为曲线的条数
  3. 利用Bezier曲线公式求出对应点的坐标
  4. 通过TriangleNet的SetVertex函数将求出点的坐标保存
  5. 返回TriangleNet
具体实现函数
//获取点的个数与分割个数
    int patch_tessellation = args->patch_tessellation;
    int num_vertices = 16;
    //获取面的分割个数
    int _v_tess = patch_tessellation;
    Vec3f** myVec3f = new Vec3f*[patch_tessellation];
    for (int i = 0; i < patch_tessellation; i++) {
        myVec3f[i] = new Vec3f[patch_tessellation];
    }
    Vec3f *helpVec = new Vec3f[4 * patch_tessellation];
    TriangleNet *myMesh = new TriangleNet(patch_tessellation, _v_tess);
    GLint *c;
    Point3D* Pt = new  Point3D;
    GLint ControlN = 4;
    c = new GLint[ControlN];
    GetCnk(ControlN - 1, c);
    Point3D* ControlP = new Point3D[4];
    for (int i = 0; i < 4; i++) {
        for (int n = 0; n < 4; n++) {
            ControlP[n].x = Point[i*4 + n].x;
            ControlP[n].y = Point[i*4 + n].y;
            ControlP[n].z = Point[i*4 + n].z;
        }
        for (int j = 0; j < patch_tessellation; j++) {
            GetPointPr(c, (GLfloat)j / (GLfloat)patch_tessellation, Pt, 4, ControlP);
            helpVec[i*patch_tessellation+j].Set(Pt->x, Pt->y, Pt->z);
        }
    }
    for (int i = 0; i < patch_tessellation; i++) {
        
        for (int n = 0; n < 4; n++) {
            helpVec[i + patch_tessellation * n].Get(ControlP[n].x, ControlP[n].y, ControlP[n].z);
        }
        for (int j = 0; j < patch_tessellation; j++) {
            GetPointPr(c, (GLfloat)j / (GLfloat)patch_tessellation, Pt, 4, ControlP);
            myVec3f[i][j].Set(Pt->x, Pt->y, Pt->z);
        }
    }
    for (int i = 0; i < patch_tessellation*patch_tessellation; i++) {
        myMesh->SetVertex(i / patch_tessellation, i%patch_tessellation, myVec3f[i / patch_tessellation][i%patch_tessellation]);
    }
    for (int i = 0; i < patch_tessellation; i++) {
        delete[] myVec3f[i];
    }
    delete[] myVec3f;
    return myMesh;

效果图

image.png image.png

9. 绘制茶壶

茶壶函数已经写好,调用一下绘制保存就行了


image.png
image.png

至此,完成作业要求。

上一篇下一篇

猜你喜欢

热点阅读