今天写了一个三维网格保存ply格式的小工具函数,因为ply类型是顶点按索引排列的方式,不同于STL类型的按面片索引的方式。因此,准备借助于C++中的Map类型,来进行顶点的去重复和排序。因为对Map结构的不了解,竟然花了一天的时间去实现,中间走了不少歪路,也借鉴了不少其他的方法,最终实现了自己想要的结果。纸上得来终觉浅,绝知此事要躬行,很有必要记录一下,以此为戒。
首先定义STL类型的读取结构:ReadSTL.h
#pragma once
#include
#include
#include
using namespace std;
//-----------------------------顶点结构体----------------------------------
struct VertexT //顶点结构
{
VertexT() {}
VertexT(float x, float y, float z)
{
_x = x; _y = y; _z = z;
}
float _x, _y, _z;
static const DWORD FVF;
};
class Point3f
{
public:
Point3f() :x(0), y(0), z(0)
{
};
Point3f(float _x, float _y, float _z) :x(_x), y(_y), z(_z){};
int SetParam(float _x, float _y, float _z)
{
x = _x;
y = _y;
z = _z;
return 0;
};
inline VertexT IVertex()
{
return VertexT(x, y, z);
}
private:
float x, y, z;
};
class ReadSTLFile
{
public:
ReadSTLFile();
~ReadSTLFile();
bool ReadFile(const char *cfilename);
int NumTri();
vector& PointList();
vector& PointNormList();
private:
vector pointList;
vectorpointNormList;
unsigned int unTriangles;
bool ReadASCII(const char *cfilename);
bool ReadBinary(const char *cfilename);
char* memwriter;
int cpyint(const char*& p);
float cpyfloat(const char*& p);
};
然后是STL类型数据的实现:ReadSTL.cpp
#include "ReadSTL.h"
#include
#include
#include
#include
#include
ReadSTLFile::ReadSTLFile()
{
}
ReadSTLFile::~ReadSTLFile()
{
}
bool ReadSTLFile::ReadFile(const char *cfilename)
{
FILE * pFile;
long lSize;
char* buffer;
size_t result;
/* 若要一个byte不漏地读入整个文件,只能采用二进制方式打开 */
fopen_s(&pFile, cfilename, "rb");
if (pFile == NULL)
{
fputs("File error", stderr);
exit(1);
}
/* 获取文件大小 */
fseek(pFile, 0, SEEK_END);
lSize = ftell(pFile);
rewind(pFile);
/* 分配内存存储整个文件 */
buffer = (char*)malloc(sizeof(char)*lSize);
if (buffer == NULL)
{
fputs("Memory error", stderr);
exit(2);
}
/* 将文件拷贝到buffer中 */
result = fread(buffer, 1, lSize, pFile);
if (result != lSize)
{
fputs("Reading error", stderr);
exit(3);
}
/* 结束演示,关闭文件并释放内存 */
fclose(pFile);
ios::sync_with_stdio(false);
if (buffer[79] != '\0')//判断格式
{
ReadASCII(buffer);
}
else
{
ReadBinary(buffer);
}
ios::sync_with_stdio(true);
free(buffer);
return true;
}
bool ReadSTLFile::ReadASCII(const char *buffer)
{
unTriangles = 0;
float x, y, z;
int i;
string name, useless;
stringstream ss(buffer);
ss >> name >> name >> name >> name;
ss.get();
do {
ss >> useless;
if (useless != "facet")
break;
ss >> useless >> x >> y >> z;
pointNormList.push_back(Point3f(x, y, z));
getline(ss, useless);
getline(ss, useless);
for (i = 0; i < 3; i++)
{
ss >> useless >> x >> y >> z;
pointList.push_back(Point3f(x, y, z));
}
unTriangles++;
getline(ss, useless);
getline(ss, useless);
getline(ss, useless);
} while (1);
return true;
}
bool ReadSTLFile::ReadBinary(const char *buffer)
{
const char* p = buffer;
char name[80];
int i, j;
memcpy(name, p, 80);
p += 80;
unTriangles = cpyint(p);
for (i = 0; i < unTriangles; i++)
{
// p += 12;//跳过头部法向量
pointNormList.push_back(Point3f(cpyfloat(p), cpyfloat(p), cpyfloat(p)));
for (j = 0; j < 3; j++)//读取三顶点
pointList.push_back(Point3f(cpyfloat(p), cpyfloat(p), cpyfloat(p)));
p += 2;//跳过尾部标志
}
return true;
}
int ReadSTLFile::NumTri()
{
return unTriangles;
}
vector& ReadSTLFile::PointList()
{
return pointList;
}
vector& ReadSTLFile::PointNormList()
{
return pointNormList;
}
int ReadSTLFile::cpyint(const char*& p)
{
int cpy;
memwriter = (char*)&cpy;
memcpy(memwriter, p, 4);
p += 4;
return cpy;
}
float ReadSTLFile::cpyfloat(const char*& p)
{
float cpy;
memwriter = (char*)&cpy;
memcpy(memwriter, p, 4);
p += 4;
return cpy;
}
最后是实现:
#include
#include
#include
#include
#include
#include
#include
#include
在自定义比较函数StlCmpVec时,指定 StlCmpVec, Map建立时传入的是一个StlCmpVec类名,而不是一个函数或 functor 对象。这点要注意,然后里面的重载运算符(),只是用来做比较,返回值要符合Map的定义规则,不能直接返回true或者false。
另外借鉴了这篇文章:元素为自定义复合结构时 map,set 如何处理重复 key 及排序?