三角网格半边数据结构设计与实现(转)

功能
读取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

posted @   格美格美  阅读(401)  评论(0编辑  收藏  举报
编辑推荐:
· 从 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 热点速览」
点击右上角即可分享
微信分享提示