从源头看Dust3d | (三)Booleanmesh&Boundingboxmesh

2021SC@SDUSC

BooleanMesh 建立CGAL网格

(一)函数功能概述

函数功能概述

validatePosition

判断输入的位置是否为合法位置

buildCgalMesh

根据位置和索引建立CGAL网格结构

fetchFromCgalMesh

从网格结构中抓取顶点和面信息,放入vertices和faces中

isNullCgalMesh

判断网格结构是否为空

(二)CGAL库介绍

BooleanMesh建立CGAL网格借助了计算几何算法库CGAL实现。CGAL库是一个大型C + +库的几何数据结构和算法,可以对网格执行布尔操作。

BooleanMesh最主要使用的CGAL中的内容是其表面网格Surface_mesh类。Surface_mesh类是halfedge(半边数据结构)数据结构的一个实现,它用于表示多面体面。它是一个替代包Halfedge数据结构和三维多面体表面,并且Surface Mesh类是基于索引而不是基于指针的。

CGAL半边结构:Surface_mesh是一种以边为中心的数据结构,能够维护顶点、边和面的关联信息。每条边由两条方向相反的半边表示。每个半边存储对入射面和入射顶点的引用。此外,它将下一个和上一个半边入射的参照存储到其入射面。对于每个面和每个顶点,将存储一条入射半边。半边不存储相对半边的索引,因为Surface_mesh在内存中连续存储相对半边。

 上图为CGAL半边结构图示与参与的函数

(三)具体函数分析

     1.validatePosition函数

        validatePosition函数判断输入参数position是否为合法位置,通过isnan函数分别判断position的x、y、z是否为数字,是数字,则返回 false,否则返回 true;isinf函数判断position的x、y、z是否为无限大,若无限大,则返回true,否则返回false。

        因此只要position中的坐标值有一个分量是无限大的或者不是数字的,函数就会返回false,表明position不合法。

//验证位置,判断position是否合法
//isnan函数判断指定参数是否为数字,是数字,返回 false,否则返回 true;isinf判断数据是否为无限大,若无限大,返回1,否则返回0
inline bool validatePosition(const QVector3D &position)
{
    if (std::isnan(position.x()))
        return false;
    if (std::isnan(position.y()))
        return false;
    if (std::isnan(position.z()))
        return false;
    if (std::isinf(position.x()))
        return false;
    if (std::isinf(position.y()))
        return false;
    if (std::isinf(position.z()))
        return false;
    return true;
}

      2.buildCgalMesh函数

        buildCgalMesh函数用于建立CGAL网格。函数首先判断了用于建立面的每个顶点的合法性,一旦出现不合法的顶点,则面的合法性变为false,建立就停止;如果顶点合法,就将此顶点信息建立到existedKeys、positionKeys、positionsInKeys中,以便后续使用。

        对面中的点进行遍历,如果最终面的合法性为true且positionKeys中超过三个元素(至少三个点才能确定一个面),那么就将顶点和此面加入到网格结构mesh中。

//建立网格mesh,返回值是网格
template <class Kernel>
typename CGAL::Surface_mesh<typename Kernel::Point_3> *buildCgalMesh(const std::vector<QVector3D> &positions, const std::vector<std::vector<size_t>> &indices)
{
    typename CGAL::Surface_mesh<typename Kernel::Point_3> *mesh = new typename CGAL::Surface_mesh<typename Kernel::Point_3>;
    std::map<PositionKey, typename CGAL::Surface_mesh<typename Kernel::Point_3>::Vertex_index> vertexIndices;
    for (const auto &face: indices) {
        std::vector<typename CGAL::Surface_mesh<typename Kernel::Point_3>::Vertex_index> faceVertexIndices;
        bool faceValid = true;
        std::vector<PositionKey> positionKeys;
        std::vector<QVector3D> positionsInKeys;
        std::set<PositionKey> existedKeys;
        for (const auto &index: face) {
            const auto &position = positions[index];
            //position是positions[index]
            if (!validatePosition(position)) {
                faceValid = false;
                break;
            }
            //如果position是有效的点
            //看positionkey.h
            auto positionKey = PositionKey(position);
            if (existedKeys.find(positionKey) != existedKeys.end()) {
                continue;
            }
            //如果在existedKeys中没有positionKey,则将其信息分别插入
            existedKeys.insert(positionKey);
            positionKeys.push_back(positionKey);
            positionsInKeys.push_back(position);
        }
        if (!faceValid)
            continue;
        if (positionKeys.size() < 3)
            continue;
        //如果facevalid=true且positionKeys中超过三个元素(至少三个点一个面)
        for (size_t index = 0; index < positionKeys.size(); ++index) {
            const auto &position = positionsInKeys[index];
            const auto &positionKey = positionKeys[index];
            auto findIndex = vertexIndices.find(positionKey);
            //如果positionKey在vertexIndices中
            if (findIndex != vertexIndices.end()) {
                faceVertexIndices.push_back(findIndex->second);//findIndex的second是它的索引
            } 
            //如果positionKey不在vertexIndices中,向mesh中加入顶点,并在vertexIndices和faceVertexIndices中进行同步
            else {
                auto newIndex = mesh->add_vertex(typename Kernel::Point_3(position.x(), position.y(), position.z()));
                vertexIndices.insert({positionKey, newIndex});
                faceVertexIndices.push_back(newIndex);
            }
        }
        mesh->add_face(faceVertexIndices);
    }
    return mesh;
}

      3.fetchFromCgalMesh函数

        fetchFromCgalMesh函数根据参数mesh网格中的信息,将顶点和面的信息提取出来放入vertices和faces中。

        顶点信息的提取是通过对网格中的点一一进行遍历,取出网格中的点及它们的坐标值进行的。

        面的信息的提取是通过面迭代器faceIt、顶点迭代器vbegin, vend对半边结构进行遍历确定面的信息来进行的。

/从网格中抓取信息,对vertices和faces进行填充
template <class Kernel>
void fetchFromCgalMesh(typename CGAL::Surface_mesh<typename Kernel::Point_3> *mesh, std::vector<QVector3D> &vertices, std::vector<std::vector<size_t>> &faces)
{
    std::map<typename CGAL::Surface_mesh<typename Kernel::Point_3>::Vertex_index, size_t> vertexIndicesMap;
    //将mesh中的顶点加入vertices中,并对vertexIndicesMap进行赋值
    for (auto vertexIt = mesh->vertices_begin(); vertexIt != mesh->vertices_end(); vertexIt++) {
        auto point = mesh->point(*vertexIt);
        float x = (float)CGAL::to_double(point.x());
        float y = (float)CGAL::to_double(point.y());
        float z = (float)CGAL::to_double(point.z());
        vertexIndicesMap[*vertexIt] = vertices.size();
        vertices.push_back(QVector3D(x, y, z));
    }

    //faceIndices中存的是面的顶点,然后再存入faces中
    typename CGAL::Surface_mesh<typename Kernel::Point_3>::Face_range faceRage = mesh->faces();
    //mesh->face的迭代器,面迭代器类型是面范围的嵌套类型
    typename CGAL::Surface_mesh<typename Kernel::Point_3>::Face_range::iterator faceIt;
    //tie函数用于方便地将半边结构的两个顶点赋给vbegin和vend(简化语法)
    for (faceIt = faceRage.begin(); faceIt != faceRage.end(); faceIt++) {
        CGAL::Vertex_around_face_iterator<typename CGAL::Surface_mesh<typename Kernel::Point_3>> vbegin, vend;
        std::vector<size_t> faceIndices;
        for (boost::tie(vbegin, vend) = CGAL::vertices_around_face(mesh->halfedge(*faceIt), *mesh);
                vbegin != vend;
                ++vbegin){
            faceIndices.push_back(vertexIndicesMap[*vbegin]);
        }
        faces.push_back(faceIndices);
    }
}

      4.isNullCgalMesh函数

        isNullCgalMesh函数判断参数mesh网格结构是否为空。通过对面的迭代器进行访问,根据起始是否等于终止来判断网格结构是否为空,如果相等,则为空;如果不相等,则不为空。

emplate <class Kernel>
bool isNullCgalMesh(typename CGAL::Surface_mesh<typename Kernel::Point_3> *mesh)
{
    typename CGAL::Surface_mesh<typename Kernel::Point_3>::Face_range faceRage = mesh->faces();
    return faceRage.begin() == faceRage.end();
}

补充:PositionKey类

class PositionKey
{
public:
    PositionKey(const QVector3D &v);
    PositionKey(float x, float y, float z);
    const QVector3D &position() const;
    bool operator <(const PositionKey &right) const;
    bool operator ==(const PositionKey &right) const;

private:
    long m_intX = 0;
    long m_intY = 0;
    long m_intZ = 0;
    QVector3D m_position;

    static long m_toIntFactor;
};
PositionKey类的函数与变量
构造函数PositionKey(const QVector3D &v);
PositionKey(float x, float y, float z);
返回m_positionconst QVector3D &position() const;
运算符重载函数 bool operator <(const PositionKey &right) const;
 bool operator ==(const PositionKey &right) const;
位置信息m_intX、m_intY、m_intZ、m_position
转换位置的因子m_toIntFactor

      1.构造函数

     //两个构造函数
PositionKey::PositionKey(const QVector3D &v) :
    PositionKey(v.x(), v.y(), v.z())
{
}

PositionKey::PositionKey(float x, float y, float z)
{
    m_position.setX(x);
    m_position.setY(y);
    m_position.setZ(z);
    //m_position=(x,y,z)
    
    m_intX = x * m_toIntFactor;
    m_intY = y * m_toIntFactor;
    m_intZ = z * m_toIntFactor;
}

      2.运算符重载函数

        ‘<’的运算符重载实现的是两个PositionKey类的变量进行比较时,以x->y->z的顺序进行比较

       ‘=’ 的运算符重载实现当两个PositionKey类的变量进行比较时,只有x、y、z都相等时,才return true

bool PositionKey::operator <(const PositionKey &right) const
{
    if (m_intX < right.m_intX)
        return true;
    if (m_intX > right.m_intX)
        return false;
    if (m_intY < right.m_intY)
        return true;
    if (m_intY > right.m_intY)
        return false;
    if (m_intZ < right.m_intZ)
        return true;
    if (m_intZ > right.m_intZ)
        return false;
    return false;
}

bool PositionKey::operator ==(const PositionKey &right) const
{
    return m_intX == right.m_intX &&
        m_intY == right.m_intY &&
        m_intZ == right.m_intZ;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Dust-me selectors是一种用于检测网页中未使用的CSS选择器的工具。在网页设计和开发中,开发人员经常会使用CSS来定义网页的样式和布局。然而,由于项目开发过程中的迭代和修改,有时候会导致一些CSS选择器未被使用,这会增加页面的加载时间和浏览器的渲染压力。 Dust-me selectors可以自动分析网页中未使用的CSS选择器,并生成一个报告,列出这些未使用的选择器。开发人员可以通过该报告来优化他们的CSS代码,删除未使用的选择器,从而提高网页的性能和加载速度。 Dust-me selectors的使用非常简单。用户只需要安装该工具的浏览器插件,然后在浏览器的工具栏上点击插件图标,选择要分析的网页。工具会在后台自动运行,并生成一个报告页面。报告以表格的形式列出了所有的CSS选择器,包括选择器名称和该选择器是否被使用。 开发人员可以根据报告中列出的未使用选择器,逐一检查并处理。他们可以删除未使用的选择器,或者将其与其他相关的选择器合并,以减少CSS文件的大小和复杂性。通过优化CSS代码,可以减少浏览器的样式解析时间,提高网页的加载速度和性能。 总之,Dust-me selectors是一款非常有用的工具,可以帮助开发人员优化CSS代码,提高网页的性能。它能够自动分析网页中未使用的CSS选择器,并生成相应的报告,让开发人员能够直观地了解和处理这些未使用的选择器。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值