从“三角形汤”中找到唯一的顶点
Posted
技术标签:
【中文标题】从“三角形汤”中找到唯一的顶点【英文标题】:Find unique vertices from a 'triangle-soup' 【发布时间】:2010-03-09 08:01:07 【问题描述】:我正在两个库(Opencascade 和 DWF Toolkit)之上构建一个 CAD 文件转换器。
但是,我的问题与平台无关:
给定:
我已经生成了一个网格,三角面列表形成了一个通过我的应用程序构建的模型。每个三角形是通过三个顶点定义的,这些顶点由三个浮点数(x、y 和 z 坐标)组成。由于三角形形成了一个网格,大部分顶点都被多个三角形共享。
目标:
我需要找到唯一顶点列表,并生成一个由该列表中的三个索引的元组组成的面数组。
我想做的是这样的:
//step 1: build a list of unique vertices
for each triangle
for each vertex in triangle
if not vertex in listOfVertices
Add vertex to listOfVertices
//step 2: build a list of faces
for each triangle
for each vertex in triangle
Get Vertex Index From listOfvertices
AddToMap(vertex Index, triangle)
虽然我确实有一个实现,但 step1(唯一顶点列表的生成)在 O(n!) 的顺序上真的很慢,因为每个顶点都与列表中已经存在的所有顶点进行比较。我想“嘿,让我们使用 std::map 构建我的顶点组件的哈希图,这应该可以加快速度!”,结果发现从三个浮点值生成唯一键并不是一件容易的事。
在这里,*** 的专家开始发挥作用:我需要某种适用于 3 个浮点数的哈希函数,或者任何其他从 3d 顶点位置生成唯一值的函数。
【问题讨论】:
这个顶点唯一性必须有多健壮?我的意思是,您只是想节省空间,还是需要非常健壮的拓扑。假设顶点 Va 和 Vb 得到不同的 id pn 和 pq 但实际上是“相同的”,这会破坏交易吗? 是的,因为我正在尝试导出拓扑网格。如果来自源的单个顶点在目标中多次存在,则从它构造的三角形将不会共享边 - 拓扑可能会变得开放。 【参考方案1】:转储数组中的所有顶点,然后执行unique(sort(array))
。这应该是 O(k n log(n)),其中 k 是共享一个顶点的三角形的平均数量,通常是 k
我能想到的唯一警告是您的 unique
函数应该能够获取指向比较函数的指针,因为您可能希望认为顶点相等
distance(vertex1, vertex2) < threshold
但这似乎是OK。
【讨论】:
这种方式效率要高得多。 (我的第一种方法是 35 秒,改进方法是 1.5 秒) 如果你想在彼此的阈值距离内捕捉点,这里有一个讨厌的错误。不能保证靠近在一起的点彼此靠近“排序”。实际上,我认为您可以证明对于任何排序函数,都有任意靠近的点可以排序到列表中的较远位置...不好。 @Michael:继续证明它,然后 如果您只需要一个简单排序的案例。考虑到这个二维点数组的顺序是传统排序会返回 [1,1], [1,10], [1.0001,1] 但是 [1.0001,1] 比 [1,1] 更接近[1,10]。但是由于列表中的分离,唯一会错过它。 (如果你真的想要一个任意排序函数的一般情况的证明,我可能会构造一个,但它会很复杂 - 你可能需要数学学位才能理解它。) 使用哈希(用于这种相等条件检查),而不是数组,并且当然不是链表。【参考方案2】:三种解决方案。当然还有其他的
-
使用哈希映射。这只有在“相同”意味着完全相同的情况下才有效
使用二进制空间分区来划分点
使用常规网格划分您的分数。
在情况 2 和 3 中,如果您想指定一些容差,则需要搜索树或网格的多个部分。在 BSP 情况下,这意味着检查您是否在分割平面的公差范围内,如果是,则递归到两半。在网格情况下,这意味着检查所有在容差范围内的相邻单元格。两者都不是太难,但意味着使用“现成”解决方案将更加困难。
【讨论】:
我花了一点时间才明白空间分区对我有什么帮助——这是一个有趣的方法。 +1 我还用两个坐标多图创建了一个隐式空间索引,一个在 X 中,一个在 Y 中,这很健壮,但 O(n lg n) 缩放没有不要为我解决最大的问题(数百万个顶点)。我想我会尝试使用八叉树或某种空间索引,例如 Morton 索引。 奇怪的是,我实际上又在做类似的事情了。我在 2D-4D 中使用最近邻搜索。输入是几百万个点。一个 KD 树(二进制空间分区)实现为一个线性堆,每个叶节点大约有 16 个顶点,每个核心每秒大约有 10K 查询。构建时间是几秒钟 - 但我认为在这种情况下,您实际上可以结合搜索和构建。【参考方案3】:获取哈希的常见想法是将浮点数的每个位模式与prime 相乘,然后将它们相加。类似的东西:
unsigned int hash_point(float x, float y, float z)
unsigned int* px = (unsigned int*)&x;
unsigned int* py = (unsigned int*)&y;
unsigned int* pz = (unsigned int*)&z;
return (*px)*PRIME1 + (*py)*PRIME2 + (*pz)*PRIME3;
您应该注意 sizeof(unsigned int) 在这里被认为等于 sizeof(float)。此处的示例仅用于说明主要思想,您应该根据需要对其进行调整。
【讨论】:
当 x、y 和 z 不是整数时,将它们乘以素数并没有多大帮助。 正确。你应该乘以浮动的位模式,而不是浮动本身。 请注意,有些数字具有不止一种位模式表示,例如 0 和 -0 相同但位模式不同。这意味着它们将散列到不同的值。不过,这可能对您的应用程序来说是个问题,也可能不是。 我已经看到它(例如here)是由 XOR 完成的,而不是添加条款。以上是关于从“三角形汤”中找到唯一的顶点的主要内容,如果未能解决你的问题,请参考以下文章