Delaunay 三角网生成

算法参照 https://github.com/ironwallaby/delaunay,使用逐点插入法,并将点沿X坐标排序以优化生成速度。

//================================================
// Copyright (c) 2016 周仁锋. All rights reserved.
// ye_luo@qq.com
//================================================
#ifndef _CDELAUNAY_H_
#define _CDELAUNAY_H_
#include "CVector3.h"
#include "CMesh.h"
#include <vector>
#include <list>
using namespace std;

/**
* @brief Delaunay 三角网生成器
*/
class CDelaunay {
public:
	//! 默认构造函数
	CDelaunay();
	//! 基于顶点列表构造
	CDelaunay(const vector<CVector3>& points);

	//! 添加点
	void AddPoint(const CVector3& point);
	//! 添加坐标点
	void AddPoint(float x, float y, float z);
	//! 清空已添加
	void ClearPoint();
	//! 生成三角网
	bool Generate();

	//! 获取顶点数量
	int GetPointCount() const;
	//! 获取一个顶点
	CVector3 GetPoint(int index) const;
	//! 获取三角形数量
	int GetTriangleCount() const;
	//! 获取一个三角形
	void GetTriangle(int index, int vertices[3]) const;

	//! 生成网格对象
	CMesh* CreateMesh(bool isStatic, bool wireframe);

private:
	//! 三角形边定义
	typedef struct _SEdge {
		size_t v1;
		size_t v2;
		_SEdge(size_t a, size_t b): v1(a), v2(b) {}
	} SEdge;
	//! 三角形定义
	typedef struct _STriangle {
		size_t a;
		size_t b;
		size_t c;
		float cx;
		float cy;
		float radius2;
		_STriangle(size_t i, size_t j, size_t k): a(i), b(j), c(k) {}
	} STriangle;

private:
	//! 将所有顶点沿X轴排序
	void SortPointsAlongX(vector<CVector3>& vertices, vector<size_t>& indices);
	//! 添加超级三角形到顶点列表末尾
	void AddSuperTriangle(vector<CVector3>& vertices);
	//! 计算三角形外接圆
	STriangle& Circumcircle(STriangle& triangle);
	//! 移除边列表中的重复边
	void RemoveDoublyEdge(list<SEdge>& edges);
	//! 生成三角形列表
	void BuildTriangles(list<STriangle>& triangles);

private:
	//! 顶点数组
	vector<CVector3> m_vecPoints;
	//! 三角形列表
	vector<STriangle> m_vecTriangles;
};

#endif
//================================================
// Copyright (c) 2016 周仁锋. All rights reserved.
// ye_luo@qq.com
//================================================
#include "CDelaunay.h"

/**
* 构造函数
*/
CDelaunay::CDelaunay() {
}

/**
* 使用一系列点构造
*/
CDelaunay::CDelaunay(const vector<CVector3>& points) {
	m_vecPoints = points;
}

/**
* 添加点
*/
void CDelaunay::AddPoint(const CVector3& point) {
	m_vecPoints.push_back(point);
}

/**
* 添加坐标点
*/
void CDelaunay::AddPoint(float x, float y, float z) {
	m_vecPoints.push_back(CVector3(x, y, z));
}

/**
* 清空已添加
*/
void CDelaunay::ClearPoint() {
	m_vecPoints.clear();
	m_vecTriangles.clear();
}

/**
* 生成三角网(逐点插入法)
*/
bool CDelaunay::Generate() {
	m_vecTriangles.clear();
	if (m_vecPoints.size() < 3) return false;

	// 顶点索引
	vector<size_t> indices;
	SortPointsAlongX(m_vecPoints, indices);
	AddSuperTriangle(m_vecPoints);
	// 计算超级三角形外接圆并加入到开放列表中
	list<STriangle> openList;
	list<STriangle> closedList;
	STriangle super(indices.size(), indices.size() + 1, indices.size() + 2);
	openList.push_back(Circumcircle(super));

	// 遍历所有顶点
	for (size_t i = 0; i < indices.size(); i++) {
		list<SEdge> edgeList;
		list<STriangle>::iterator iter = openList.begin();
		while (iter != openList.end()) {
			const float dx = m_vecPoints[indices[i]][0] - iter->cx;
			const float dy = m_vecPoints[indices[i]][1] - iter->cy;
			if (dx > 0.0f && dx * dx > iter->radius2) {
				list<STriangle>::iterator tmp = iter++;
				closedList.push_back(*tmp);
				openList.erase(tmp);
				continue;
			}
			if (dx * dx + dy * dy < iter->radius2) {
				list<STriangle>::iterator tmp = iter++;
				edgeList.push_back(SEdge(tmp->a, tmp->b));
				edgeList.push_back(SEdge(tmp->b, tmp->c));
				edgeList.push_back(SEdge(tmp->c, tmp->a));
				openList.erase(tmp);
				continue;
			}
			++iter;
		}
		// 移除重边并将当前点与所有边组成三角形加入到开放列表中
		RemoveDoublyEdge(edgeList);
		for (list<SEdge>::iterator j = edgeList.begin(); j != edgeList.end(); ++j) {
			STriangle triangle(j->v1, j->v2, indices[i]);
			openList.push_back(Circumcircle(triangle));
		}
	}
	// 将超级三角形的顶点移除
	m_vecPoints.pop_back();
	m_vecPoints.pop_back();
	m_vecPoints.pop_back();
	// 将开放列表中三角形加入到闭合列表中
	for (list<STriangle>::iterator i = openList.begin(); i != openList.end(); ++i) {
		closedList.push_back(*i);
	}
	BuildTriangles(closedList);
	return true;
}

/**
* 获取顶点数量
*/
int CDelaunay::GetPointCount() const {
	return m_vecPoints.size();
}

/**
* 获取一个顶点
*/
CVector3 CDelaunay::GetPoint(int index) const {
	return m_vecPoints[index];
}

/**
* 获取三角形数量
*/
int CDelaunay::GetTriangleCount() const {
	return m_vecTriangles.size();
}

/**
* 获取一个三角形
*/
void CDelaunay::GetTriangle(int index, int vertices[3]) const {
	const STriangle& tri = m_vecTriangles[index];
	vertices[0] = tri.a;
	vertices[1] = tri.b;
	vertices[2] = tri.c;
}

/**
* 生成网格对象
*/
CMesh* CDelaunay::CreateMesh(bool isStatic, bool wireframe) {
	CMesh* pMesh = new CMesh();
	pMesh->SetVertexUsage(m_vecPoints.size());
	for (size_t i = 0; i < m_vecPoints.size(); i++) {
		CVector3& vrt = m_vecPoints[i];
		pMesh->AddVertex(CVertex(vrt[0], vrt[1], vrt[2]));
	}
	for (size_t i = 0; i < m_vecTriangles.size(); i++) {
		STriangle& tri = m_vecTriangles[i];
		pMesh->AddTriangle(tri.a, tri.b, tri.c);
	}
	pMesh->Create(isStatic, wireframe);
	return pMesh;
}

/**
* 将所有顶点沿X轴排序
*/
void CDelaunay::SortPointsAlongX(vector<CVector3>& vertices, vector<size_t>& indices) {
	indices.resize(vertices.size());
	for (size_t i = 0; i < vertices.size(); i++) indices[i] = i;
	for (size_t i = 0; i < vertices.size(); i++) {
		for (size_t j = i + 1; j < vertices.size(); j++) {
			if (vertices[indices[j]][0] < vertices[indices[i]][0]) {
				size_t temp = indices[i];
				indices[i] = indices[j];
				indices[j] = temp;
			}
		}
	}
}

/**
* 添加超级三角形到顶点列表末尾
*/
void CDelaunay::AddSuperTriangle(vector<CVector3>& vertices) {
	float minx = vertices[0][0];
	float miny = vertices[0][1];
	float maxx = vertices[0][0];
	float maxy = vertices[0][1];
	for (size_t i = 1; i < vertices.size(); i++) {
		if (vertices[i][0] < minx) minx = vertices[i][0];
		else if (vertices[i][0] > maxx) maxx = vertices[i][0];
		if (vertices[i][1] < miny) miny = vertices[i][1];
		else if (vertices[i][1] > maxy) maxy = vertices[i][1];
	}
	float dx = maxx - minx;
	float dy = maxy - miny;
	float midx = minx + dx * 0.5f;
	float midy = miny + dx * 0.5f;
	float maxw = dx > dy ? dx : dy;
	CVector3 a(midx - 3.0f * maxw, midy - maxw, 0.0f);
	CVector3 b(midx + 3.0f * maxw, midy - maxw, 0.0f);
	CVector3 c(midx, midy + 2.0f * maxw, 0.0f);
	vertices.push_back(a);
	vertices.push_back(b);
	vertices.push_back(c);
}

/**
* 计算三角形外接圆
*/
CDelaunay::STriangle& CDelaunay::Circumcircle(STriangle& triangle) {
	const CVector3& pa = m_vecPoints[triangle.a];
	const CVector3& pb = m_vecPoints[triangle.b];
	const CVector3& pc = m_vecPoints[triangle.c];

	float x1  =  pa.m_fValue[0];
	float x2  =  pb.m_fValue[0];
	float x3  =  pc.m_fValue[0];
	float y1  =  pa.m_fValue[1];
	float y2  =  pb.m_fValue[1];
	float y3  =  pc.m_fValue[1];

	float t1 = x1 * x1 + y1 * y1;
	float t2 = x2 * x2 + y2 * y2;
	float t3 = x3 * x3 + y3 * y3;
	float dt = 2.0f * (x1 * y2 + x2 * y3 + x3 * y1 - y1 * x2 - y2 * x3 - y3 * x1);

	triangle.cx = ((y3 - y1) * (t2 - t1) - (y2 - y1) * (t3 - t1)) / dt;
	triangle.cy = ((x2 - x1) * (t3 - t1) - (x3 - x1) * (t2 - t1)) / dt;
	triangle.radius2 = (x1 - triangle.cx) * (x1 - triangle.cx) + (y1 - triangle.cy) * (y1 - triangle.cy);
	return triangle;
}

/**
* 移除边列表中的重复边
*/
void CDelaunay::RemoveDoublyEdge(list<SEdge>& edges) {
	list<SEdge>::iterator iter = edges.begin();
	while (iter != edges.end()) {
		size_t a = iter->v1;
		size_t b = iter->v2;
		list<SEdge>::iterator temp = iter++;
		for (list<SEdge>::iterator i = iter; i != edges.end(); ++i) {
			size_t m = i->v1;
			size_t n = i->v2;
			if ((a == m && b == n) || (a == n && b == m)) {
				if (i == iter) ++iter;
				edges.erase(i);
				edges.erase(temp);
				break;
			}
		}
	}
}

/**
* 生成三角形列表
*/
void CDelaunay::BuildTriangles(list<STriangle>& triangles) {
	size_t count = m_vecPoints.size();
	for (list<STriangle>::iterator i = triangles.begin(); i != triangles.end(); ++i) {
		if (i->a < count && i->b < count && i->c < count) {
			// 检查三角形方向
			CVector3 ab = m_vecPoints[i->b] - m_vecPoints[i->a];
			CVector3 ac = m_vecPoints[i->c] - m_vecPoints[i->a];
			if (ab.CrossProduct(ac).DotProduct(CVector3::Z) < 0) {
				size_t temp = i->a;
				i->a = i->b;
				i->b = temp;
			}
			m_vecTriangles.push_back(*i);
		}
	}
}

  • 4
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值