使用C++ 标准库中的中的Map对自定义的结构体数据集进行排序+去重复

今天写了一个三维网格保存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
#include 
#include"ReadSTL.h"

//自定义比较排序函数
class StlCmpVec
{
public:

	bool operator()(const LVertex3f& _v0, const LVertex3f& _v1) const
	{
		if (_v0.x == _v1.x)
		{
			if (_v0.y == _v1.y)
			{
				if (_v0.z == _v1.z)
				{
					return (_v0.z < _v1.z);
				}
				else
				{
					return (_v0.z < _v1.z);
				}
			}
			else return (_v0.y < _v1.y);
		}
		else return (_v0.x < _v1.x);
	}
};

void saveBinaryPLY(char* oupathply, float* pts, float* nor, const int face_num)
{
	//Create Vertex Index
	int verts_num = 0;
	std::vector outpts_(face_num * 3);
	std::vector facetIndex(face_num);
	std::vector outtex_(face_num * 3);
	//StlCmpVec comp(0.000001f);
	std::map VertexIndexMap;
	std::map ::iterator  VertexIndexItr;
	LTriangle trianglevertex;
	LTexture triangletexture;
	LVertexIndex triangleVertexIndex;
	for (int i = 0; i < face_num; i++)
	{
		trianglevertex.vertex[0].x = pts[i * 9];
		trianglevertex.vertex[0].y = pts[i * 9 + 1];
		trianglevertex.vertex[0].z = pts[i * 9 + 2];
		trianglevertex.vertex[1].x = pts[i * 9 + 3];
		trianglevertex.vertex[1].y = pts[i * 9 + 4];
		trianglevertex.vertex[1].z = pts[i * 9 + 5];
		trianglevertex.vertex[2].x = pts[i * 9 + 6];
		trianglevertex.vertex[2].y = pts[i * 9 + 7];
		trianglevertex.vertex[2].z = pts[i * 9 + 8];
		for (int j = 0; j < 3; j++)
		{
			VertexIndexItr = VertexIndexMap.find(trianglevertex.vertex[j]);
			if (VertexIndexItr == VertexIndexMap.end())
			{
				outpts_[verts_num] = trianglevertex.vertex[j];//vertex
				outtex_[verts_num] = triangletexture.texture[j];
				triangleVertexIndex.index[j] = verts_num;
				VertexIndexMap[trianglevertex.vertex[j]] = verts_num;
				verts_num++;
			}
			else
				triangleVertexIndex.index[j] = VertexIndexItr->second;
		}
		facetIndex[i] = triangleVertexIndex;//index
	}
	outpts_.resize(verts_num);

	cout << "Ascii : verts_num = " << verts_num << endl;
	cout << "Ascii : face_num   = " << face_num << endl;
	cout << "Ascii : VertexIndexMap size " << VertexIndexMap.size() << endl;

	//write binary ply
	FILE* fpply;
	fopen_s(&fpply, oupathply, "wb");
	fprintf_s(fpply, "%s\n", "ply");
	fprintf_s(fpply, "%s %s\n", "format binary_little_endian ", "1.0");
	fprintf_s(fpply, "%s\n", "comment Export generated by Fussen");
	fprintf_s(fpply, "%s\n", "comment TextureFile texture.jpg");
	fprintf_s(fpply, "%s %d\n", "element vertex", verts_num);
	fprintf_s(fpply, "%s\n", "property float x");
	fprintf_s(fpply, "%s\n", "property float y");
	fprintf_s(fpply, "%s\n", "property float z");
	fprintf_s(fpply, "%s %d\n", "element face", face_num);
	fprintf_s(fpply, "%s\n", "property list uchar int vertex_indices");
	fprintf_s(fpply, "%s\n", "end_header");
	for (int i = 0; i < verts_num; ++i)
	{
		float temp[3];
		temp[0] = outpts_[i].x;
		temp[1] = outpts_[i].y;
		temp[2] = outpts_[i].z;
		fwrite((char const*)temp, sizeof(float), 3, fpply);
	}
	unsigned char value = (unsigned char)3;//ply format demand value must unsigned char and const char*
	for (int i = 0; i < face_num; ++i)
	{
		fwrite(reinterpret_cast (&value), sizeof(unsigned char), 1, fpply);
		int temp[3];
		temp[0] = facetIndex[i].index[0];
		temp[1] = facetIndex[i].index[1];
		temp[2] = facetIndex[i].index[2];
		fwrite(reinterpret_cast (temp), sizeof(int), 3, fpply);
	}
	fclose(fpply);
}

int main()
{
	cout << "Test Start..." << endl;
	//Test texture
	char *readpath = "../Test.stl";
	int vertpts_size = 0;
	float *vertpts = new float[MAXTRIANGLENUM * 9]();//vertpts_size 是顶点个数;vertpts_的排列方式是每个面片三个顶点的xyz坐标依次排列
	float *vertnor = new float[MAXTRIANGLENUM * 3]();
	unsigned char *vertcolor = new unsigned char[MAXTRIANGLENUM * 3]();
	ReadAsciiSTL(readpath, vertpts, vertnor, vertcolor, vertpts_size);
	cout << "Read Date end" << endl;
	char* oupathply = "Test.ply";
	int face_num = vertpts_size /  9;
	saveBinaryPLY(oupathply, vertpts, vertnor, face_num);
	delete[]vertpts;
	delete[]vertnor;
	delete[]vertcolor;
	cout << "Test Texture End." << endl;
	getchar();
	return 0;
}

 

在自定义比较函数StlCmpVec时,指定 StlCmpVec, Map建立时传入的是一个StlCmpVec类名,而不是一个函数或 functor 对象。这点要注意,然后里面的重载运算符(),只是用来做比较,返回值要符合Map的定义规则,不能直接返回true或者false。

另外借鉴了这篇文章:元素为自定义复合结构时 map,set 如何处理重复 key 及排序?

 

 

 

 

你可能感兴趣的:(C++编程技术)