天天看點

Delaunay 三角網生成

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

//================================================
// Copyright (c) 2016 周仁鋒. All rights reserved.
// [email protected]
//================================================
#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.
// [email protected]
//================================================
#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);
		}
	}
}
           

繼續閱讀