CGAL入门——点和线的关系示例
官网源码: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的精确中点肯定也是与他们共线的!
总结:
想使用精确的小数数字,需要精确的核,且不能直接使用浮点数构造,要么使用字符串构造,要么使用精确构造函数。