点和三角形之间的链接

Link between points and triangles

本文关键字:链接 之间 三角形      更新时间:2023-10-16

我想在两个类之间建立一个链接,但我不确定什么是最好的方法。我有一组点,我使用delaunay三角测量来查找它们之间的三角形(注意,一个点可以属于几个三角形)。接下来,在视频的几个帧中跟踪和更新这些点的位置。因此,三角形的位置也需要以某种方式更新。除此之外,我还希望能够删除丢失的点,并自动删除与之相关的三角形。你们能给我一些如何组合这些类的建议吗?

class Point  
{ 
  float x,y;
}
class Triangle
{
  Point p1, pt2, pt3;
}

计算机图形中的此类集合交互通常使用索引来实现。

class Point
{ 
  float x,y;
}
class Triangle
{
  unsigned int indices[3]; // indices in std::vector<Points>
}
std::vector<Points> points;
std::vector<Triangle> triangles;

和指针解决方案相比,主要的优点是可以在着色器程序中使用这样的索引引用。

编辑:通过已知索引(未测试)删除顶点的代码示例。请注意,现在我们将顶点存储在std::map中,以便在擦除索引时不破坏索引。

class Mesh
{
public:
    struct Vertex
    {
        float x, y;
    };
    struct Triangle
    {
        bool Contains(unsigned int index) const 
        { 
            return ((indices[0] == index) || (indices[1] == index) || (indices[2] == index));
        }
        unsigned int indices[3];
    };
    // Removes vertex and corresponding triangles
    void RemoveByIndex(unsigned int index)
    {
        for (auto it = triangles.begin(); it != triangles.end(); ++it)
        {
            if (it->Contains(index))
                triangles.erase(it);
        }
        vertices.erase(index);
    }
private:
    std::map<unsigned int, Vertex> vertices;
    std::vector<Triangle> triangles;
};

当然,还有很多优化空间:首先是容器的类型,但这取决于集合的大小,以及插入/擦除数据的频率。

@slava指的是一个点可以属于几个三角形的事实。考虑到这一点,你的课程应该看起来像:

class Point  
{ 
  float x,y;
}
class Triangle
{
  Point * p1;
  Point * pt2;
  Point * pt3;
}

按照你定义三角形类的方式,你会随身携带点的副本。如果修改了这些点,则必须单独更新Triangle类。

请记住,这些不是字面上的类定义(所有内容都是私有的),并且您可能希望使用引用计数指针,如unique_ptr

您可能会使用以下数据结构:

struct Point
{
    double x, y;
};
struct Triangle
{
    unsigned int ids[3];
    bool isValid;
};
// Store points for each frame
std::vector<std::vector<Point>> points;
// Store triangles
std::vector<Triangle> triangles;

你也可以为每个帧保留所有三角形,如下所示:

// Store triangles for each frame
std::vector<std::vector<Triangle>> triangles;

这是一个"工作示例"。在3帧期间,4个点、2个三角形。它只输出顶点全部为正的三角形。从一帧到另一帧,点云将沿Y轴平移-1。这显然是一个假测试,但希望它能帮助你开始。

#include <vector>
#include <iostream>
struct Point
{
    double x, y;
};
struct Triangle
{
    unsigned int ids[3];
    bool isValid;
};
void TrackFirstFrame(std::vector<Point> &firstFrame)
{
    Point p1, p2, p3, p4;
    p1.x = 1; p1.y = 0;
    p2.x = 2; p2.y = 1;
    p3.x = 1; p3.y = 2;
    p4.x = 0; p4.y = 1;
    firstFrame[0] = p1;
    firstFrame[1] = p2;
    firstFrame[2] = p3;
    firstFrame[3] = p4;
}
void Delaunay(const std::vector<Point> &points, std::vector<Triangle> &triangles)
{
    Triangle t1;
    t1.ids[0] = 0;
    t1.ids[1] = 1;
    t1.ids[2] = 3;
    triangles.push_back(t1);
    Triangle t2;
    t2.ids[0] = 1;
    t2.ids[1] = 2;
    t2.ids[2] = 3;
    triangles.push_back(t2);
}
// Assumption: all previous frame points give a new tracked point for current frame 
void TrackFrame(const std::vector<Point> &previousFramePoints, unsigned int currentFrame, std::vector<Point> &trackedPoints)
{
    for (unsigned int i = 0; i < previousFramePoints.size(); ++i)
    {
        Point previousPoint = previousFramePoints[i];
        Point trackedPoint;
        trackedPoint.x = previousPoint.x;
        trackedPoint.y = previousPoint.y - 1;
        trackedPoints[i] = trackedPoint;
    }
}
// Assumption: all vertices are positive. If not, triangle is invalid
void UpdateTriangles(const std::vector<Point> &points, std::vector<Triangle> &triangles)
{
    std::vector<Triangle>::iterator trianglesIT = triangles.begin();
    for (; trianglesIT != triangles.end(); ++trianglesIT)
    {
        (*trianglesIT).isValid = true; // By default
        for (unsigned int i = 0; i < 3; ++i)
        {
            Point vertex = points[(*trianglesIT).ids[i]];
            if (vertex.x < 0 || vertex.y < 0)
            {
                (*trianglesIT).isValid = false;
                break;
            }
        }
    }
}
void PrintPoints(const std::vector<Point> &points)
{
    std::cout<<"Points"<<std::endl;
    std::vector<Point>::const_iterator pointsIT = points.begin();
    for (; pointsIT != points.end(); ++pointsIT)
    {
        std::cout<<"("<<pointsIT->x<<", "<<pointsIT->y<<")"<<std::endl;
    }
}
void PrintTriangles(const std::vector<Triangle> &triangles)
{
    std::cout<<"Triangles"<<std::endl;
    std::vector<Triangle>::const_iterator trianglesIT = triangles.begin();
    for (; trianglesIT != triangles.end(); ++trianglesIT)
    {
        if (trianglesIT->isValid)
        {
            std::cout<<"["<<trianglesIT->ids[0]<<", "<<trianglesIT->ids[1]<<", "<<trianglesIT->ids[2]<<"])"<<std::endl;
        }
    }
}
int main()
{
    unsigned int nbFrames = 3;
    unsigned int nbPoints = 4;
    // Init 2D points
    std::vector<std::vector<Point>> points;
    points.resize(nbFrames);
    std::vector< std::vector<Point> >::iterator framesIT = points.begin();
    for (; framesIT != points.end(); ++framesIT)
    {
        framesIT->resize(nbPoints);
    }
    TrackFirstFrame(points[0]);
    std::cout<<"Frame 0"<<std::endl;
    PrintPoints(points[0]);
    // Init triangles with Delaunay. 4 points => 2 triangles;
    std::vector<Triangle> triangles;
    Delaunay(points[0], triangles);
    PrintTriangles(triangles);
    for (unsigned int i = 1; i < nbFrames; ++i)
    { 
        std::cout<<"Track frame #"<<i<<std::endl;
        TrackFrame(points[i-1], i, points[i]);
        PrintPoints(points[i]);
        UpdateTriangles(points[i], triangles);
        PrintTriangles(triangles);
    }
    char c;
    std::cin >> c;
}

三角形不应包含点,而应包含指向点的指针,因为点存在于三角形之外。此外,在每个点中保留相关三角形的列表(当然是指针的列表)可能很方便,但这实际上取决于使用情况。

我认为您需要一些东西来删除(或添加)三角形。我的代码有点像

std::vector<Triangle> update_triangles(const std::vector<Point> & points)
{
//...
}

此代码将重新查找新点集合的delaunay三角形。这可能会被证明是缓慢的,并且可能有一些聪明的算法可以在以后用来加快速度。