DCEL数据结构


前言

虽然https://blog.csdn.net/baidu_34931359/article/details/129962825?spm=1001.2014.3001.5501
已经实现了平面点云的三角化,但是其在效率上还存在较大的优化空间,在非法边的检索过程中会变已生成三角形进行遍历,若可提前直到非法边所对应的三角形,可大幅提高检索效率,将该过程的时间复杂度由n降至常数。
在邓老师的计算几何中提及到双向链接边表(Doubly-Connected Edge List,DCEL),它是一种用来表示平面图拓扑信息的数据结构,DCEL 结构(或者半边结构)可以高效的进行点、边和面等各种相互关系的查询。


DCEL介绍:

​ 在DCEL中,将每条边的两端分别作为一条半边。半边有方向性,由起点出发,指向另一个端点。一条边的两条半边互为孪生半边。
在这里插入图片描述
​ 半边数据结构中包含了三种对象:顶点、半边和面片。每个对象均为固定长度,分别存有下列几何的及拓扑的信息:

顶点:在对应于顶点vetrx 的顶点记录中,除了存储点的坐标信息,还有一个指向半边的指针,指向以V为起点的某一条半边。

半边:在对应于半边edge 的半边记录中,存储了半边的起点,一个半边的指针指向其孪生半边twins。一个面指针指向位于半边左侧的面face。此外还有2个半边指针,分别指向沿着face(e)边界方向的前一条半边和后一条半边。

面片:在对应于面face的记录中,只存储了一个指向半边的指针,指向该面边界上的某一条半边。
在这里插入图片描述

DCEL优势:

半边(half-edge)数据结构是一种略微复杂的边表示方法,优点是可以方便、快速地获得以下信息:

  1. 哪些面使用了这个顶点
  2. 哪些边使用了这个顶点
  3. 哪些面使用了这条边
  4. 哪些边构成了这个面
  5. 哪些面和这个面相邻

如果用最简单的顶点+索引的方式存储网格,问题1,2,3,5都是很难解决的,需要遍历所有顶点。而半边结构可以在常数时间内完成上述操作。

缺点是网格连接关系变动后,需要维护的信息也比较多。另外,半边结构表达的网格需要是流形结构,半边结构的构造也需要一定的时间开销。

DCEL数据结构实现:

下面给出了关键的数据结构Point、Vertex、HalfEdge与Triangle的具体实现。

Point:

class Point
{
public:
    long long id;
    double x;
    double y;
    bool used = 0; //used or not
    Face* bucket = NULL; //the bucket of the point
    Point(long long id_, double x_, double y_)
    {
        this->id = id_;
        this->x = x_;
        this->y = y_;
    }
    ~Point(){}
};

Vertex:

class Vertex
{
public:
    Point* p = NULL; //the coordinate of the vertex
    HalfEdge* half_edge = NULL; //reference to the first outgoing incident half-edge
    Vertex(Point* p_)
    {
        this->p = p_;
        this->half_edge = NULL;
    }
    ~Vertex(){}

};

HalfEdge

class HalfEdge 
{
public:
    bool valid = 1; //whether the edge is valid
    HalfEdge* twin = NULL; //reference to the twin half edge
    Vertex* source = NULL; //reference to the source vertex
    Vertex* target = NULL;
    Face* face = NULL; //reference to the left incident face
    HalfEdge* next = NULL; //reference to CCW next half-edge
    HalfEdge()
    {
        this->valid = 1;
    }
    ~HalfEdge(){}

};

Face

class Face
{
public:
    HalfEdge* half_edge = NULL; //the first half edge incident to the face from left
    vector<Point*> bucket_points; //the unassigned points of the bucket
    Face()
    {
        this->bucket_points.clear();
    }
    ~Face()
    {
        this->bucket_points.clear();
    }
};

逐点将点P插入三角形中是该算法的关键,在构件新的三角形的同时,需要更新DCEL数据结构,并删除旧的三角形。

void DelaunayTriangulation::ConnectTriangle(Vertex* p, Face* original_face)
{
	Face* abc = original_face;
	HalfEdge* ab = abc->halfEdge;
	HalfEdge* bc = ab->next;
	HalfEdge* ca = bc->next;
	Vertex* a = ab->source;
	Vertex* b = bc->source;
	Vertex* c = ca->source;
	HalfEdge* pa = new HalfEdge();
	HalfEdge* pb = new HalfEdge();
	HalfEdge* pc = new HalfEdge();
	HalfEdge* ap = new HalfEdge();
	HalfEdge* bp = new HalfEdge();
	HalfEdge* cp = new HalfEdge();
	Face* pab = new Face();
	Face* pbc = new Face();
	Face* pca = new Face();
	p->halfEdge = pa;
	pa->source = p;
	pa->target = a;
	pb->source = p;
	pb->target = b;
	pc->source = p;
	pc->target = c;

	ap->source = a;
	ap->target = p;
	bp->source = b;
	bp->target = p;
	cp->source = c;
	cp->target = p;

	pa->face = pab;
	pb->face = pbc;
	pc->face = pca;
	ap->face = pca;
	bp->face = pab;
	cp->face = pbc;
	ab->face = pab;
	bc->face = pbc;
	ca->face = pca;

	pa->twin = ap;
	ap->twin = pa;
	pb->twin = bp;
	bp->twin = pb;
	pc->twin = cp;
	cp->twin = pc;

	pa->next = ab;
	ab->next = bp;
	bp->next = pa;

	pb->next = bc;
	bc->next = cp;
	cp->next = pb;

	pc->next = ca;
	ca->next = ap;
	ap->next = pc;

	pab->halfEdge = pa;
	pbc->halfEdge = pb;
	pca->halfEdge = pc;

	this->all_half_edges.push_back(pa);
	this->all_half_edges.push_back(pb);
	this->all_half_edges.push_back(pc);
	this->all_half_edges.push_back(ap);
	this->all_half_edges.push_back(bp);
	this->all_half_edges.push_back(cp);

	//update bucket points
	for (int i = 0; i < abc->bucketPoints.size(); i++)
	{
		Point* the_point = abc->bucketPoints[i];
		if (the_point->used == 1)
		{
			continue;
		}
		if (InTriangle(pab, the_point))
		{
			pab->bucketPoints.push_back(the_point);
			the_point->bucket = pab;
		}
		else if (InTriangle(pbc, the_point))
		{
			pbc->bucketPoints.push_back(the_point);
			the_point->bucket = pbc;
		}
		else
		{
			pca->bucketPoints.push_back(the_point);
			the_point->bucket = pca;
		}
	}
	abc->bucketPoints.clear();
	delete(abc);
}

Reference:

http://www.holmes3d.net/graphics/dcel/

https://www.flipcode.com/archives/The_Half-Edge_Data_Structure.shtml

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sankingvenice

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值