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

embedded/2025/2/12 3:38:52/

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

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

        参考文献《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/embedded/161504.html

相关文章

使用Redis实现业务信息缓存(缓存详解,缓存更新策略,缓存三大问题)

一、什么是缓存? 缓存是一种高效的数据存储方式,它通过将数据保存在内存中来提供快速的读写访问。这种机制特别适用于需要高速数据访问的应用场景,如网站、应用程序和服务。在处理大量数据和高并发请求时, 缓存能显著提高性能和用户体验。 Redis就是一款常用的缓存中间件。…

工作案例 - python绘制excell表中RSRP列的CDF图

什么是CDF图 CDF&#xff08;Cumulative Distribution Function&#xff09;就是累积分布函数&#xff0c;是概率密度函数的积分。CDF函数是一个在0到1之间的函数&#xff0c;描述了随机变量小于或等于一个特定值的概率。在可视化方面&#xff0c;CDF图表明了一个随机变量X小于…

0073.基于springboot的蜗牛兼职网的设计与实现+论文

一、系统说明 基于springbootvue的蜗牛兼职网的设计与实现,系统功能齐全, 代码简洁易懂&#xff0c;适合小白学编程。 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;蜗牛兼职…

【蓝桥杯嵌入式】2_LED

全部代码网盘自取 链接&#xff1a;https://pan.baidu.com/s/1PX2NCQxnADxYBQx5CsOgPA?pwd3ii2 提取码&#xff1a;3ii2 1、电路图 74HC573是八位锁存器&#xff0c;当控制端LE脚为高电平时&#xff0c;芯片“导通”&#xff0c;LE为低电平时芯片“截止”即将输出状态“锁存”…

0 CAD开源内核 Truck

Truck是一个基于Rust编写的开源CAD内核&#xff0c;专注于高性能、安全性和模块化设计&#xff0c;适用于寻求高效、可靠CAD解决方案的开发者和企业。开源地址&#xff1a;https://github.com/ricosjp/truck 一、Truck CAD 内核概述 项目背景&#xff1a; Truck是一个创新的C…

springboot 事务管理

在Spring Boot中&#xff0c;事务管理是通过Spring框架的事务管理模块来实现的。Spring提供了声明式事务管理和编程式事务管理两种方式。通常&#xff0c;我们使用声明式事务管理&#xff0c;因为它更简洁且易于维护。 1. 声明式事务管理 声明式事务管理是通过注解来实现的。…

软件工程-软件需求分析基础

基本任务 准确地回答“系统必须做什么&#xff1f;”&#xff0c;也就是对目标系统提出完整、准确、清晰、具体的要求 目标是&#xff0c;在分析阶段结束之前&#xff0c;系统分析员应该写出软件需求规格说明书&#xff0c;以书面形式准确地描述软件需求。 准则 1&#xff…

c++初始

目录 一数据类型 1. 2.sizeof 3.布尔 4.字符串类型 二.数据输入与输出 1.输出 2.输入 三.运算 1.加减乘除取模&#xff0c;&#xff0c;--都一样 2.逻辑非与或&#xff0c;与C语言一样 3.比较运算符&#xff0c;与C语言一样 4.三目运算符&#xff08;与C语言一样&a…