Hello World

Author
CGAL Editorial Board

本教程是为知道C++和几何算法的基本知识的CGAL新手准备的。第一节展示了如何特化点和段CGAL类,以及如何应用几何谓词(predicates)。本部分提出了在使用浮点数坐标时存在严重问题。在第二部分中,你将看到二维凸包函数如何得到输入和结果。第三部分说明了我们所说的特征类的含义, 第四部分解释了概念和模型的概念。

This tutorial is for the CGAL newbie, who knows C++ and has a basic knowledge of geometric algorithms. The first section shows how to define a point and segment class, and how to apply geometric predicates on them. The section further raises the awareness that that there are serious issues when using floating point numbers for coordinates. In the second section you see how the 2D convex hull function gets its input and where it puts the result. The third section shows what we mean with a Traits class, and the fourth section explains the notion of concept and model.

1 Three Points and One Segment

In this first example we see how to construct some points and a segment, and we perform some basic operations on them.

All CGAL header files are in the subdirectory include/CGAL. All CGAL classes and functions are in the namespace CGAL. Classes start with a capital letter, global function with a lowercase letter, and constants are all uppercase. The dimension of an object is expressed with a suffix.

The geometric primitives, like the point type, are defined in a kernel. The kernel we have chosen for this first example uses double precision floating point numbers for the Cartesian coordinates of the point.

Besides the types we see predicates like the orientation test for three points, and constructions like the distance and midpoint computation. A predicate has a discrete set of possible results, whereas a construction produces either a number, or another geometric entity.

File Kernel_23/points_and_segment.cpp

#include <iostream>
#include <CGAL/Simple_cartesian.h> typedef double NumberType;
typedef CGAL::Simple_cartesian<NumberType> Kernel;
typedef Kernel::Point_2 Point_2;
typedef Kernel::Segment_2 Segment_2; int main()
{
Point_2 p(, ), q(, );
std::cout << "p = " << p << std::endl;
std::cout << "q = " << q.x() << " " << q.y() << std::endl;
std::cout << "sqdist(p,q) = "
<< CGAL::squared_distance(p, q) << std::endl; Segment_2 s(p, q);
Point_2 m(, ); std::cout << "m = " << m << std::endl;
std::cout << "sqdist(Segment_2(p,q), m) = "
<< CGAL::squared_distance(s, m) << std::endl;
std::cout << "p, q, and m ";
switch (CGAL::orientation(p, q, m)) {
case CGAL::COLLINEAR:
std::cout << "are collinear\n";
break;
case CGAL::LEFT_TURN:
std::cout << "make a left turn\n";
break;
case CGAL::RIGHT_TURN:
std::cout << "make a right turn\n";
break;
}
std::cout << " midpoint(p,q) = " << CGAL::midpoint(p, q) << std::endl;
return ;
}

To do geometry with floating point numbers can be surprising as the next example shows.

// File Kernel_23/surprising.cpp

#include <iostream>
#include <CGAL/Simple_cartesian.h> typedef double NumberType;
typedef CGAL::Simple_cartesian<NumberType> Kernel;
typedef Kernel::Point_2 Point_2; int main()
{
{
Point_2 p(, 0.3), q(, 0.6), r(, 0.9);
std::cout << (CGAL::collinear(p, q, r) ? "collinear\n" : "not collinear\n");
}
{
Point_2 p(, 1.0 / 3.0), q(, 2.0 / 3.0), r(, );
std::cout << (CGAL::collinear(p, q, r) ? "collinear\n" : "not collinear\n");
}
{
Point_2 p(, ), q(, ), r(, );
std::cout << (CGAL::collinear(p, q, r) ? "collinear\n" : "not collinear\n");
}
return ;
}
 When reading the code, we would assume that it prints three times "collinear". However we obtain:
not collinear
not collinear
collinear

As the fractions are not representable as double precision number the collinearity test will internally compute a determinant of a 3x3 matrix which is close but not equal to zero, and hence the non collinearity for the first two tests.

Something similar can happen with points that perform a left turn, but due to rounding errors during the determinant computation, it seems that the points are collinear, or perform a right turn.

If you need that the numbers get interpreted at their full precision you can use a CGAL kernel that performs exact predicates and extract constructions.

// File Kernel_23/exact.cpp
#include <iostream>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <sstream>
typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel;
typedef Kernel::Point_2 Point_2; int main()
{
Point_2 p(, 0.3), q, r(, 0.9);
{
q = Point_2(, 0.6);
std::cout << (CGAL::collinear(p, q, r) ? "collinear\n" : "not collinear\n");
} {
std::istringstream input("0 0.3 1 0.6 2 0.9");
input >> p >> q >> r;
std::cout << (CGAL::collinear(p, q, r) ? "collinear\n" : "not collinear\n");
} {
q = CGAL::midpoint(p, r);
std::cout << (CGAL::collinear(p, q, r) ? "collinear\n" : "not collinear\n");
} return ;
}

Here comes the output and you may still be surprised.

not collinear
collinear
collinear

In the first block the points are still not collinear, for the simple reason that the coordinates you see as text get turned into floating point numbers. When they are then turned into arbitrary precision rationals, they exactly represent the floating point number, but not the text.

This is different in the second block, which corresponds to reading numbers from a file. The arbitrary precision rationals are then directly constructed from a string so that they represent exactly the text.

In the third block you see that constructions as midpoint constructions are exact, just as the name of the kernel type suggests.

In many cases you will have floating point numbers that are "exact", in the sense that they were computed by some application or obtained from a sensor. They are not the string "0.1" or computed on the fly as "1.0/10.0", but a full precision floating point number. If they are input to an algorithm that makes no constructions you can use a kernel that provides exact predicates, but inexact constructions. An example for that is the convex hull algorithm which we will see in the next section. The output is a subset of the input, and the algorithm only compares coordinates and performs orientation tests.

At a first glance the kernel doing exact predicates and constructions seems to be the perfect choice, but performance requirements or limited memory resources make that it is not. Also for many algorithms it is irrelevant to do exact constructions. For example a surface mesh simplification algorithm that iteratively contracts an edge, by collapsing it to the midpoint of the edge.

Most CGAL packages explain what kind of kernel they need or support.

2 The Convex Hull of a Sequence of Points

All examples in this section compute the 2D convex hull of a set of points. We show that algorithms get their input as a begin/end iterator pair denoting a range of points, and that they write the result, in the example the points on the convex hull, into an output iterator.

2.1 The Convex Hull of Points in a Built-in Array

In the first example we have as input an array of five points. As the convex hull of these points is a subset of the input it is safe to provide an array for storing the result which has the same size.

//File Convex_hull_2/array_convex_hull_2.cpp
#include <iostream>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/convex_hull_2.h>
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef K::Point_2 Point_2; int main()
{
Point_2 points[] = { Point_2(,), Point_2(,), Point_2(,), Point_2(,), Point_2(,) };
Point_2 result[];
Point_2 *ptr = CGAL::convex_hull_2(points, points + , result);
std::cout << ptr - result << " points on the convex hull:" << std::endl;
for (int i = ; i < ptr - result; i++) {
std::cout << result[i] << std::endl;
}
return ;
}

We saw in the previous section that CGAL comes with several kernels. As the convex hull algorithm only makes comparisons of coordinates and orientation tests of input points, we can choose a kernel that provides exact predicates, but no exact geometric constructions.

The convex hull function takes three arguments, the start and past-the-end pointer for the input, and the start pointer of the array for the result. The function returns the pointer into the result array just behind the last convex hull point written, so the pointer difference tells us how many points are on the convex hull.

2.2 The Convex Hull of Points in a Vector

In the second example we replace the built-in array by a std::vector of the Standard Template Library.

//File Convex_hull_2/vector_convex_hull_2.cpp
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/convex_hull_2.h>
#include <vector>
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef K::Point_2 Point_2;
typedef std::vector<Point_2> Points; int main()
{
Points points, result;
points.push_back(Point_2(, ));
points.push_back(Point_2(, ));
points.push_back(Point_2(, ));
points.push_back(Point_2(, ));
points.push_back(Point_2(, ));
CGAL::convex_hull_2(points.begin(), points.end(), std::back_inserter(result));
std::cout << result.size() << " points on the convex hull" << std::endl;
return ;
}

We put some points in the vector calling the push_back() method of the std::vector class.

We then call the convex hull function. The first two arguments, points.begin() and points.end() are iterators, which are a generalization of pointers: they can be dereferenced and incremented. The convex hull function is generic in the sense that it takes as input whatever can be dereferenced and incremented.

The third argument is where the result gets written to. In the previous example we provided a pointer to allocated memory. The generalization of such a pointer is the output iterator, which allows to increment and assign a value to the dereferenced iterator. In this example we start with an empty vector which grows as needed. Therefore, we cannot simply pass it result.begin(), but an output iterator generated by the helper function std::back_inserter(result). This output iterator does nothing when incremented, and calls result.push_back(..) on the assignment.

If you know the STL, the Standard Template Library, the above makes perfect sense, as this is the way the STL decouples algorithms from containers. If you don't know the STL, you maybe better first familiarize yourself with its basic ideas.

3 About Kernels and Traits Classes

In this section we show how we express the requirements that must be fulfilled in order that a function like convex_hull_2() can be used with an arbitrary point type.

If you look at the manual page of the function convex_hull_2() and the other 2D convex hull algorithms, you see that they come in two versions. In the examples we have seen so far the function that takes two iterators for the range of input points and an output iterator for writing the result to. The second version has an additional template parameter Traits, and an additional parameter of this type.

template<class InputIterator, class OutputIterator, class Traits >
OutputIterator
convex_hull_2(InputIterator first,
InputIterator beyond,
OutputIterator result,
const Traits & ch_traits)

What are the geometric primitives a typical convex hull algorithm uses? Of course, this depends on the algorithm, so let us consider what is probably the simplest efficient algorithm, the so-called "Graham/Andrew Scan". This algorithm first sorts the points from left to right, and then builds the convex hull incrementally by adding one point after another from the sorted list. To do this, it must at least know about some point type, it should have some idea how to sort those points, and it must be able to evaluate the orientation of a triple of points.

And that is where the template parameter Traits comes in. For ch_graham_andrew() it must provide the following nested types:

  • Traits::Point_2
  • Traits::Less_xy_2
  • Traits::Left_turn_2
  • Traits::Equal_2

As you can guess, Left_turn_2 is responsible for the orientation test, while Less_xy_2 is used for sorting the points. The requirements these types have to satisfy are documented in full with the concept ConvexHullTraits_2.

The types are regrouped for a simple reason. The alternative would have been a rather lengthy function template, and an even longer function call.

template <class InputIterator, class OutputIterator, class Point_2, class Less_xy_2, class Left_turn_2, class Equal_2>
OutputIterator
ch_graham_andrew(InputIterator first,
InputIterator beyond,
OutputIterator result);

There are two obvious questions: What can be used as argument for this template parameter? And why do we have template parameters at all?

To answer the first question, any model of the CGAL concept Kernel provides what is required by the concept ConvexHullTraits_2.

As for the second question, think about an application where we want to compute the convex hull of 3D points projected into the yz plane. Using the class Projection_traits_yz_3 this is a small modification of the previous example.

//File Convex_hull_2/convex_hull_yz.cpp
#include <iostream>
#include <iterator>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Projection_traits_yz_3.h>
#include <CGAL/convex_hull_2.h>
typedef CGAL::Exact_predicates_inexact_constructions_kernel K3;
typedef CGAL::Projection_traits_yz_3<K3> K;
typedef K::Point_2 Point_2;
int main()
{
std::istream_iterator< Point_2 > input_begin(std::cin);
std::istream_iterator< Point_2 > input_end;
std::ostream_iterator< Point_2 > output(std::cout, "\n");
CGAL::convex_hull_2(input_begin, input_end, output, K());
return ;
}

Another example would be about a user defined point type, or a point type coming from a third party library other than CGAL. Put the point type together with the required predicates for this point type in the scope of a class, and you can run convex_hull_2() with these points.

Finally, let us explain why a traits object that is passed to the convex hull function? It would allow to use a more general projection traits object to store state, for example if the projection plane was given by a direction, which is hardwired in the class Projection_traits_yz_3.

4 Concepts and Models

In the previous section we wrote that "Any model of the CGAL concept Kernel provides what is required by the concept ConvexHullTraits_2".

A concept is a set of requirements on a type, namely that it has certain nested types, certain member functions, or comes with certain free functions that take the type as it. A model of a concept is a class that fulfills the requirements of the concept.

Let's have a look at the following function.

template <T>
T
duplicate(T t)
{
return t;
}

If you want to instantiate this function with a class C this class must at least provide a copy constructor, and we say that class C must be a model of CopyConstructible. A singleton class does not fulfill this requirment.

Another example is the function

template <typename T>
T& std::min(const T& a, constT& b)
{
return (a<b) ? a : b;
}

This function only compiles if the operator<<(..) is defined for the type used as T, and we say that the type must be a model of LessThanComparable.

An example for a concept with required free functions is the HalfedgeListGraph in the CGAL package CGAL and the Boost Graph Library. In order to be a model of HalfedgeListGraph a class G there must be a global function halfedges(const G&), etc.

An example for a concept with a required traits class is InputIterator. For a model of an InputIterator a specialization of the class std::iterator_traits must exist (or the generic template must be applicable).

5 Further Reading

We also recommend the standard text books "The C++ Standard Library, A Tutorial and Reference" by Nicolai M. Josuttis from Addison-Wesley, or "Generic Programming and the STL" by Matthew H. Austern for the STL and its notion of concepts and models.

Other resources for CGAL are the rest of the tutorials and the user support page at http://www.cgal.org/.

CGAL Manual/tutorial_hello_world.html的更多相关文章

  1. Linear and Quadratic Programming Solver ( Arithmetic and Algebra) CGAL 4.13 -User Manual

    1 Which Programs can be Solved? This package lets you solve convex quadratic programs of the general ...

  2. 3D Spherical Geometry Kernel( Geometry Kernels) CGAL 4.13 -User Manual

    Introduction The goal of the 3D spherical kernel is to offer to the user a large set of functionalit ...

  3. 2D Circular Geometry Kernel ( Geometry Kernels) CGAL 4.13 -User Manual

    1 Introduction The goal of the circular kernel is to offer to the user a large set of functionalitie ...

  4. dD Geometry Kernel ( Geometry Kernels) CGAL 4.13 -User Manual

    1 Introduction This part of the reference manual covers the higher-dimensional kernel. The kernel co ...

  5. 2D and 3D Linear Geometry Kernel ( Geometry Kernels) CGAL 4.13 -User Manual

    1 Introduction CGAL, the Computational Geometry Algorithms Library, is written in C++ and consists o ...

  6. Algebraic Foundations ( Arithmetic and Algebra) CGAL 4.13 -User Manual

    理解: 本节主要介绍CGAL的代数结构和概念之间的互操作.与传统数论不同,CGAL的代数结构关注于实数轴的“可嵌入”特征.它没有将所有传统数的集合映射到自己的代数结构概念中,避免使用“数的类型”这一术 ...

  7. 2D Polygons( Poygon) CGAL 4.13 -User Manual

    1 Introduction A polygon is a closed chain of edges. Several algorithms are available for polygons. ...

  8. 2D Convex Hulls and Extreme Points( Convex Hull Algorithms) CGAL 4.13 -User Manual

    1 Introduction A subset S⊆R2 is convex if for any two points p and q in the set the line segment wit ...

  9. Monotone and Sorted Matrix Search ( Arithmetic and Algebra) CGAL 4.13 -User Manual

    monotone_matrix_search() and sorted_matrix_search() are techniques that deal with the problem of eff ...

随机推荐

  1. CentOS中操作

    在Centos中yum安装和卸载软件的使用方法安装方法安装一个软件时 :yum -y install httpd安装多个相类似的软件时:yum -y install httpd*安装多个非类似软件时 ...

  2. 聊聊 iOS 开发中的协议

    前言 何为协议,简单来说在OC中我们使用关键字@protocol可以声明一个协议,并在协议中添加多个属性.方法供于遵循者实现,从某个角度上来说,这是一种不同于category机制的category.在 ...

  3. 业界最有价值的 ASP.NET 博文汇总

    ASP.NET凭借它丰富的控件,强大的适应性及良好的封装性,成为业界开发的一门巨匠,它大大缩短了网站开发的时间,降低开发成本.并且可以运行在Web应用软件开发者的全部平台上.本电子书汇集了业界最有价值 ...

  4. 关于Git补丁文件交互

    之前各个章节的版本库的交互都是通过 git push和git pull命令来实现的.这个是Git最主要的交互模式,但并不是全部. 使用补丁文件是另外一种交互方式,适用于参与者众多的大型项目进行的分布式 ...

  5. Time complexity analysis of algorithms

    时间复杂性的计算一般而言,较小的问题所需要的运行时间通常要比较大的问题所需要的时间少.设一个程序P所占用的时间为T,则 T(P)=编译时间+运行时间. 编译时间与实例特征是无关的,且可假设一个编译过的 ...

  6. oracle拼接字段用||

    ① //dual相当于一个临时表.用来测量@@@@H210000000003I4R 的长度用length() select length('@@@@H210000000003I4R') from du ...

  7. NC表型参照类

    package nc.ui.bd.ref; /** * 表参照-其他参照基类. 创建日期:(2001-8-23 20:26:54) 模型里未处理栏目 * * @author:张扬 * */ impor ...

  8. MySQL - 日志管理

    在 MySQL 中,有 4 种不同的日志,分别是错误日志.二进制日志.查询日志和慢查询日志. 错误日志 错误日志记录了 MySQL 启动和停止时以及服务器在运行过程中发生严重错误时的相关信息. 查看错 ...

  9. oc语言学习之基础知识点介绍(四):方法的重写、多态以及self、super的介绍

    一.方法重写 /* 重写:当子类继承了父类的方法时,如果觉得父类的方法不适合,那么可以对这个方法进行重新实现,那么这个就重写. 注意:也就是说,一定只能发生在父类和子类关系中. 然后是子类重新实现父类 ...

  10. php连接Access数据库的三种方法

    http://www.php100.com/html/webkaifa/PHP/PHPyingyong/2009/1115/3524.html 虽然不是一个类但先放这儿吧 最近想把一个asp的网站改成 ...