【zeno】找到三角面的邻接三角面的算法

定义

先讲什么是邻接三角面:
一个三角面,有三条边,这三条边不但属于这个三角面,而且还可能属于其他相邻的三角面。这些相邻的三角面就是邻接三角面。最少有一个邻接三角面(在边角处),最多有三个邻接三角面(在中间)

邻接的英文是adjacent

算法

算法概述

分两步:

  1. 建立边到三角面的映射。也就是找到边所属的三角面。
  2. 对每个三角面的三条边,分别查询边到三角面映射,就可以得到邻接三角面。

在此期间,我们需要利用以下关键特性:

尽管是同一条边,但是由于所属三角面不同,他们的序号恰好是相反的。也就是同一条边以相反的序号存储了两边。

数据结构

我们利用C++中的unorded_map。

首先定义边到三角面映射,保存到map1中:

std::unordered_map<edgeType, triType, tuple_hash, tuple_equal> map1;//边到三角面

还要定义另一个映射表,从三角面到邻接三角面的。map2

using multiTriType = std::vector<triType>;
std::unordered_map<triType, multiTriType, tuple_hash, tuple_equal> map2;//面到邻接面(多个)

其中

        using edgeType = vec2i;
        using triType = vec3i;

并且C++本身只支持单个数字的哈希,不支持三个数的哈希。因此我们要重载unordered_map中的哈希函数(也就是第三个类型参数)。最好还要指明什么样的哈希是相同的哈希值(也就是第四个类型参数)。
这时就需要把三个数合成一个哈希值。通常就用三个乱序数字的异或来实现。这三个magic number可以取

2718281828, 3141592653, 1618033989

假如使用zeno框架,我们直接使用现成的函数即可。只需要include如下头文件。

#include <zeno/utils/tuple_hash.h>

具体实现

  1. 先建立边到面的映射表。存到map1里。
// 1. 先建立边到面的映射表。存到map1里。
std::unordered_map<edgeType, triType, tuple_hash, tuple_equal> map1;//边到三角面
for(auto const & t : tris)
{
    vec2i e1{t[0], t[1]};   //三角面的三条边
    vec2i e2{t[1], t[2]};   
    vec2i e3{t[2], t[0]};

    map1.emplace(e1,t);  
    map1.emplace(e2,t);  
    map1.emplace(e3,t);  
} 
  1. 找到三角面的邻接面。
    遍历所有边到三角面的映射表。
    对每条边,假如恰好有个与其序号相反的边也在表内,那么这条边就是它的邻接边。
    那么邻接边所属的三角面就是它的一个邻接三角面。存到map2里。
        //2. 找到三角面的邻接面。
        //遍历所有边到三角面的映射表。
        //对每条边,假如恰好有个与其序号相反的边也在表内,那么这条边就是它的邻接边。
        //那么邻接边所属的三角面就是它的一个邻接三角面。存到map2里。
        using multiTriType = std::vector<triType>;
        std::unordered_map<triType, multiTriType, tuple_hash, tuple_equal> map2;//面到邻接面(多个)
        for(auto &[k,v]:map1) //k是边,v是所属的面
        {
            edgeType inv{k[1],k[0]}; //相反序号的边
            if(map1.find(inv) != map1.end())
            {
                const triType & one= map1.at(inv); //其中一个邻接面
                map2[v].push_back(one);//放到邻接面表中
            }
        }

以上就是算法的全部内容。到此,我们拥有了map2。通过查询map2,那么就能找到相应的邻接三角面了。


然而zeno不方便传递map数据结构。zeno中最常用的是传递vector数据(也就是attrVec),并且只能是一维vector(不能是vector套vector)。
所以假如你使用zeno,我们必须想办法把数据转换为vector形式的。

于是我们就把邻接三角面用tris中的下标来表示。如果不存在,那就存-1。这样传递的数据就是一个vector的数组了。

首先我们得把构建一个反向映射的map,给定三个数,得到相应的序号。

//把tris和lines都存成反向的序号map。也就是键是tris的三个int,值则是tris数组中的序号。
std::unordered_map<triType, int, tuple_hash, tuple_equal> triMap;
for (int i = 0; i < tris.size(); i++)
    triMap[tris[i]] = i;

其次我们建立一个新的attrVec,把数据都放进去。

        //3. 最后把邻接三角面存到tris的属性 adjTriId 当中。用于可视化
        auto & adjTriId = prim->tris.add_attr<vec3i>("adjTriId");
        std::fill(adjTriId.begin(),adjTriId.end(),vec3i{-1,-1,-1});
        for (size_t i = 0; i < tris.size(); i++)
        {
            const multiTriType & adjs = map2.at(tris[i]); //这是一组邻接面,可能有多个,是个vector
            for(int j=0; j < adjs.size(); j++) //取出每个邻接面
            {
                int ind = triMap.at(adjs[j]); //找到在tris中对应的序号
                adjTriId[i][j] = ind; //j是0,1,2
            }
        }
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值