CGAL入门——点和线的关系示例

发布时间 2023-08-15 08:01:23作者: 一只小瓶子

官网源码:CGAL 5.6 - Manual: Hello World

 

所有 CGAL 头文件都位于子目录include/CGAL中。所有 CGAL 类和函数都位于命名空间 CGAL中。全局函数以小写字母开头(如CGAL::squared_distance和CGAL::orientation),常量全部大写(如GAL::COLLINEAR、CGAL::LEFT_TURN和CGAL::RIGHT_TURN)。对象的维度(dimension)用后缀表示(如Point_2和Segment_2)。

 

几何基元(primitives)(如点类型)是在(kernel)中定义的。下面示例选择的核使用精确的浮点数double作为点的笛卡尔坐标。

除了类型之外,我们还会看到:

  谓词(predicates):如三点的方向测试(CGAL::orientation)。

  构造(constructions):如距离(CGAL::squared_distance)和中点(CGAL::midpoint)计算。

谓词具有一组离散的可能结果,而构造则生成数字或另一个几何实体。

 

示例一:点和线段

在第一个示例中,演示了如何构造一些点和一个线段,并对它们执行一些基本操作。

 

#include <iostream>
#include <CGAL/Simple_cartesian.h>

typedef CGAL::Simple_cartesian<double> K; // 该核精度一般,但是效率最高,可为float或double
typedef K::Point_2 Point_2;
typedef K::Segment_2 Segment_2; 
//点和线的位置关系 使用Simple_cartesian核 int points_and_segment() { // 定义两个位于笛卡尔坐标系下的二维点坐标 Point_2 p(1, 1), q(10, 10); std::cout << "p = " << p << std::endl; std::cout << "q = " << q.x() << " " << q.y() << std::endl; // 计算两点之间的平方距离 std::cout << "两点之间的平方距离:" << CGAL::squared_distance(p, q) << std::endl; // 计算m到线段pq的平方距离 Segment_2 s(p, q); // p和q两点构成的线段 Point_2 m(5, 9); // 点坐标m std::cout << "m = " << m << std::endl; std::cout << "点m到线段pq的平方距离:" << CGAL::squared_distance(s, m) << std::endl; // 判断三点之间的位置关系 std::cout << "p 到 q 再到 m 三点的关系为(与先后顺序有关): "; switch (CGAL::orientation(p, q, m)) { case CGAL::COLLINEAR: std::cout << "三点共线\n"; break; case CGAL::LEFT_TURN: std::cout << "三点构成左转\n"; break; case CGAL::RIGHT_TURN: std::cout << "三点构成右转\n"; break; } std::cout << "p和q的中点为: " << CGAL::midpoint(p, q) << std::endl; return 0; } int main() { points_and_segment(); return 0; }

 

运行结果

 

示例二:三个点的位置关系

#include <iostream>
#include <CGAL/Simple_cartesian.h>

typedef CGAL::Simple_cartesian<double> K;
typedef K::Point_2 Point_2;


//三个点是否共线 Simple_cartesian
int surprising()
{
    {
        Point_2 p(0, 0.3), q(1, 0.6), r(2, 0.9);
        std::cout << (CGAL::collinear(p, q, r) ? "共线\n" : "不共线\n");//不共线
    }
    {
        Point_2 p(0, 1.0 / 3.0), q(1, 2.0 / 3.0), r(2, 1);
        std::cout << (CGAL::collinear(p, q, r) ? "共线\n" : "不共线\n");//不共线
    }
    {
        Point_2 p(0, 0), q(1, 1), r(2, 2);
        std::cout << (CGAL::collinear(p, q, r) ? "共线\n" : "不共线\n");//共线
    }
    return 0;
}

int main()
{
    surprising();
    return 0;
}
 

运行结果

 如上所示,前面两个并非共线,因为使用的是双精度(double)的浮点数,会有舍入误差(如0.3,舍入误差可能为0.29999或者0.300001)。整数数字不会有舍入误差,所以共线。

 

如果想使用精确的小数数字,可以使用精确谓词精确构造的核来构造点,如示例三


示例三:三个点的位置关系 (精确构造)

 

#include <iostream>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <sstream>

typedef CGAL::Exact_predicates_exact_constructions_kernel K;//predicates和constructions都精确的内核,精度最高
typedef K::Point_2 Point_2;

//三个点是否共线 Exact_predicates_exact_constructions_kernel
int exact()
{
    Point_2 p(0, 0.3), q, r(2, 0.9);
    {
        q = Point_2(1, 0.6);
        std::cout << (CGAL::collinear(p, q, r) ? "共线\n" : "不共线\n");//不共线
    }
    {
        std::istringstream input("0 0.3   1 0.6   2 0.9");
        input >> p >> q >> r;
        std::cout << (CGAL::collinear(p, q, r) ? "共线\n" : "不共线\n");//共线
    }
    {
        q = CGAL::midpoint(p, r);
        std::cout << (CGAL::collinear(p, q, r) ? "共线\n" : "不共线\n");//共线
    }
    return 0;
}

int main()
{
    exact();
    return 0;
}

运行结果

 可以看到,第一个仍然是不共线,因为它依然是从浮点数构造,有舍入误差;而第二个是从字符串构造,无舍入误差;第三个生成的中点是精确的构造,所以共线。

 

突发奇想一下:把二三的位置对换一下,p,r为浮点数构造,有舍入误差,精确构造计算中点q,结果依然是共线吗?

答案:是的。因为在精确构造计算中点q之前,p,r已经构造完成,p和r的精确中点肯定也是与他们共线的!

 

总结:

想使用精确的小数数字,需要精确的核,且不能直接使用浮点数构造,要么使用字符串构造,要么使用精确构造函数。