四边形网格处理——沿Edge遍历 矩形域顶点提取

news/2025/2/10 7:44:22/

        记录最近遇到的一个问题和解决方案。

        最近遇到的问题:将一个五边形划分为四边形网格。

        参考文献《Closed -form Quadrangulation of n-Sided Patches》,划分方式如下图所示,实际上是在五边形中间添加了一个顶点,顶点分别向五边形的5条边各引出一条分割线,将五边形划分成了5个四边形区域,再将5个四边形区域分成更细小的四边形网格。

                ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        

        假如划分后的整体四边形网格已得到,如何得到分割的5块四边形区域网格,并将其顶点按照矩形域的形式(形如N行M列)排好呢?此问题可整理为:

输入: quadMesh -- 四边形网格;

            corners     -- 五边形5个角点的vid;

            sides        -- 各条边(side)的vid集合;

            mpoint      -- 中间的分界点;上图中的红点;

输出

            patchMesh  -- mpoint分割出来的5个四边形区域的顶点,顶点整理成N*M的形式

大致思路

          1. 遍历mpoint邻接的所有四边形;

          2. 对每一个四边形,找到与mpoint邻接的两个Edge;

          3. 分别沿这两条Edge向边的方向遍历,直到抵达五边形边界;

           四边形网格沿Edge遍历的大致思路,Edge首尾点分别为a,b,首先分别找到a,b邻接的所有面,取b邻接面的所有与b邻接的顶点,从这些顶点中再筛选出不属于a的邻接面的顶点,即沿Edge遍历的下一个顶点。

          4. 固定一条EdgeA为矩形域首行点,另一条EdgeB为矩形域首列点;那么矩形域顶点的行数为EdgeB的顶点数,矩形域顶点的列数为EdgeA的顶点数。

          5. 从第2行开始,根据前一行顶点位置,和当前行第一个顶点的位置,查找并填充当前行的所有顶点,直到所有行顶点填充完毕。

        这里需要实现已知面的三个顶点ID,搜索该面ID及余下一顶点的ID。搜索面ID的时候,从三个顶点的邻接面中搜索即可。

        代码使用C++\vcglib实现。

        代码基于quadwild-bimdf项目修改(GitHub - cgg-bern/quadwild-bimdf: QuadWild extended with BiMDF solver)。五边形mpoint的编号固定为3。

 //Get polymeshPolyMeshType quadrangulatedChartMesh;QuadRetopology::internal::eigenToVCG(quadrangulationV, quadrangulationF, quadrangulatedChartMesh, 4);vcg::tri::UpdateTopology<PolyMeshType>::FaceFace(quadrangulatedChartMesh);vcg::tri::UpdateTopology<PolyMeshType>::VertexFace(quadrangulatedChartMesh);vcg::tri::UpdateFlags<PolyMeshType>::VertexBorderFromFaceAdj(quadrangulatedChartMesh);auto getSideId = [&](int vid) {for (int i = 0; i < patchSides.size(); i++){for (int j = 0; j < patchSides[i].size(); j++){if (patchSides[i][j] == vid)return i;}}return -1;};auto getCornerId = [&](int vid) {for (int i = 0; i < patchCorners.size(); i++){if (patchCorners[i] == vid)return i;}return -1;};auto isEdgeInFace = [&](int vid1, int vid2, int fid) {auto* f = &quadrangulatedChartMesh.face[fid];for (int i = 0; i < f->VN(); i++){auto a = vcg::tri::Index(quadrangulatedChartMesh, f->V(i));auto b = vcg::tri::Index(quadrangulatedChartMesh, f->V((i + 1) % f->VN()));if (a == vid1 && b == vid2)return true;if (a == vid2 && b == vid1)return true;}return false;};// 在fid所在面中找到vid的两个相邻顶点auto findNeighborVertex = [&](int vid, int fid) {auto* f = &quadrangulatedChartMesh.face[fid];for (int i = 0; i < f->VN(); i++){auto iv = vcg::tri::Index(quadrangulatedChartMesh, f->V(i));if (iv == vid){auto a = vcg::tri::Index(quadrangulatedChartMesh, f->V((i - 1 + f->VN()) % f->VN()));auto b = vcg::tri::Index(quadrangulatedChartMesh, f->V((i + 1) % f->VN()));return std::make_pair(a, b);}}};auto rf = quadrangulatedChartMesh.face[0];auto findNextVF = [&](int last_vid, int cur_vid, int last_fid) {auto v_cur = &quadrangulatedChartMesh.vert[cur_vid];auto v_last = &quadrangulatedChartMesh.vert[last_vid];std::vector<decltype(rf)*> faceVec_cur;std::vector<int> index_cur;vcg::face::VFStarVF(v_cur, faceVec_cur, index_cur); // 获取所有与顶点相邻的面std::vector<decltype(rf)*> faceVec_last;std::vector<int> index_last;vcg::face::VFStarVF(v_last, faceVec_last, index_last); // 获取所有与顶点相邻的面int curr_dir = 0;for (unsigned int i = 0; i < faceVec_cur.size(); i++){decltype(rf)* curr_f = faceVec_cur[i]; // 当前面auto curr_f_id = vcg::tri::Index(quadrangulatedChartMesh, curr_f);if (curr_f_id == last_fid){continue;}auto pairv = findNeighborVertex(cur_vid, curr_f_id);bool findAdjFace = false;for (unsigned int j = 0; j < faceVec_last.size(); j++){decltype(rf)* tmpf = faceVec_last[j]; // 当前面auto tmpf_id = vcg::tri::Index(quadrangulatedChartMesh, tmpf);if (isEdgeInFace(pairv.first, cur_vid, tmpf_id)){findAdjFace = true;break;}}if (!findAdjFace)return std::make_pair((int)pairv.first, (int)curr_f_id);findAdjFace = false;for (unsigned int j = 0; j < faceVec_last.size(); j++){decltype(rf)* tmpf = faceVec_last[j]; // 当前面auto tmpf_id = vcg::tri::Index(quadrangulatedChartMesh, tmpf);if (isEdgeInFace(pairv.second, cur_vid, tmpf_id)){findAdjFace = true;break;}}if (!findAdjFace)return std::make_pair((int)pairv.second, (int)curr_f_id);}return std::make_pair(-1, -1);};// 3个点的顺序要与实际一致,中间不能有断开的点auto findFaceAndLastV = [&](int v[3]){for (int i = 0; i < 3; i++){std::vector<decltype(rf)*> faceVec;std::vector<int> index;vcg::face::VFStarVF(&quadrangulatedChartMesh.vert[v[i]], faceVec, index); // 获取所有与顶点相邻的面for (int j = 0; j < faceVec.size(); j++){auto f = faceVec[j];for (int k = 0; k < 4; k++){auto v1 = vcg::tri::Index(quadrangulatedChartMesh, f->V(k));auto v2 = vcg::tri::Index(quadrangulatedChartMesh, f->V((k + 1) % 4));auto v3 = vcg::tri::Index(quadrangulatedChartMesh, f->V((k + 2) % 4));if ((v1 == v[0] && v2 == v[1] && v3 == v[2]) || (v1 == v[2] && v2 == v[1] && v3 == v[0]))return std::make_pair(index[j], (int)vcg::tri::Index(quadrangulatedChartMesh, f->V((k + 3) % 4)));}}}return std::make_pair(-1, -1);};auto findPathToSide = [&](int mpoint_vid, int next_vid, int fid, int& sideId){std::vector<int> subPatchV;subPatchV.push_back(mpoint_vid);subPatchV.push_back(next_vid);auto lastVid = mpoint_vid;sideId = -1;bool mpoint_on_board = (getSideId(mpoint_vid) >= 0);if (mpoint_on_board){sideId = getSideId(mpoint_vid);}do {auto ret = findNextVF(lastVid, next_vid, fid);if (ret.first == -1)break;lastVid = next_vid;fid = ret.second;next_vid = ret.first;subPatchV.push_back(next_vid);sideId = getSideId(next_vid);} while ((sideId < 0 && (!mpoint_on_board)) || (mpoint_on_board && getCornerId(next_vid) < 0));return subPatchV;};//vcg::tri::UpdateEdges<PolyMeshType>::UpdateEdge(quadrangulatedChartMesh);if (chart.chartSides.size() == 5){auto* mpoint = &quadrangulatedChartMesh.vert[3];std::vector<decltype(rf)*> faceVec;std::vector<int> index;vcg::face::VFStarVF(mpoint, faceVec, index); // 获取所有与顶点相邻的面for (unsigned int i = 0; i < faceVec.size(); i++){auto lastVid = vcg::tri::Index(quadrangulatedChartMesh, mpoint);decltype(rf)* f = faceVec[i];auto curFid = vcg::tri::Index(quadrangulatedChartMesh, f);auto neighborV = findNeighborVertex(lastVid, curFid);int side_U = -1, side_V;std::vector<int> u_verts = findPathToSide(lastVid, neighborV.first, curFid, side_U);std::vector<int> v_verts = findPathToSide(lastVid, neighborV.second, curFid, side_V);std::vector<std::vector<int>> subPatchsV;subPatchsV.resize(v_verts.size());subPatchsV[0] = u_verts;for (int j = 1; j < v_verts.size(); j++){subPatchsV[j] = u_verts;int vids[3] = { v_verts[j], subPatchsV[j - 1][0], subPatchsV[j - 1][1] };auto f_v = findFaceAndLastV(vids);int side = -1;subPatchsV[j] = findPathToSide(v_verts[j], f_v.second, f_v.first, side);}}}

      


http://www.ppmy.cn/news/1570799.html

相关文章

代理软件更改IP地址会影响网速吗

在数字化时代&#xff0c;代理软件因其能够隐藏真实IP、突破地域限制、保护隐私安全等特性&#xff0c;而受到了广大用户的青睐。然而&#xff0c;许多用户在使用代理软件更改IP地址时&#xff0c;都会担心一个问题&#xff1a;这样的操作是否会影响网速&#xff1f;本文旨在深…

C#中的Frm_Welcome.Instance.Show(),是什么意思

Frm_Welcome.Instance.Show() 是一种常见的单例模式&#xff08;Singleton Pattern&#xff09;实现方式&#xff0c;通常用于在应用程序中确保某个窗体&#xff08;Form&#xff09;只有一个实例&#xff0c;并通过该实例显示窗体。以下是对这段代码的详细解释&#xff1a; 代…

高效 MyBatis SQL 写法一

高效 MyBatis SQL 写法一 前言 MyBatis 作为一款优秀的持久层框架&#xff0c;极大地简化了数据库操作。 然而&#xff0c;在实际开发中&#xff0c;XML 配置的编写仍然可能显得繁琐。 本文将分享一些 MyBatis 动态 SQL 的优质写法&#xff0c;帮助开发者提升效率并减少错误…

mongo命令执行js脚本的若干个示例

查询 示例1&#xff1a; echo "var versions db.version(); print(\"version is \" versions);" > 4.js mongo -u root -p pass --authenticationDatabase admin core 4.js示例2&#xff1a; echo "var versions db.version(); print(\"…

ISAAC Sim 1.1. Isaac Sim 4.2.0 环境配置

我使用的系统是ubuntu20.04&#xff0c;根据推荐使用Binary installation 1.下载和安装 1.1. Isaac Sim 参照Installation using Isaac Sim Binaries — Isaac Lab Documentation完成ISAAC SIM安装。 1.2. Isaac Lab “Isaac Lab 提供您需要的工具&#xff0c;以便您根据项…

浅谈Java和C#UDP通信

Java UDP高级用法示例 1. 多线程UDP服务器 import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;public class MultiThreadedUDPReceiver …

Flutter List 的 every 如果回调函数抛出异常 应该如何处理

在使用 List 的 every 方法时&#xff0c;如果回调函数抛出异常&#xff0c;可以通过以下几种方式进行处理&#xff1a; 1. 在回调函数内部捕获异常 在回调函数内部使用 try-catch 语句捕获可能抛出的异常&#xff0c;并根据具体情况进行处理。这样可以避免异常直接导致 ever…

【QT】 Qt经典面试题

文章目录 1. Qt 基础1.1 Qt 的核心特性1.2 Qt 的信号与槽机制1.3 Qt 的元对象系统 2. Qt GUI 编程2.1 Qt 的窗口部件2.2 Qt 的布局管理器 3. Qt 多线程编程3.1 QThread3.2 QtConcurrent 4. Qt 网络编程4.1 QTcpSocket 和 QTcpServer4.2 QNetworkAccessManager 5. Qt 数据库编程…