在上一篇中我们梳理了半边数据结构的基础,以及如何用 OpenMesh 遍历元素。这一次我们把目光聚焦到一个核心函数——add_face,看看它是如何在背后把整个拓扑图搭起来的。
你对网格说:
![]()
mesh.add_face(vh0, vh1, vh2);OpenMesh 就会执行一连串操作,把相关的半边、边和面全部“接通”。这个过程到底发生了什么?我们用一个具体例子走一遍。
![]()
先回忆一下上一篇里那两张三角形拼成的四边形,现在在它的右侧再贴一个三角形。顶点坐标如下:
auto v0 = mesh.add_vertex({0, 0, 0});auto v1 = mesh.add_vertex({1, 0, 0});auto v2 = mesh.add_vertex({1, 1, 0});auto v3 = mesh.add_vertex({0, 1, 0});auto v4 = mesh.add_vertex({2, 0.5, 0}); // 新三角形的顶点mesh.add_face(v0, v1, v2); // F0mesh.add_face(v0, v2, v3); // F1mesh.add_face(v2, v1, v4); // F2在添加第三个面 F2 之前,四边形的边界环是闭合的,由四条半边构成:h1(v1,v0) → h9(v0,v3) → h7(v3,v2) → h3(v2,v1) → h1。沿着这个环走一圈,你会毫不费力地回到起点。
现在,如果我们只是简单地创建新边、分配面,但不更新边界链接,会发生什么?
假设我们仅创建了缺失的两条边——也就是四条新半边 h10 到 h13,把它们分配给新面,并闭合其内部轮廓。此时,原来的半边 h3 会继续指向 h10。你再次尝试走旧的边界环:h1 → h9 → h7 → h3 → h10 → h12 → h3 → h10……永远回不到 h1。你从 h3 掉进了新面的循环,再也出不来,边界环就这样被打破了。
![]()
所以,add_face 远不是“补几条边”那么简单。沿接缝处的半边链接必须被小心且按正确顺序修复,这才是 add_face 内部大量工作的重心。
新边带来的不仅是面自身消耗的半边——h10 和 h12,还有它们的对偶半边:h11(v4,v1) 和 h13(v2,v4)。这两条是尚不属于任何面的“新鲜”边界半边。修复的思路,就是让边界环绕过它们,而不是穿过 h3:让 h7 指向 h13,h13 再指向 h11,最后 h11 指向 h1。只改写三个 next 指针,边界就重新闭合——h1 → h9 → h7 → h13 → h11 → h1,边界现在绕着新三角形走。
那么,这些 next 指针要怎么算?核心不变量是:next(h) 必须是与 h 在同一循环(面回路或边界环)中的下一条半边,并且它必须从 h 的终点开始。因此,要修复一条边界半边,我们可以站在它的终点顶点,轮转所有出射半边,直到找到另一条边界半边:
fix_next(h): // h 是边界半边,终点为 v out = twin(h) // v 的第一个出射候选 while face(out) != -1: // 已经属于某个面 out = twin(prev(out)) // 绕 v 旋转 next(h) = out // 第一个空闲的出射半边这一小段算法,正是让半边结构在每次添加新面后维持拓扑一致性的秘诀。下一次你调用 add_face 时,不妨在脑海里过一遍这条指针的修复轨迹。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.