【计算几何】一、编程表示基本元素-01-点和点云(C、C++、python)

前言

始于2024年1月14日,我打算从0,从三种语言的方式,实现计算几何的相关知识,如凸包、向量、三角剖分、线性规划、 kd-树一直到计算机图形学,即使用计算机语言描述几何学的知识。

点是基本元素。

如何表示一个点,用C的数组和结构体,C++的vector或者类,python的numpy array。

总结一下就是:

double point[3] = {0, 0, 1};C的数组

struct Vertex vertex = {12, 1, 10};C的结构体

std::vector<std::array<double, 3>> points;C++的vector,以C++的数组为类型

std::vector<Vertex> vertex;C++的vector,以C的结构体为类型

C++的类,核心属性就是点云:std::vector<Vertex> vertex

class MyPoint
{
public:
    MyPoint(/* args */);
    ~MyPoint();

    std::vector<Vertex> points;//点云
};

C 使用数组表示点

使用一维数组存储一个点,多维数组存多个点

单个点: double point[3] = {0, 0, 1};

三个点:double pointss[3][3] = {
        {1.0, 2.0, 3.0},
        {4.0, 5.0, 6.0},
        {7.0, 8.0, 9.0}};

数组有个缺点就是容量是固定不变的,这就为使用C++的vector或者类做铺垫。

#include <stdio.h>
int main()
{
    //---------------------------one point----------------------------
    // array point
    double point[3] = {0, 0, 1};

    printf("X=%lf, Y=%lf, Z=%lf, nice to meet you!\n", point[0], point[1], point[2]);

    //---------------------------more point----------------------------
    // more array point
    double pointss[3][3] = {
        {1.0, 2.0, 3.0},
        {4.0, 5.0, 6.0},
        {7.0, 8.0, 9.0}};

    // 获取数组的行数和列数
    int rows = sizeof(pointss) / sizeof(pointss[0]);
    int cols = sizeof(pointss[0]) / sizeof(pointss[0][0]);

    return 0;
}

C 使用struct表示点

经安哥点拨,在视频中是这样表示角点的

【48】C++的stdvector使用优化_哔哩哔哩_bilibili

简化一下就是

struct Vertex

{

    float x, y, z;

};



int main()

{

struct Vertex vertex = {12, 1, 10};

printf("X=%lf, Y=%lf, Z=%lf, struct Vertex!\n", vertex.x, vertex.y, vertex.z);

return 0;

}

C++ 使用vector表示点

在容器里装一堆 一维数组,表示点云

std::vector<std::array<double, 3>> points;

或者在vector中装上文的C结构体

std::vector<Vertex> vertexList;

为什么用vector?

因为vector容量大小可变,而数组不可变,并且vector被C++封装了各种遍历、排序、增删改的功能,方便使用。

为什么不用C的数组作为类型
因为:这边初始Vector不设置死他的容量,否则不如用c的数组来的简洁,要的就是他的灵活大小。

所以:采用默认初始化:
vector<T> v1    ;      v1 是一个元素类型为 T 的空 vector

又因为:C++ 的标准库中的 vector 不直接支持数组作为元素类型,所以不能写为vector<double[3
>

所以:如果一定要添加C的数组到vector中是没有办法的,最多就是用C的数组初始化array
double point1[3] = {1.0, 1.0, 5.0};
std::vector<double> pointVector1(point1, point1 + sizeof(point1) / sizeof(point1[0]));

需要注意的是:
std::array 是一个 C++ 类模板,它封装了数组并提供了许多实用的成员函数,如 .size() 以获取数组大小。
double point[3] = {0, 0, 1}; 是一个简单的数组声明,不封装任何额外的信息。它在 C++ 中被允许,但不提供与数组大小相关的信息。

关于vector的其他知识,请移步
https://blog.csdn.net/Flag_ing/article/details/123380655

#include <vector>
#include <array>
#include <iostream>

struct Vertex
{
    float x, y, z;
};

int main()
{
    // vector point
    std::vector<std::array<double, 3>> points;
    std::vector<Vertex> vertexList;

    //---------------------------three point----------------------------
    std::array<double, 3> point = {1.0, 1.0, 5.0};
    points.push_back(point);
    points.push_back(point);
    points.push_back(point);
    // 打印第一个点的坐标
    int i = 1;
    for (auto pt : points)
    {
        std::cout << "Point: " << std::endl;
        for (double coordinate : pt)
        {
            std::cout << coordinate << " ";
        }
        std::cout << std::endl;
        i++;
    }

    //---------------------------three point----------------------------

    struct Vertex vertex01 = {251.0, 100.0, -7.0};
    struct Vertex vertex02 = {14.0, 8.0, -100.0};
    struct Vertex vertex03 = {99.0, 69.0, 45.0};
    vertexList.push_back(vertex01);
    vertexList.push_back(vertex02);
    vertexList.push_back(vertex03);
    // 打印第一个点的坐标
    for (auto pt : vertexList)
    {
        std::cout << "Point: " << i << std::endl;

        std::cout << pt.x << " ";
        std::cout << pt.y << " ";
        std::cout << pt.z << " ";

        std::cout << std::endl;
        i++;
    }

    return 0;
}


 

C++ 使用类表示点云

思路是这样的,上文里用了vector表示点云,但是作为一个点云对象,有一些点云库具备各种实例方法方便大家使用,还可以重载运算符如 + ,两个点云相加就非常明了了。(心虚,其实我就是想搞个类重现一下点云库而已)

这里设置一个class MyPoint,里面关键的属性就是点云std::vector<Vertex> points;然后自定义一个添加其他点云的add函数,和一个打印点云坐标的函数,解析如下,基础知识放在本章最后:

结构体 Vertex

struct Vertex: 这里定义了一个结构体 Vertex,包含三个浮点型成员变量 x、y 和 z,用于表示一个三维点的坐标。

struct Vertex

{

    float x, y, z;

};

class MyPoint

class MyPoint: 这是一个类的定义,表示一个点云。类中包含一个私有数据成员(private data member)和一些公有成员函数(public member functions)。

核心就在 std::vector<Vertex> points;  // 公有成员变量,存储 Vertex 结构体的向量

class code

struct Vertex

{

    float x, y, z;

};



class MyPoint

{

private:

    /* data */   // 这里可以声明私有成员变量

public:

    MyPoint(/* args */);  // 构造函数的声明

    ~MyPoint();           // 析构函数的声明

    MyPoint add(MyPoint);  // add 成员函数的声明

    void print();          // print 成员函数的声明

    std::vector<Vertex> points;  // 公有成员变量,存储 Vertex 结构体的向量

};

MyPoint MyPoint::add(MyPoint a)

{

    for (auto pt : a.points)

    {

        // 将另一个点的坐标逐个加到当前点的坐标上

        this->points.push_back(pt);

    }

    // 返回修改后的当前点

    return *this;

}



void MyPoint::print()

{

    // 注意,这里是->访问而不是 . 访问

    int i = 1;

    for (auto pt : this->points)

    {

        // 将另一个点的坐标逐个加到当前点的坐标上

        std::cout << "class Point: " << i << std::endl;



        std::cout << pt.x << " ";

        std::cout << pt.y << " ";

        std::cout << pt.z << " ";



        std::cout << std::endl;

        i++;

        // 返回修改后的当前点

    }

}



MyPoint::MyPoint(/* args */)

{

}



MyPoint::~MyPoint()

{

}

函数解析

MyPoint::add(MyPoint a): 这是 MyPoint 类中 add 函数的实现。该函数接受一个 MyPoint 类型的参数 a,将其成员变量 points 中的点逐个添加到当前点的 points 向量中。该函数返回修改后的当前点。

void MyPoint::print(): 这是 MyPoint 类中 print 函数的实现。该函数用于打印当前点的坐标。在函数中,通过循环遍历 points 向量,逐个打印每个点的坐标。

MyPoint::MyPoint(/* args */): 这是 MyPoint 类中构造函数的实现。构造函数是在对象创建时被调用的函数,这里的构造函数没有实现特殊的逻辑。

MyPoint::~MyPoint(): 这是 MyPoint 类中析构函数的实现。析构函数是在对象销毁时被调用的函数,这里的析构函数没有实现特殊的逻辑。

C和C++中类的基础知识:

构造函数和析构函数:

构造函数是在对象创建时被调用的特殊成员函数,用于初始化对象的成员变量。

析构函数是在对象销毁时被调用的特殊成员函数,用于清理对象的资源。

成员函数:

成员函数是定义在类中的函数,它们可以访问类的私有成员。

成员函数可以有返回值,也可以是 void(无返回值)。

成员变量:

成员变量是类中的数据成员,可以是各种数据类型,包括基本数据类型和自定义的结构体、类等。

访问修饰符:

private、public 和 protected 是访问修饰符,用于指定成员的访问权限。

private 表示只能在类的内部访问,public 表示可以在类的外部访问。

this 指针:

this 指针是一个隐含在每个成员函数中的指针,指向调用该函数的对象。

通过 this 指针,可以访问对象的成员变量和其他成员函数。

标准库容器:

std::vector 是C++标准库中的一个动态数组容器,可以动态增长或缩小。

在这个例子中,points 是一个存储 Vertex 结构体的向量,用于保存点的坐标。

C++、C struct 类的可运行代码:

#include <vector>
#include <array>
#include <iostream>

struct Vertex
{
    float x, y, z;
};

class MyPoint
{
private:
    /* data */
public:
    MyPoint(/* args */);
    ~MyPoint();
    MyPoint add(MyPoint);
    void print();
    std::vector<Vertex> points;
};

MyPoint MyPoint::add(MyPoint a)
{
    for (auto pt : a.points)
    {
        // 将另一个点的坐标逐个加到当前点的坐标上
        this->points.push_back(pt);
    }
    // 返回修改后的当前点
    return *this;
}

void MyPoint::print()
{
    // 注意,这里是->访问而不是 . 访问
    int i = 1;
    for (auto pt : this->points)
    {
        // 将另一个点的坐标逐个加到当前点的坐标上
        std::cout << "class Point: " << i << std::endl;

        std::cout << pt.x << " ";
        std::cout << pt.y << " ";
        std::cout << pt.z << " ";

        std::cout << std::endl;
        i++;
        // 返回修改后的当前点
    }
}

MyPoint::MyPoint(/* args */)
{
}

MyPoint::~MyPoint()
{
}

int main()
{
    // vector point
    std::vector<std::array<double, 3>> points;
    std::vector<Vertex> vertexList;
    MyPoint pointCloud;

    //---------------------------three point----------------------------
    std::array<double, 3> point = {1.0, 1.0, 5.0};
    points.push_back(point);
    points.push_back(point);
    points.push_back(point);
    // 打印第一个点的坐标
    int i = 1;
    for (auto pt : points)
    {
        std::cout << "arry Point: " << std::endl;
        for (double coordinate : pt)
        {
            std::cout << coordinate << " ";
        }
        std::cout << std::endl;
        i++;
    }

    //---------------------------three point----------------------------

    struct Vertex vertex01 = {251.0, 100.0, -7.0};
    struct Vertex vertex02 = {14.0, 8.0, -100.0};
    struct Vertex vertex03 = {99.0, 69.0, 45.0};
    vertexList.push_back(vertex01);
    vertexList.push_back(vertex02);
    vertexList.push_back(vertex03);
    // 打印第一个点的坐标
    for (auto pt : vertexList)
    {
        std::cout << "struct Point: " << i << std::endl;

        std::cout << pt.x << " ";
        std::cout << pt.y << " ";
        std::cout << pt.z << " ";

        std::cout << std::endl;
        i++;
    }

    //---------------------------three point----------------------------

    pointCloud.points.push_back(vertex01);
    pointCloud.points.push_back(vertex02);
    pointCloud.points.push_back(vertex03);
    // 打印第一个点的坐标
    pointCloud.add(pointCloud);
    pointCloud.print();

    return 0;
}

python 使用numpy表示点云

numpy数组表示点云有诸多好处,因为numpy中有大量的数学库,后面都会用到,这里先做一个点意思一下。

pt = np.array([10, 2, 3])

import numpy as np

pt = np.array([10, 2, 3])

print(pt)

至此,三种语言描述点元素暂时完成,文件夹如下。需要注意的是,由于混合了py和c在同一环境,这里运行py文件还需要切换到conda的有numpy环境下用命令行运行

更新日志

更新日志:2024年1月20日新增C的结构体点表示,新增C++ std::vector<Vertex> vertexList;新增C++的class表达,新增numpy点云表达

  • 12
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 答案: # 导入需要的库 import numpy as np import pandas as pd import scipy.spatial as spatial# 定义 α-shape 算法 def alpha_shape(points, alpha): # 计算距离矩阵 dist_matrix = spatial.distance.cdist(points, points) # 创建最小生成树 mst = minimum_spanning_tree(dist_matrix) # 根据 alpha 值,计算切片轮廓 edges = set() for u, v, d in mst: if d > alpha: edges.add(frozenset((u, v))) return edges ### 回答2: α-shape算法是一种用于提取点云切片轮廓的方法。它基于α参数来控制提取等级,通过计算点云中每个的边界长度来决定是否属于轮廓。 下面是使用Python实现α-shape算法提取点云切片轮廓的代码: ```python import numpy as np from scipy.spatial import Delaunay def alpha_shape(points, alpha): tri = Delaunay(points) edges = set() edge_points = [] for i in range(tri.nsimplex): simplex = tri.simplices[i] for j in range(3): edge = tuple(sorted((simplex[j], simplex[(j + 1) % 3]))) if edge in edges: edges.remove(edge) else: edges.add(edge) for edge in edges: p1, p2 = edge if np.linalg.norm(points[p1] - points[p2]) > alpha: edge_points.append(points[p1]) edge_points.append(points[p2]) return np.array(edge_points) # 示例使用 if __name__ == "__main__": # 生成随机点云数据 np.random.seed(0) points = np.random.rand(100, 2) # 提取α-shape轮廓 alpha = 0.1 contour_points = alpha_shape(points, alpha) print("提取的轮廓:") for point in contour_points: print(point) ``` 上述代码中,首先导入了必要的库,使用`Delaunay(points)`函数构建点云的Delaunay三角网格。然后,通过遍历所有三角面,找出所有边界的边,并计算边界的长度。接着,根据α参数和边界的长度,将符合条件的添加到轮廓集合中。最后返回点云的切片轮廓。 在示例部分,生成了一个包含100个随机点云,并设定α参数为0.1。调用`alpha_shape`函数提取点云的切片轮廓,并打印结果。 以上就是使用Python实现α-shape算法提取点云切片轮廓的代码。注意,代码中使用了`numpy`和`scipy`这两个常用的科学计算库。 ### 回答3: α-Shape算法是一种用于提取点云数据中切片轮廓的方法。这个算法基于几何学模型,可以通过设置参数α的不同值来得到不同程度的轮廓提取。 下面是使用Python实现α-Shape算法提取点云切片轮廓的代码: ```python import numpy as np from scipy.spatial import Delaunay def alpha_shape(points, alpha): """ 使用α-Shape算法提取切片轮廓 参数: points:点云数据,形状为(N, 3),N为的数量,每个有3个坐标(x, y, z) alpha:α参数的值 返回: contour:切片轮廓,形状为(M, 3),M为轮廓的数量,每个有3个坐标(x, y, z) """ tri = Delaunay(points[:, :2]) edges = set() edge_points = [] # 遍历Delaunay三角形的边 for ia, ib, ic in tri.vertices: a = tuple(points[ia]) b = tuple(points[ib]) c = tuple(points[ic]) edge_points.append((a, b)) for i, edge1 in enumerate(edge_points): for j, edge2 in enumerate(edge_points): if i == j: continue a, b = edge1 c, d = edge2 if (a == c and b == d) or (a == d and b == c): edges.add(edge1) contour = np.array(list(edges)) return contour # 测试代码 # 生成随机点云数据 points = np.random.rand(100, 3) # 提取切片轮廓 contour = alpha_shape(points, 0.5) print(contour) ``` 需要注意的是,该代码使用了Scipy库中的Delaunay函数来创建Delaunay三角剖分,并遍历了所有的边来判断是否属于轮廓边。返回的轮廓是一个二维数组,每个轮廓都有三个坐标值。 这段代码提供了一个基本的实现,但在实际应用中可能需要根据具体需求进行优化或修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值