简化测试代码

Source
#include "stdafx.h"
#include <gtest/gtest.h>
#include <fstream>
#include <math.h>
#include <algorithm>
#include <iostream>
#include "..\TestServer\FileHelper.h"
#include "Algorithm/algsimplifier.h"
#include "algqemsimplifier.h"
#include "Geometry/Plane.h"
#include "Algorithm/algTriMeshSegm.h"
#include "gmath/GBox3.h"

// coe = 4*1.732*w/(a*a + b*b + c*c) :其中w代表面积,a,b,c代表边长,coe 的结果1表示等边三角形,0表示边重合
// 狭长三角形系数 coe 的边界值
#define COE_MIN_LONG_TRIANGLE 0.1

// 两个面片法线夹角的最大值
#define MAX_ANGLE 3.1415926

// 简化比允许的上下波动
#define FLUCT_RATIO 0.02

// 计算两个网格模型距离平均值时选取的顶点数的比率
#define DIS_NUM_RATIO 0.1

// 计算两个网格模型距离平均值的边界值
#define DIS_MAX 1.0

typedef CVec3<double> point;
typedef CBox3<double> ptrBox;

// 简化后网格模型文件名标识
string SimSign("_simply");

class mesh_vertex;
class mesh_face;
class tri_mesh;

typedef set<mesh_face*, ggp::dereference_functor_less> FaceSet;

// 类表示网格模型的点
class mesh_vertex
{
public:
    // 默认构造函数
    mesh_vertex(bool inisPatch = false):isPatch(inisPatch){}

    // 坐标
    point pt;
    // 顶点是否加入了分割
    bool isPatch;
    // 与顶点相连的面
    FaceSet p_face;
    friend class tri_mesh;
};

// 类表示网格模型的面
class mesh_face
{
public:
    // 对应的顶点号
    size_t vertices[3];

    //重载“<”操作符,自定义排序规则
    bool operator < (const mesh_face &f) const
    {
        //按vertices从小到大排列
        return (vertices[0] < f.vertices[0]) ||
               (vertices[0] == f.vertices[0] && vertices[1] < f.vertices[1]) ||
               (vertices[0] == f.vertices[0] && vertices[1] == f.vertices[1] && vertices[2] < f.vertices[2]);
    }
    friend class tri_mesh;
};

// 类表示三角网格
class tri_mesh
{
public:
    // 获得pos位置的面
    mesh_face getFace(size_t pos);
    vector<mesh_vertex*> VertexList;
    FaceSet FaceList;
};

// 获得 tri_mesh 中某个位置的面
mesh_face tri_mesh::getFace(size_t pos)
{
    FaceSet::iterator it = FaceList.begin();
    int i = 0;
    while(i < pos)
    {
        it ++;
        i ++;
    }

    return **it;
}

// 读取网格文件中的面和点,网格文件类型为 .ply ,成功返回 true
bool ReadMeshFromFilePly(string mesh_path, tri_mesh & mesh)
{
    ifstream infile(mesh_path);
    string line, str;
    int num_vertex = -1, num_face = -1;

    //读取点和面的数量
    while(getline(infile, line))
    {
        stringstream s_line(line);
        if(s_line >> str )
        {
            if(str == "element" && s_line >> str)
            {
                if(str == "vertex" && s_line >> str)
                    num_vertex = stoi(str);
                if(str == "face" && s_line >> str)
                {
                    num_face = stoi(str);
                    break;
                }
            }
        }        
    }

    if(num_vertex == -1 || num_face == -1)
        return false;
    while(getline(infile, line))
    {
        if(line == "end_header")
            break;
    }

    int i = 0;
    // 读取点和面
    while(i < num_vertex && getline(infile, line))
    {
        mesh_vertex * mem = new mesh_vertex;
        stringstream s_line(line);

        // 读取顶点坐标,如果有方向的话,读取方向
        if(! (s_line >> mem->pt.X >> mem->pt.Y >> mem->pt.Z))
            return false;

        mesh.VertexList.push_back(mem);
        i ++;
    }
    i = 0;
    while(i < num_face && getline(infile, line))
    {
        mesh_face * mem = new mesh_face;
        std::stringstream s_line(line);

        // 面中顶点个数
        size_t num_v;
        s_line >> num_v;
        
        // 读取面的顶点号
        for(int j = 0; j < 3; j++)
        {
            if(! (s_line >> mem->vertices[j]))
                return false;            
        }

        mesh.FaceList.insert(mem);
     
        i ++;
    }
    int m1 = mesh.VertexList.size(), m2 = mesh.FaceList.size();

    if(m1 != num_vertex)
    {
        for(size_t i = 0; i < m1; i ++)
            delete mesh.VertexList[i];
        for(FaceSet::iterator it = mesh.FaceList.begin(); it != mesh.FaceList.end(); it ++)
            delete *it;

        return false;
    }
    
    infile.close();

    size_t pos = 0;
    for(FaceSet::iterator it = mesh.FaceList.begin(); it != mesh.FaceList.end(); it ++)
    {
        for(int j = 0; j < 3; j++)
            mesh.VertexList[(*it)->vertices[j]]->p_face.insert(*it);  // 将面号保存在顶点信息中
        pos ++;
    }

    return true;
}

// 读取网格文件中的面和点,网格文件类型为 .obj ,成功返回 true
bool ReadMeshFromFileObj(string mesh_path, tri_mesh & mesh)
{
    // 为方便计算,使vec_vertex从第二个元素开始有效
    mesh_vertex * temp = new mesh_vertex;
    mesh.VertexList.push_back(temp);

    ifstream infile(mesh_path);
    string line;
        
    // 读取点的数据
    while(getline(infile, line))
    {
        string str;
        stringstream s_line(line);
        s_line >> str;
        if(str == "v")
        {
            // 读取顶点坐标,如果有方向的话,读取方向
            mesh_vertex * mem = new mesh_vertex;
            if( ! (s_line >> mem->pt.X >> mem->pt.Y >> mem->pt.Z))
                return false;           

            mesh.VertexList.push_back(mem);
        }

        if(str == "f")
        {
            // 读取面的顶点号
            mesh_face * mem = new mesh_face;
            for(int j = 0; j < 3; j++)
            {
                string str_v;
                if( ! (s_line >> str_v))
                    return false;
                else
                {
                    if(str_v.find("/"))
                        str_v = str_v.substr(0, str_v.find_first_of("/"));

                    size_t v = stoi(str_v);
                    mem->vertices[j] = v;                    
                }
            }
            
            mesh.FaceList.insert(mem);
        }        
    }
    int m1 = mesh.VertexList.size(), m2 = mesh.FaceList.size();     

    infile.close();

    size_t pos = 0;
    for(FaceSet::iterator it = mesh.FaceList.begin(); it != mesh.FaceList.end(); it ++)
    {
        for(int j = 0; j < 3; j++)
            mesh.VertexList[(*it)->vertices[j]]->p_face.insert(*it);  // 将面号保存在顶点信息中
        pos ++;
    }

    return true;
}

// 读取网格文件中的面和点,成功返回 true
bool ReadMeshFromFile(string mesh_path, tri_mesh & mesh)
{
    string type = mesh_path.substr(mesh_path.find_last_of("."), mesh_path.size() - mesh_path.find_last_of("."));
    if(type == ".obj")
        return ReadMeshFromFileObj(mesh_path, mesh);

    if(type == ".ply")
        return ReadMeshFromFilePly(mesh_path, mesh);
    return true;
}

// 判断两个网格模型空间之间的距离属性,mesh1:简化前,mesh2:简化后。返回 num 个点距离平方的平均值
double GetDisSquareOfTwoMesh(tri_mesh & mesh1, tri_mesh & mesh2, size_t num)
{
    double dissquare = 0.0;

    if(num == 0)
        return dissquare;  

    // 每隔per个点取 mesh2 中的一个点
    size_t per;
    if(num > mesh2.VertexList.size())
        per = 1;
    else
        per = mesh2.VertexList.size()/num;

    // 找到对应的距离最小的点
    int i = 0, pos = 0;
    while(pos < mesh2.VertexList.size())
    {
        i ++;
        if(i == per + 1)
            i = 0;
        if(i != per)
        {
            pos ++;
            continue;
        }

        // 循环找到 vec_vertex1 中距离该点最近距离的点(为简化计算,用x+y+z的值来找最近点)        
        double dis = mesh1.VertexList[0]->pt.SqrDistanceTo(mesh2.VertexList[pos]->pt);
        for(size_t j = 1; j < mesh1.VertexList.size(); j++)
        {
            double new_dis = mesh1.VertexList[j]->pt.SqrDistanceTo(mesh2.VertexList[pos]->pt);
            if(dis > new_dis)
            {
                if(new_dis < 1e-6)
                    int o = 10;
                dis = new_dis;
            }
        }

        // 将距离平方加入到 dissquare 中
        dissquare += dis;
        pos ++;
    }   

    cout<<"两个网格模型距离:"<<dissquare/num<<endl;
    return dissquare/num;
}

// 获得三角形的面积(海伦-秦九公式),已知一个面的三个顶点,a,b,c分别为边长
double GetTriangleArea(tri_mesh & mesh, const mesh_face & face, double & a, double & b, double &c)
{
    point pt1, pt2, pt3;
    pt1 = mesh.VertexList[face.vertices[0]]->pt;
    pt2 = mesh.VertexList[face.vertices[1]]->pt;
    pt3 = mesh.VertexList[face.vertices[2]]->pt;

    double p, s;

    // 求三边长度
    a = pt1.DistanceTo(pt2);
    b = pt1.DistanceTo(pt3);
    c = pt2.DistanceTo(pt3);

    p = (a + b + c) / 2;
    // 求面积
    s = sqrt(p*(p - a)*(p - b)*(p - c));

    return s;
}

// 计算两个向量的夹角
static double GetAngleOfTwoDir(const point& v1, const point & v2)
{
    double mult = v1.Dot(v2);
    double longth1 = v1.Length();
    double longth2 = v2.Length();
    return acos(mult / (longth1 * longth2));
}

// 通过两个点获得一个向量,vectex1 表示起点,vertex2 表示终点
static void GetVecFromVertex(const point & vertex1, const point & vertex2, point & vect)
{
    vect[0] = vertex2[0] - vertex1[0];
    vect[1] = vertex2[1] - vertex1[1];
    vect[2] = vertex2[2] - vertex1[2];
}
// 由三角面片的三个点获得三角面片的方向
static void GetDirectOfFace(const point & vertex1, const point & vertex2, const point & vertex3, point & dir)
{
    // 两个点组成的向量
    point vector1, vector2;
    GetVecFromVertex(vertex1, vertex2, vector1);
    GetVecFromVertex(vertex2, vertex3, vector2);
    
    // 获得两个向量的叉乘,即面片的方向
    dir = vector1.Cross(vector2);
}

// 使用公式 coe = 4*1.732*w/(a*a + b*b + c*c) :其中w代表面积,a,b,c代表边长,coe 的结果1表示等边三角形,0表示边重合
int GetLongTriangleNum(tri_mesh & mesh)
{
    int num = 0;
    for(FaceSet::iterator it = mesh.FaceList.begin(); it != mesh.FaceList.end(); it ++)
    {
        double coe, a, b, c, s;
        s = GetTriangleArea(mesh, **it, a, b, c);
        coe = 4 * 1.732 * s /(pow(a, 2) + pow(b, 2) + pow(c, 2));

        if(coe <= COE_MIN_LONG_TRIANGLE)
        {
            //cout<<"狭长三角形:"<<i<<"   系数是:"<<coe<<endl;
            num ++;
        }
    }
    cout<<"狭长三角形个数:"<<num<<endl;

    return num;
}

// 获得两个 mesh_face 的公共顶点个数
size_t GetCommenVertexNum(const mesh_face & face1, const mesh_face & face2, tri_mesh & mesh)
{
    size_t num = 0;
    for(size_t i = 0; i < 3; i ++)
    {
        for(size_t j = 0; j < 3; j++)
        {
            if(face1.vertices[i] == face2.vertices[j])
                num ++;
        }
    }

    return num;
}

// 找到三角面片 n_pos  周围相邻的面片
void FindAdjTriangle(mesh_face face, tri_mesh & mesh, FaceSet & adj_triangle)
{
    for(size_t i = 0; i < 3; i ++)
    {
        size_t vertex_pos =  face.vertices[i]; // 找到面的第i个顶点的顶点号
        FaceSet & pFace = mesh.VertexList[vertex_pos]->p_face;  // 第i个顶点周围的面号
        for(FaceSet::iterator it = pFace.begin(); it != pFace.end(); it ++)
        {
            if(GetCommenVertexNum(face, **it, mesh) == 2)
                adj_triangle.insert(*it);
        }
    }
}

// 获得面的法线
void GetUnitOfFace(mesh_face face, tri_mesh & mesh, point & dir)
{
    point vertex[3];
    for(size_t i = 0; i < 3; i ++)
        vertex[i] = mesh.VertexList[face.vertices[i]]->pt;

    GetDirectOfFace(vertex[0], vertex[1], vertex[2], dir);
}

// 判断褶皱,即判断网格模型中的三角形是否有两个三角形角度小于等于120度的情况,即两个三角形法线点乘大于0
int GetCoincideTriangleNum(tri_mesh & mesh)
{
    int num = 0;
    for(FaceSet::iterator it = mesh.FaceList.begin(); it != mesh.FaceList.end(); it ++)
    {
        point dir1, dir2;        
        GetUnitOfFace(**it, mesh, dir1);

        // 找到相邻的面
        FaceSet adj_triangle;
        FindAdjTriangle(**it, mesh, adj_triangle);
        
        // 查找跟 vec_face[i] 有公共边的面
        for(FaceSet::iterator it2 = adj_triangle.begin(); it2 != adj_triangle.end(); it2 ++)
        {       
            GetUnitOfFace(**it2, mesh, dir2);

            // 如果两个面片的方向向量点乘小于等于0.0,证明两个面片夹角小于等于180度            
            double angle = GetAngleOfTwoDir(dir1, dir2);
            if(angle >= MAX_ANGLE)
            {
                num ++;
            }
        }        
    }
    cout<<"褶皱面个数:"<<num<<endl;

    return num;
}

// 获得拟合平面的距离误差
double GetFitPlaneCoef(ggp::UVPatch::UVPatchGeom & goems, tri_mesh & mesh, string fileType)
{
    vector<CVector3d> pPtsVec;
    CPlaneCoef plane_coe;
    double coe;
    for(size_t i = 0; i < goems.faceVertexIdxs.size(); i ++)
    {
        size_t vertex_id = goems.faceVertexIdxs[i];
        if(fileType == ".obj")
            vertex_id ++;

        mesh.VertexList[vertex_id]->isPatch = true; // 点标记为加入到分割中
        
        point & re_vetex = mesh.VertexList[vertex_id]->pt;
        pPtsVec.push_back(re_vetex);
    }

    plane_coe.CalPlaneCoef(pPtsVec, pPtsVec.size(), coe);
    return coe;
}

// 获得平均网格模型的平均边长
double GetAverSqrSideLenth(tri_mesh & mesh)
{
    double sqrlength = 0.0;
    int num = 0;
    for(FaceSet::iterator it = mesh.FaceList.begin(); it != mesh.FaceList.end(); it ++)
    {
        point pts[3];
        for(size_t j = 0; j < 3; j ++)
        {
            size_t ver_id = (*it)->vertices[j];
            pts[j] = mesh.VertexList[ver_id]->pt;
        }

        sqrlength += pts[0].SqrDistanceTo(pts[1]) + pts[0].SqrDistanceTo(pts[2]) + pts[1].SqrDistanceTo(pts[2]);
        num += 3;
    }
    return sqrlength/num;
}
// 验证是否所有的平面加入了分割
bool IsAllInPatch(tri_mesh & mesh)
{
    bool isAllPath = true;
    for(size_t i = 0; i < mesh.VertexList.size(); i ++)
    {
        if(mesh.VertexList[i]->p_face.size() > 0 && ! mesh.VertexList[i]->isPatch)
            return false;
    }
    return isAllPath;
}

// 释放内存
void FreeMesh(tri_mesh & mesh)
{
    // 释放顶点
    for(size_t i = 0; i < mesh.VertexList.size(); i ++)
        if(mesh.VertexList[i])
            delete mesh.VertexList[i];

    // 释放面
    for(FaceSet::iterator it = mesh.FaceList.begin(); it != mesh.FaceList.end(); it ++)
        if(*it)
            delete *it;
}

// 验证三角网格简化:是否能够正确获得简化模型
void SimplifyResultTest_1(string fileName, double ratio)
{
    string meshPath = CFileHelper::GetFilePath(fileName, g_WhiteboxCaseSimplifyMetrics);

    string fileType = fileName.substr(fileName.find_last_of("."), fileName.size() - fileName.find_last_of("."));

    // 读取网格模型,并将简化的模型写入新的文件
    SimConfig simSign(false, COE_MIN_LONG_TRIANGLE, cos(MAX_ANGLE));
    ggp::SimMesh* simedMesh = ggp::Simplify(meshPath, ratio, simSign);
   
    EXPECT_TRUE(simedMesh != nullptr);

    // 将简化后网格模型写入文件
    string sub1 = meshPath.substr(0, meshPath.find_last_of("."));
    string sub2 = meshPath.substr(meshPath.find_last_of("."), meshPath.size() - meshPath.find_last_of("."));
    string simplydMeshPath = sub1 + SimSign + sub2;
    bool isWriteOk = ggp::EdgeCollapsor::writeTriMesh(*simedMesh, simplydMeshPath);

    EXPECT_TRUE(isWriteOk);
    
    // 读取简化前后
    tri_mesh mesh1, mesh2;

    ReadMeshFromFile(meshPath, mesh1);
    ReadMeshFromFile(simplydMeshPath, mesh2);

    // 重新计算简化比,使简化比在0.0 - 1.0
    if(ratio > 1.0 + g_DistEpsilon)
    {
        int temp = ratio;
        ratio = (double)temp/mesh1.FaceList.size();
    }

    if(ratio > 1.0 && ratio <= 1.0 + g_DistEpsilon)
        ratio = 1.0;

    EXPECT_TRUE(ratio >= 0.0 && ratio <= 1.0);

    // 验证实际简化比是否正确
    double f1, f2;
    f1 = mesh1.FaceList.size();
    f2 = mesh2.FaceList.size();

    double real_ratio = f2/f1;
    
    if(ratio < 1.0/f1)
        EXPECT_TRUE(real_ratio == 0.0);
    else
        EXPECT_TRUE(fabs(ratio - real_ratio) <= FLUCT_RATIO);

    // 验证两个网格模型距离是否正确                                //-----------------------------------------------------------待修正---------------------------------
    double dis = GetDisSquareOfTwoMesh(mesh1, mesh2, DIS_NUM_RATIO * mesh2.VertexList.size());
    EXPECT_TRUE(dis < DIS_MAX);
                                                                   //-----------------------------------------------------------待修正---------------------------------
    // 折叠三角形
    int CoinNum = GetCoincideTriangleNum(mesh1);
    int CoinNum_Simplify = GetCoincideTriangleNum(mesh2);
    EXPECT_TRUE(CoinNum_Simplify <= CoinNum);

    // 狭长三角形
    int LongNum = GetLongTriangleNum(mesh1);
    int LongNum_Simplify = GetLongTriangleNum(mesh2);
    EXPECT_TRUE(LongNum_Simplify <= LongNum);    

    // 验证简化后的网格模型——网格分割是否正确
    double sqrLength = GetAverSqrSideLenth(mesh2);

    ggp::TriMeshSegm meshSegm;
    double dDistEpsilon = 0.0;
    meshSegm.PartitionMesh(*simedMesh);
    std::vector<ggp::UVPatch::UVPatchGeom> patchGeoms = meshSegm.ExportPatchs();

    for(int i = 0; i < patchGeoms.size(); i ++)
    {
        dDistEpsilon = GetFitPlaneCoef(patchGeoms[i], mesh2, fileType);  // 拟合平面距离误差
        //cout<<dDistEpsilon<<"   ";
        // 验证拟合平面的距离误差是否正确
        double coe_patch = dDistEpsilon /(sqrt(sqrLength) * 2);
        EXPECT_TRUE(coe_patch < 10.0)<<coe_patch;
    }
    EXPECT_TRUE(IsAllInPatch(mesh2)); // 验证是否所有面参与了分割
    
    // 释放内存
    delete simedMesh;
    FreeMesh(mesh1);
    FreeMesh(mesh2);

    // 删除创建的简化后网格模型文件
    string delFile = (string)"del" + " " + simplydMeshPath;
    system(delFile.data());
}

// 验证三角网格简化:不能正确获得简化模型
void SimplifyResultTest_2(string fileName, double ratio)
{
    string meshPath = CFileHelper::GetFilePath(fileName, g_WhiteboxCaseSimplifyMetrics);

    // 读取网格模型,并将简化的模型写入新的文件
    ggp::SimMesh* simedMesh = ggp::Simplify(meshPath, ratio);

    EXPECT_TRUE(simedMesh == nullptr);

    string sub1 = meshPath.substr(0, meshPath.find_last_of("."));
    string sub2 = meshPath.substr(meshPath.find_last_of("."), meshPath.size() - meshPath.find_last_of("."));
    string simplydMeshPath = sub1 + "_simply" + sub2;
    bool isWriteOk = ggp::EdgeCollapsor::writeTriMesh(*simedMesh, simplydMeshPath);

    EXPECT_FALSE(isWriteOk);
}

//表示 文件名 和 简化比的类
struct FilenameAndRatio
{
    FilenameAndRatio(string f, double r) : fileName(f), ratio(r){}

    string fileName;  // 文件名
    double ratio;  // 简化比
};
// 测试用例参数化类:获得正确的简化模型
class MeshSimplifyTest_1 : public ::testing::TestWithParam<FilenameAndRatio>
{
};

// 测试用例参数化类:不能获得正确的简化模型
class MeshSimplifyTest_2 : public ::testing::TestWithParam<FilenameAndRatio>
{
};

//  时间超长的测试用例
class LongTime_MeshSimplifyTest : public ::testing::TestWithParam<FilenameAndRatio>
{
};

// 参数化:正确简化
TEST_P(MeshSimplifyTest_1, GGP_22144)
{
    FilenameAndRatio Data = GetParam();
    string fileName = Data.fileName;
    double ratio = Data.ratio;

    SimplifyResultTest_1(fileName, ratio);
}

// 参数化:不能正确简化
TEST_P(MeshSimplifyTest_2, GGP_22144)
{
    FilenameAndRatio Data = GetParam();
    string fileName = Data.fileName;
    double ratio = Data.ratio;

    SimplifyResultTest_2(fileName, ratio);
}

// 参数化:不能正确简化
TEST_P(LongTime_MeshSimplifyTest, GGP_22144)
{
    FilenameAndRatio Data = GetParam();
    string fileName = Data.fileName;
    double ratio = Data.ratio;

    SimplifyResultTest_1(fileName, ratio);
}

// 测试用例:正确简化
INSTANTIATE_TEST_CASE_P(TrueReturn, MeshSimplifyTest_1, testing::Values(// 经典网格模型

                                        FilenameAndRatio("bunny.obj", 0.0),               // /0   //v: 3508    f: 6944
                                        FilenameAndRatio("bunny.ply", 0.5),               // /1   //v: 35947   f: 69451
                                        FilenameAndRatio("horse.ply", 1.0),               // /2   //v: 48485   f: 96966
                                        FilenameAndRatio("bunny.obj", 0.00001),           // /3   //v: 3508    f: 6944 
                                        FilenameAndRatio("bunny.ply", 0.99999),           // /4   //v: 35947   f: 69451
                                        FilenameAndRatio("bunny.obj", 1.00001),           // /5   //v: 3508    f: 6944
                                        FilenameAndRatio("horse.ply", 96966 - 3),         // /6   //v: 48485   f: 96966
                                        FilenameAndRatio("horse.ply", 5),                 // /7   //v: 48485   f: 96966

                                        // 离散获得的网格模型
                                        FilenameAndRatio("annulus.ply", 0.5),             // /8   //v: 64      f: 64
                                        FilenameAndRatio("cone.ply", 0.00001),            // /9   //v: 74      f: 144
                                        FilenameAndRatio("sphere.ply", 0.99999),          // /10  //v: 642     f: 1280
                                        FilenameAndRatio("ModifyFile.obj", 0.5),          // /11  //v: 642     f: 1280

                                        // 存在往复边
                                        FilenameAndRatio("ReciproEdge.ply", 0.5),         // /12  //v: 75      f: 145
                                        // 存在退化三角形
                                        FilenameAndRatio("DegeTriangle.ply", 0.5),        // /13  //v: 74      f: 144
                                        // 存在重合的三角形
                                        FilenameAndRatio("CoinTriangle.ply", 0.1),        // /14  //v: 642     f: 1281
                                        // 非流行网格模型
                                        FilenameAndRatio("NonManifold.ply", 0.5),         // /15  //v: 38      f: 67
                                        // 存在游离的点
                                        FilenameAndRatio("FreeVertex.obj", 0.5),          // /16  //v: 3511    f: 6944

                                        // 验证三角网分割的特殊用例
                                        FilenameAndRatio("DesignChair.obj", 0.5),         // /17  //v: 16034   f: 32064
                                        FilenameAndRatio("Medalballfootball.obj", 0.5),   // /18  //v: 23778   f: 47492
                                        FilenameAndRatio("MedBottles.obj", 0.5),          // /19  //v: 2214    f: 4396
                                        FilenameAndRatio("StingSword.obj", 0.5) ));       // /20  //v: 1329    f: 2596
                                                                        
// 测试用例:不能正确简化
INSTANTIATE_TEST_CASE_P(FalseReturn, MeshSimplifyTest_2, testing::Values(// 错误的简化比

                                        FilenameAndRatio("bunny.obj", -1.0),              // /0   //v: 3508    f: 6944
                                        FilenameAndRatio("bunny.ply", -0.5),              // /1   //v: 35947   f: 69451
                                        FilenameAndRatio("horse.ply", -0.00001),          // /2   //v: 48485   f: 96966
                                        FilenameAndRatio("bunny.ply", 69451 + 3),         // /3   //v: 35947   f: 69451

                                        // 错误的模型
                                        // 含有错误的标识符
                                        //FilenameAndRatio("ErrorIdentifier.ply", 0.5),     // /4   //v: 64      f: 64          // 卡住了
                                        // 三角形有些点不存在
                                        //FilenameAndRatio("InvalidVertex.ply", 0.5),       // /5   //v: 642     f: 1280      // 生成了简化后网格模型,没有报错
                                        // 0个点,0个面
                                        FilenameAndRatio("NoVertexFace.ply", 0.5),        // /6   //v: 0       f: 0

                                        // 错误的路径
                                        FilenameAndRatio("bunny", 0.5),                   // /7
                                        FilenameAndRatio("12345543", 0.5),                // /8
                                        FilenameAndRatio("bunny\\bunny.obj", 0.5) ));     // /9

// 运行超长时间的测试用例
INSTANTIATE_TEST_CASE_P(LongTime, LongTime_MeshSimplifyTest, testing::Values(// 运行时间较长的用例

                                        FilenameAndRatio("Armadillo.ply", 0.9),           // /0   //v: 172974  f: 345944
                                        FilenameAndRatio("blade.ply", 441477),            // /1   //v: 882954  f: 1765388
                                        FilenameAndRatio("dragon_vrip.ply", 0.9),         // /2   //v: 437645  f: 871414
                                        FilenameAndRatio("hand.ply", 0.9),                // /3   //v: 327323  f: 654666
                                        FilenameAndRatio("happy_vrip.ply", 0.9),          // /4   //v: 543652  f: 1087716
                                        FilenameAndRatio("sofa.obj", 0.9) ));             // /5   //v: 189637  f: 379234