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

今天写了一个三维网格保存ply格式的小工具函数,因为ply类型是顶点按索引排列的方式,不同于STL类型的按面片索引的方式。因此,准备借助于C++中的Map类型,来进行顶点的去重复和排序。因为对Map结构的不了解,竟然花了一天的时间去实现,中间走了不少歪路,也借鉴了不少其他的方法,最终实现了自己想要的结果。纸上得来终觉浅,绝知此事要躬行,很有必要记录一下,以此为戒。

首先定义STL类型的读取结构:ReadSTL.h

#pragma once

#include<d3d9.h>
#include<windows.h>
#include<vector>
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<Point3f>& PointList();
	vector<Point3f>& PointNormList();
private:
	vector<Point3f> pointList;
	vector<Point3f>pointNormList;
	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 <vector>
#include <fstream>
#include <iostream>
#include <string>
#include <sstream>

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<Point3f>& ReadSTLFile::PointList()
{
	return pointList;
}

vector<Point3f>& 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 <iostream>
#include <fstream>
#include <vector>
#include <time.h>
#include <windows.h>
#include <stdio.h>
#include <vector>
#include<map>
#include <string>
#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<LVertex3f> outpts_(face_num * 3);
	std::vector<LVertexIndex> facetIndex(face_num);
	std::vector<LTexture2f> outtex_(face_num * 3);
	//StlCmpVec comp(0.000001f);
	std::map<LVertex3f, int, StlCmpVec> VertexIndexMap;
	std::map<LVertex3f, int, StlCmpVec> ::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<const char*> (&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<const char*> (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 及排序?

 

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值