上一篇文章说了提取拉普拉斯矩阵的第二个(或者其它)特征向量能得到一个有规律分布的标量场。现在讲的是如何在这样一个规律分布的标量场上提取出等值线。注意,这是在一个三角网格上面提取等值线,并不是真正的曲面。提取出等值线后,可以做一些相关的应用。
那么,如何提取等值线了?
首先,如果只是绘制出等值线,可以简单的设置纹理贴图值(比如间隔多少行,设置纹理值为白色),标量场对应一维纹理坐标。更详细的说明,可以参考我的一篇博文:在三维模型上可视化标量场及注意方面。
现在考虑如何计算等值线上的间隔点。先看单个的三角面,无论如何可以得到一条等值线的。最简单的思想,就是把单个三角面上的等值线连接起来。
给定一个三角面,如何计算从该三角面出发的一条等值线了?
第一步,求出三个顶点的标量值顺序,那么标量范围在[fMin,fMax]之间。可以任选区间内的一个值,比如中间值作为该等值线的标量值。
第二步,得到等值线标量值在[fMin,fMax]所在边pEdge的位置ptBeg,作为起始点,加入点集合isoLine。
第三步,在pEdge所在的三角面上,找pEdge相邻的两条边的等值点(ptBeg的标量值相等的点)ptNext。如果存在这样的点,那么修改ptBeg = ptNext,加入点集合isoLine。同时,修改pEdge为对应的相邻边pEdge->next()或者是pEdge->next()->next()。如果不存在ptNext,进入第四步,同时设置不成环标志。否则,pEdge = pEdge->opposite(),(效果是进入相邻面,再循环第三步),如果发现ptNext等于isoLine的第一个点,那么说明已经成环,进入第四步,同时设置成环标志。
第四步,返回等值线,以及等值线是否成环状。
相关代码如下,里面使用了cgal的半边结构,同时该函数标记了当前面是否已经计算过等值线,避免等值线过密。
1 | bool CSEMesh::ComputeIsoLine(std::vector<double> scalarField, Enriched_Model::Facet_iterator pBegFacet, |
最后要讲的是如何遍历整个模型的三角面,使得计算出来的等值线有一定的规律?诚然,任意的顺序都能计算出等值线来。但是,会丢失一些信息。我用的广度优先搜索的顺序计算。我先找到最小的标量值,从这个顶点所在的一个面开始进行广度优先搜索遍历所有的面。同时,在队列里面保存进入队列的顺序,以进行下一步的等值线排序。最终,我得到的是一些根据标量值和进入队列顺序排序过的一堆等值线。相关代码如下:
1 | //计算所有等值线 |
等值线效果图: