三角网格半边数据结构设计与实现(转)
功能
读取obj三角网格文件,将数据转化成半边数据结构。
将半边数据结构写入obj文件。
给定一个三角面,找到3个点按逆时针排列。
给定1个点,找到与该点相邻的面。
给定1个点,找到从该点出发的边。
给定1个点,找到进入到该点的边。
给定1个点,找到与该点相邻的点。
给定1条边,找到与该边相邻的面。
给定1个面及其1边,找到与该边相邻的另一面(如果有)。
判断点是否在边界点。
判断边是否为边界。
数据结构设计及功能算法
半边数据结构中的一条边分成2个方向,分别属于被边分割的2个部分。
每条边都有自己的后继和前驱,从一条边开始遍历后继,最后可以形成一个环。
上图中箭头指向的边就是下一条边,反之是上一条边。
每个三角形内部会形成循环,最外部的边界所有边也会形成循环。如果内部有循环说明有空洞。
数据结构定义
// 点定义 class MVert { private: int index_;// 编号, 从0开始 MPoint3 point_; // 坐标 MHalfedge* he_; // 从该点出发的一条半边,尽量设置成边界边(如果有) }; // 半边结构 class MHalfedge { private: int index_; // 编号 MVert* v_; // 半边起点 MEdge* e_; // 半边对应的全边 MPolyFace* poly_face_; // 半边对应的三角面 MHalfedge* next_, * prev_; // 后继和前驱 MHalfedge* pair_; // 对应的另一条半边 }; // 全边结构 class MEdge { private: int index_; // 编号 MVert* v1_; MVert* v2_; // 对应2点 MHalfedge* he_; // 其中一条半边 }; // 三角面定义 class MPolyFace { private: int index_; // 编号 MHalfedge* he_begin_; // 开始半边 }; // 物体实体定义 class PolyMesh { private: std::vector<MHalfedge *> half_edges_; std::vector<MVert *> vertices_; std::vector<MEdge *> edges_; std::vector<MPolyFace *> polygons_; };
算法过程
读取obj三角网格文件,将数据转化成半边数据结构。
读入点入到数组里。
对于每个面,读入面的三个点。
构建出三条边,如果已经构建则复用。
三条边初始后继和前驱都是自己。
建立边与面的关系。
调整边的前驱和后继关系。返回步骤2.
上述过程比较复杂的是调整边的前驱和后继关系。下面以图示说明。每次调整的是三角形2条相邻边的关系。
给定1个点,找到进入到该点的边。 同上,先找到第1条边he = v.he.pair, 下一条边是he.next.pair,如此反复找到所有边。 给定1个点,找到与该点相邻的点。 同上,找到所进入的边,边的起点就是相邻点。 给定1条边,找到与该边相邻的面。 找出2条半边he, he.pair, 半边邻接的面就是与该边相邻的面。 给定1个面及其1边,找到与该边相邻的另一面(如果有)。 找出边的2个半边,判断哪边与现有面连接,另一条相邻的面就是所求的面。 判断点是否在边界点。 扫描从该点出发的所有边,判断是否存在一条边没有面连接。 判断边是否为边界。 是否有邻接面。
代码实现
https://github.com/LightningBilly/ACMAlgorithms/tree/master/图形学算法/半边数据结构/glTriangle/
// // Created by chenbinbin on 2022/3/30. // #include "MPoint3.h" #include "MVector3.h" #ifndef GLTRIANGLE_POLYMESH_H #define GLTRIANGLE_POLYMESH_H class MVert; class MEdge; class MHalfedge; class MPolyFace; // 点定义 class MVert { private: int index_;// 编号, 从0开始 MPoint3 point_; // 坐标 MHalfedge* he_; // 从该点出发的一条半边,尽量设置成边界边(如果有) public: MVert() : index_(-1), point_(0, 0, 0), he_(nullptr){} MVert(double x, double y, double z) : index_(-1), point_(x, y, z), he_(nullptr) {} ~MVert() { index_ = -1; } public: MHalfedge* const halfEdge() { return he_; } const MHalfedge* const halfEdge() const { return he_; } void setHalfedge(MHalfedge* he) { he_ = he; } void setPosition(MPoint3 new_point) { point_ = new_point; } void setPosition(double x, double y, double z) { point_ = MPoint3(x, y, z); } MPoint3 position() { return point_; } const MPoint3& position() const { return point_; } double x() const { return point_.x(); } double y() const { return point_.y(); } double z() const { return point_.z(); } int index() const { return index_; } void set_index(int index) { index_ = index; } bool isIsolated() const { return he_ == nullptr; } //Try to set the half of the point as the boundary half. Be sure call it before adding faces by yourself. void adjustOutgoingHalfedge(); }; // 半边结构 class MHalfedge { private: int index_; // 编号 MVert* v_; // 半边起点 MEdge* e_; // 半边对应的全边 MPolyFace* poly_face_; // 半边对应的三角面 MHalfedge* next_, * prev_; // 后继和前驱 MHalfedge* pair_; // 对应的另一条半边 public: MHalfedge() : index_(-1), next_(nullptr), prev_(nullptr), pair_(nullptr), v_(nullptr), e_(nullptr), poly_face_(nullptr){} MHalfedge(MHalfedge* next, MHalfedge* prev, MHalfedge* pair, MVert* v, MEdge* e, MPolyFace* p) : index_(-1), next_(next), prev_(prev), pair_(pair), v_(v), e_(e), poly_face_(p){} ~MHalfedge() { index_ = -1; }; public: MHalfedge* const next() { return next_; } MHalfedge* const prev() { return prev_; } MHalfedge* const pair() { return pair_; } MVert* const fromVertex() { return v_; } MVert* const toVertex() { return next()->fromVertex(); } MEdge* const edge() { return e_; } MPolyFace* const polygon() { return poly_face_; } MHalfedge* const rotateNext() { return pair()->next(); } MHalfedge* const rotatePrev() { return prev()->pair(); } const MHalfedge* const next() const { return next_; } const MHalfedge* const prev() const { return prev_; } const MHalfedge* const pair() const { return pair_; } const MVert* const fromVertex() const { return v_; } const MVert* const toVertex() const { return next()->fromVertex(); } const MEdge* const edge() const { return e_; } const MPolyFace* const polygon() const { return poly_face_; } void setNext(MHalfedge* next) { next_ = next; } void setPrev(MHalfedge* prev) { prev_ = prev; } void setPair(MHalfedge* pair) { pair_ = pair; } void setVert(MVert* vert) { v_ = vert; } void setEdge(MEdge* edge) { e_ = edge; } void setPolygon(MPolyFace* poly) { poly_face_ = poly; } bool isBoundary() const { return poly_face_ == nullptr; } int index() { return index_; } //int edge_index() { return index_ / 2; } void set_index(int index) { index_ = index; } }; // 全边结构 class MEdge { private: int index_; // 编号 MVert* v1_; MVert* v2_; // 对应2点 MHalfedge* he_; // 其中一条半边 public: MEdge() : index_(-1), v1_(nullptr), v2_(nullptr), he_(nullptr) {} MEdge(MVert* v1, MVert* v2) : index_(-1), v1_(v1), v2_(v2), he_(nullptr){} MEdge(MVert* v1, MVert* v2, MHalfedge* he) : index_(-1), v1_(v1), v2_(v2), he_(he) {} ~MEdge() { index_ = -1; }; public: MHalfedge* const halfEdge() { return he_; } const MHalfedge* const halfEdge() const { return const_cast<MEdge*>(this)->halfEdge(); } void setHalfedge(MHalfedge* he) { he_ = he; } void setVert(MVert* v1, MVert* v2) { v1_ = v1; v2_ = v2; } void updateVert() { v1_ = he_->fromVertex(), v2_ = he_->toVertex(); } int index() const { return index_; } void set_index(int index) { index_ = index; } ///get Vertex of the edge, the 0 is the first, the 1 is the second, the return is not orderd; MVert* getVert(int edge_v) { updateVert(); if (edge_v == 0) return v1_; else if (edge_v == 1) return v2_; else return nullptr; } const MVert* getVert(int edge_v) const { return const_cast<MEdge*>(this)->getVert(edge_v); } double length() { updateVert(); MVector3 t = v1_->position() - v2_->position(); return t.norm(); } MPoint3 getCenter() { updateVert(); return v1_->position() * 0.5 + v2_->position() * 0.5; } //MPoint3 interpolate(const double& t) //{ // updateVert(); // return v1_->position() * t + v2_->position() * (1 - t); //} /get the direction of the edge, from v1 to v2; //MVector3 tangent() const //{ // MVector3 t = v2_->position() - v1_->position(); // t.normalize(); // return t; //} }; // 三角面定义 class MPolyFace { private: int index_; // 编号 MHalfedge* he_begin_; // 开始半边 public: MPolyFace() : index_(-1), he_begin_(nullptr){} MPolyFace(MHalfedge* he) : index_(-1), he_begin_(he){} ~MPolyFace() { index_ = -1; } public: MHalfedge* const halfEdge() { return he_begin_; } const MHalfedge* const halfEdge() const { return const_cast<MPolyFace*>(this)->halfEdge(); } void setHalfedge(MHalfedge* he) { he_begin_ = he; } int index() const { return index_; } void set_index(int index) { index_ = index; } MPoint3 getFaceCenter(); }; class PolyMesh { private: std::vector<MHalfedge *> half_edges_; std::vector<MVert *> vertices_; std::vector<MEdge *> edges_; std::vector<MPolyFace *> polygons_; public: PolyMesh() {}; ~PolyMesh(){}; public: std::vector<MVert *> polygonVertices(MPolyFace *face) const; std::vector<MPolyFace *> vertAdjacentPolygon(MVert *vert) const; std::vector<MHalfedge *> vertAdjacentHalfEdge(MVert *vert) const; // 出发的边 std::vector<MHalfedge *> vertAdjacentReverseHalfEdge(MVert *vert) const; // 进入的边 std::vector<MVert *> vertAdjacentVertices(MVert *vert) const; std::vector<MPolyFace *> edgeAdjacentPolygon(MEdge *edge) const; MPolyFace * edgeAdjacentAnotherPolygon(MEdge *edge, MPolyFace *face) const; bool isBoundary(MVert *vert) const; bool isBoundary(const MEdge *edge) const; bool isBoundary(const MHalfedge *halfedge) const; MVert* addVertex(double x, double y, double z); MPolyFace* addPolyFace(std::vector<size_t>& v_loop_id); MPolyFace* addPolyFace(std::vector<MVert*>& v_loop); MEdge* edgeBetween(MVert* v0, MVert* v1); MEdge* addEdge(MVert* v_begin, MVert* v_end); MEdge* newEdge(MVert* v1, MVert* v2); MHalfedge* newHelfEdge(); MPolyFace* newPolyFace(); size_t numVertices() const { return vertices_.size(); } size_t numPolygons() const { return polygons_.size(); } MVert *vert(const size_t id) { return (id < numVertices() ? vertices_[id] : nullptr); } MPolyFace *polyface(const size_t id) { return (id < numPolygons() ? polygons_[id] : nullptr); } }; #endif //GLTRIANGLE_POLYMESH_H // // Created by chenbinbin on 2022/4/3. // #include "PolyMesh.h" #ifndef GLTRIANGLE_IOMANAGER_H #define GLTRIANGLE_IOMANAGER_H class OBJReader { public: OBJReader() {}; ~OBJReader() {}; bool read(const std::string &_filename, PolyMesh *mesh); private: bool loadMeshFromOBJ(std::istream &_in, PolyMesh *mesh); std::string path_; }; class OBJWriter { public: OBJWriter() {}; ~OBJWriter() {}; bool write(const std::string &, PolyMesh *mesh, std::streamsize _precision = 6) const; private: mutable std::string path_; mutable std::string objName_; bool write(std::ostream &, PolyMesh *mesh, std::streamsize _precision = 6) const; }; #endif //GLTRIANGLE_IOMANAGER_H
————————————————
版权声明:本文为CSDN博主「闪电彬彬」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/chenbb1989/article/details/123774576
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」