1. 程式人生 > >簡化測試程式碼

簡化測試程式碼

#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];

    // 頂點的紋理座標
    CVector2d textcoord[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;            
        }

        // 面的紋理座標個數
        size_t num_vt;
        s_line >> num_vt;

        // 讀取面的頂點的紋理座標(假如含有紋理座標)
        for(int j = 0; j < 3; j++)
            s_line >> mem->textcoord[j][0] >> mem->textcoord[j][1];

        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);

    vector<CVector2d> vec_text; // 臨時儲存紋理座標

    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 == "vt")
        {
            CVector2d textcoord;
            s_line >> textcoord[0] >> textcoord[1];
            vec_text.push_back(textcoord);
        }

        if(str == "f")
        {            
            mesh_face * mem = new mesh_face;
            for(int j = 0; j < 3; j++)
            {
                string str_f;
                if( ! (s_line >> str_f))
                    return false;

                string str_v, str_vt;
                // 讀取面的頂點號
                if(str_f.find("/") != string::npos)
                {
                    str_vt = str_f.substr(str_f.find_first_of("/") + 1, str_f.find_last_of("/"));
                    str_v = str_f.substr(0, str_f.find_first_of("/"));
                }
                else
                    str_v = str_f;

                size_t v = stoi(str_v);
                mem->vertices[j] = v;

                // 讀取面的頂點的紋理座標
                if(str_vt != "")
                {
                    size_t n_vt = stoi(str_vt);
                    mem->textcoord[j] = vec_text[n_vt - 1];
                }
            }
            
            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