ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

CGAL 4.11.3 - 2D and 3D Linear Geometry Kernel

2022-04-03 17:03:23  阅读:297  来源: 互联网

标签:4.11 Kernel typedef const Linear Point CGAL 内核 类型


CGAL 4.11.3 - 2D and 3D Linear Geometry Kernel

2D和3D线性几何内核

1、简介

CGAL,即计算几何算法库,是用 C++ 编写的,由三个主要部分组成。第一部分是内核,它由恒定大小的不可修改的几何图元对象和对这些对象的操作组成。这些对象既表示为由表示类参数化的独立类,表示类指定用于计算的基础数字类型,也表示为内核类的成员,这允许内核具有更大的灵活性和适应性。第二部分是基本几何数据结构和算法的集合,它们由特征类参数化,这些特征类定义了数据结构或算法与其使用的基元之间的接口。在许多情况下,CGAL 中提供的内核类可以用作这些数据结构和算法的特征类。该库的第三部分由非几何支持设施组成,例如循环器、随机源、用于调试的 I/O 支持以及用于将 CGAL 连接到各种可视化工具的支持。

参考手册的这一部分涵盖了内核。内核包含大小恒定的对象,如点、向量、方向、线、射线、线段、三角形、等向矩形和四面体。每种类型都有一组函数,可以应用于这种类型的对象。您通常会找到访问函数(例如点的坐标)、点相对于对象的位置的测试、返回边界框、对象的长度或面积的函数等等。 CGAL 内核还包含基本操作,例如仿射变换、交叉点的检测和计算以及距离计算。

1.1、鲁棒性

理论论文中提出的几乎所有几何算法的正确性证明都假设使用实数进行精确计算。伴随着几何算法的实现,产生了一个现实的问题。单纯地,在实现中经常使用不精确的浮点算术代替精确的实数算术。对于大多的数据计算,这样的结果也是可以接受的。然而,及时对于最简单的几何算法的实现,这种简化有时也会得不到想要的结果。由于不准确的算术引入的舍入误差可能导致结果的不一致性,使得计算失败。有很多方法可以解决这个问题,其中精确计算是一个方法(精确到所有的判断都是精确的),这在许多情况下是可行的,但会带来比标准浮点计算更昂贵的计算费用。

在CGAL中,你可以选择基础数字类型和算法。你可以使用不同类型的算法,并且可以轻松更改选择,例如供测试使用。因此,您可以在具有快速但偶尔不精确的算术的实现和保证精确计算和准确结果的实现之间进行选择。当然,您必须为执行时间和存储空间的准确性付费。有关数字类型及其功能和性能的更多详细信息,请参阅专门的章节。

2、内核表示

我们的研究对象是d-维仿射欧几里得空间。这里我们主要关注 d = 2 和 d = 3 的情况。该空间中的对象是点集。表示点的常用方法是使用笛卡尔坐标,它假定参考系(原点和d维正交轴)。在该框架下,一个点由d-元组(C0, C1, ……,Cd-1)组成,底层线性空间中的向量也是如此。每个点都由这样的笛卡尔坐标唯一地表示。另一种表示点的方法是齐次坐标系。再该框架中,一个点由一个(d+1)-元组(h0, h1,……, hd)组成。通过公式ci=hi/hd, 可以计算出笛卡尔坐标。请注意,笛卡尔坐标不是唯一的。对于λ≠0,元组(h0,h1,…,hd) 和(λ⋅h0,λ⋅ h1,…,λ⋅hd) 表示同一点。对于具有笛卡尔坐标 (c0,c1,…,cd−1) 的点,可能的齐次表示为(c0,c1 ,…,cd-1,1)。齐次坐标实际上允许在更一般的空间,即投影空间 PdPd 中表示对象。在 CGAL 中,我们不在射影几何中计算。相反,我们使用齐次坐标来避免除法运算,因为附加坐标可以用作公分母。

2.1通过参数化实现通用性

几乎所有的内核对象(和相应的函数)都是带有参数的模板,允许用户选择内核对象的表示。用作此参数的参数的类型必须满足语法和语义上的某些要求。需求列表定义了一个抽象的内核概念。对于所有内核对象类型,类型 CGAL::Type 和 Kernel::Type 是相同的。

CGAL 为内核概念提供了四个具体模型系列,两个基于点的笛卡尔表示,两个基于点的齐次表示。内核对象的接口被设计成可以很好地与笛卡尔表示和齐次表示一起工作。例如,2D 中的点也有一个带有三个参数的构造函数(点的三个齐次坐标)。使用内核类参数化的公共接口允许开发独立于所选表示的代码。我们说模型的“族”,因为这两个族也是参数化的。用户可以选择用于表示坐标的数字类型。

由于稍后将变得显而易见的原因,内核类为数据类型提供了两个类型名,即 Kernel::FTKernel::RT。 Kernel::FT 类型必须满足 CGAL 中所谓的 FieldNumberType 的要求。这大致意味着 Kernel::FT 是一种类型,其操作 +、−、∗ 和 / 是用与数学意义上的字段对应的语义(近似)定义的。请注意,严格来说,内置类型 int 不满足对域类型的要求,因为 int 对应于环的单元而不是域,特别是操作 / 不是 ∗ 的逆。对 Kernel::RT 类型的要求较弱。这种类型必须满足 CGAL 中所谓的 RingNumberType 的要求。这大致意味着 Kernel::RT 是一种类型,其操作 +、−、∗ 的语义(近似)对应于数学意义上的环的语义。

2.2笛卡尔内核

使用 Cartesian 您可以选择坐标的笛卡尔表示。当您选择笛卡尔表示时,您必须同时声明坐标的类型。与笛卡尔表示类一起使用的数字类型应该是如上所述的 FieldNumberType。如上所述,内置类型 int 不是 FieldNumberType。但是,对于某些使用笛卡尔表示的计算,不需要除法运算,即在这种情况下,RingNumberType 就足够了。使用 Cartesian,Cartesian::FT 和 Cartesian::RT 都映射到 FieldNumberType。

Cartesian 在内部使用引用计数来节省复制成本。 CGAL 还提供了 Simple_cartesian,这是一个使用笛卡尔表示但没有引用计数的内核。使用 Simple_cartesian 进行调试更容易,因为坐标存储在类中,因此可以直接访问坐标。根据算法的不同,它也可能比 Cartesian 的效率略高或略低。同样,在 Simple_cartesian 中,Simple_cartesian::FT 和 Simple_cartesian::RT 都映射到 FieldNumberType。

2.3齐次内核

齐次坐标允许避免数值计算中的除法运算,因为附加坐标可以用作公分母。避免除法对于精确的几何计算很有用。使用 Homogeneous 您可以为内核对象的坐标选择同质表示。至于笛卡尔表示,必须声明用于存储坐标的类型。由于齐次表示不使用除法,因此与齐次表示类关联的数字类型必须是仅用于较弱概念 RingNumberType 的模型。但是,此内核提供的一些操作涉及除法,例如计算平方距离或笛卡尔坐标。为了保持对 Homogeneous 的数字类型参数的要求较低,需要除法的操作使用数字类型 Quotient。这种号码类型可以看作是一个将 RingNumberType 转换为 FieldNumberType 的适配器。它将数字保持为商,即分子和分母。对于 Homogeneous,Homogeneous::FT 等于 Quotient,而 Homogeneous::RT 等于 RingNumberType。

Homogeneous 在内部使用引用计数来节省复制成本。 CGAL 还提供了 Simple_homogeneous,这是一个使用同构表示但没有引用计数的内核。使用 Simple_homogeneous 调试更容易,因为坐标存储在类中,因此可以直接访问坐标。根据算法的不同,它也可能比 Homogeneous 的效率略高或略低。同样,在 Simple_homogeneous 中,类型 Simple_homogeneous::FT 等于 Quotient,而 Simple_homogeneous::RT 等于 RingNumberType。

2.4、命名约定

内核类的使用不仅避免了问题,而且还使所有 CGAL 类非常统一。它们始终包括:

1、几何对象的大写基本名称,例如点、线段或三角形。

2、下划线后跟对象的尺寸,例如 _2、 _3 或 _d。

3、一个内核类作为参数,它本身是用数字类型参数化的,例如 Cartesian 或 Homogeneous<leda_integer>。

2.5、内核作为特征类

CGAL 基本库中的算法和数据结构由一个特征类参数化,该特征类包含算法或数据结构操作的对象以及如何操作。对于基本库中的大多数算法和数据结构,您可以将内核用作特征类。对于某些算法,您甚至不必指定内核;它使用传递给算法的几何对象的类型自动检测。在其他一些情况下,算法或数据结构需要的不仅仅是内核概念所提供的。在这些情况下,内核不能用作特征类。

2.6、选择内核和预定义内核

如果从积分笛卡尔坐标开始,许多几何计算将只涉及整数数值。尤其是对于只进行判断的几何计算而言,这是正确的,这相当于行列式计算。示例是点集的三角剖分和凸包计算。在这种情况下,笛卡尔表示可能是首选,即使是环类型。您可以使用有限精度的整数类型,如 int 或 long,使用 double 来表示整数(它们的尾数比 int 有更多位并且很好地溢出),或者任意精度的整数类型,如 GMP 整数的包装器 Gmpz,leda_integer ,或 MP_Float。请注意,除非您使用任意精度的环类型,否则都可能会因溢出而出现不正确的结果。

如果要构建新点,例如两条线的交点,笛卡尔坐标的计算通常涉及除法。因此,需要使用具有笛卡尔表示的 FieldNumberType,或者切换到齐次表示。 double 类型是 FieldNumberType 的 -though imprecise - 模型。您还可以将任何 RingNumberType 放入 Quotient 适配器以获得字段类型,然后可以将其放入笛卡尔。但是在 RingNumberType 上使用同构表示通常是更好的选择。其他有效的 FieldNumberTypes 是 leda_rational 和 leda_real。

如果计算的可靠性对您至关重要,那么正确的选择可能是保证精确计算的数字类型。 Filtered_kernel 提供了一种应用过滤技术 [1] 来实现具有精确和高效判断的内核的方法。还有一些人更喜欢内置类型 double,因为他们需要速度并且可以忍受近似结果,甚至算法有时会由于累积的舍入误差而崩溃或计算不正确的结果。

2.6.1、预定义内核

为了方便用户,CGAL 为常用的内核提供了 3 个typedef。

  • 它们都是笛卡尔坐标。
  • 它们都支持从double笛卡尔坐标系构造点。
  • 这 5 个内核都提供了精确的几何判断。
  • 它们以不同的方式处理几何结构:

Exact_predicates_inexact_constructions_kernel

Exact_predicates_exact_constructions_kernel

Exact_predicates_exact_constructions_kernel_with_sqrtbut the number type is a model of concept FieldWithSqrt

Exact_predicates_exact_constructions_kernel_with_kth_root but the number type is a model of concept FieldWithKthRoot

Exact_predicates_exact_constructions_kernel_with_root_of but the number type is a model of concept FieldWithRootOf

3、内核几何

3.1、点和向量

在 CGAL 中,我们严格区分点、向量和方向。点是欧几里得空间中的一个点。向量是两个点的差,并且表示两个点之间的方向和距离。方向是一个不考虑长度的向量。它们是不同的数学概念。例如,它们在仿射变换下表现不同,并且在仿射几何中添加两个点是没有意义的。通过将它们放在不同的类中,我们不仅可以得到更清晰的代码,而且还可以通过编译器进行类型检查,从而避免歧义的表达式。因此,做出这种区分需要付出两次代价。

CGAL 定义了一个 Origin 类型的符号常量 ORIGIN,它表示原点处的点。该常量用于点和向量之间的转换。从点 p 中减去它会得到 p 的轨迹向量。

Cartesian<double>::Point_2 p(1.0, 1.0), q;
Cartesian<double>::Vector_2 v;
v = p - ORIGIN;
q = ORIGIN + v; 
assert( p == q );

为了获得与向量 v 对应的点,您只需将 ORIGIN 加上 v 。如果要确定两点p1和p2中间的点q,可以写:

q = p_1 + (p_2 - p_1) / 2.0;

请注意,这些构造不涉及使用当前可用表示类进行转换的任何性能开销。

3.2、内核对象

除了点(Kernel::Point_2Kernel::Point_3)、向量(Kernel::Vector_2Kernel::Vector_3)和 方向(Kernel::Direction_2Kernel::Direction_3 ),CGAL 提供线条、射线、线段、平面、三角形、四面体、等矩形、等长方体、圆和球体。

CGAL 中的线(Kernel::Line_2Kernel::Line_3)是由方向的。在二维空间,它们将平面划分为正面和负面。一条线上任何两点都会生成一个该条线的方向。射线(Kernel::Ray_2, Kernel::Ray_3)是一条线上的半无限区间,这条线从这个区间的有限端点指向这个区间的任何其他点。线段 (Kernel::Segment_2, Kernel::Segment_3) 是有向线上的有界区间,端点是有序的,因此它们的方向与线的方向相同。

平面是 E3 中维数为 2 的仿射子空间,通过三个点,或一个点和一条线、射线或线段。 CGAL 提供了环境空间 E3 中的任何平面与该空间中 E2的嵌入之间的对应关系。就像线一样,平面也是有方向的,并将空间划分为正面和负面。在 CGAL 中,没有半空间的特殊类。 2D 和 3D 中的半空间应该分别由定向线和平面表示。

关于多边形和多面体,内核提供三角形、等向矩形、等向长方体和四面体。更复杂的多边形[3]和多面体或多面体表面可以从基本库(Polygon_2,Polyhedron_3)中获得,因此它们不是内核的一部分。与任何 Jordan 曲线一样,三角形、等向矩形和圆形将平面分成两个区域,一个有界,一个无界。

3.3、方向和相对位置

CGAL 中的几何对象具有成员函数,用于测试点相对于对象的位置。全维对象及其边界由相同的类型表示,例如半空间和超平面没有区别,球和球体、圆盘和圆也没有区别。这样的对象将环境空间分成两个全维部分,一个有界部分和一个无界部分(例如圆),或两个无界部分(例如超平面)。默认情况下,这些对象是定向的,即,结果部分之一称为正面,另一个称为负面。这两种可能都是无限的。

这些对象有一个成员函数 oriented_side() ,它决定了一个测试点是在正侧、负侧还是在有向边界上。这些函数返回 Oriented_side 类型的值。

那些将空间分成有界部分和无界部分的对象,具有返回值类型为 Bounded_side 的成员函数 bounded_side()。

如果一个对象是低维的,例如三维空间中的三角形或二维空间中的线段,只有一个点是否属于对象的测试。这个以点为参数并返回布尔值的成员函数称为 has_on()。

4、判断和计算

4.1判断

判断时几何内核的核心。它们是构成几何算法和封装决策的基本单位。因此,它们的正确性对于控制流以及几何算法实现的正确性至关重要。GCAL 在广义上使用术语判断。不仅返回布尔值的组件称为判断,而且返回枚举类型(如比较结果或方向)的组件也称为判断。我们说组件,因为判断即作为函数也作为函数对象(由内核提供)实现。

CGAL 为点集的方向提供判断(orientation(), left_turn(), right_turn(), collinear(), coplanar() ),用于根据给定顺序比较点,尤其在笛卡尔坐标(例如, lexicographically_xy_smaller() ),圆内和球内测试,以及比较距离的判断。

4.2计算构造

生成既不是 bool 类型, 也不是 enun 类型的对象的函数和函数对象称为计算构造。计算构造涉及新数值的计算,并且由于舍入误差可能不精确,除非使用具有精确数字类型的内核。

仿射变换(Kernel::Aff_transformation_2, Kernel::Aff_transformation_3)允许在任一仿射变换下生成新的对象实例。这些变换包括平移、旋转(仅在 2D 中)和缩放。内核中的大多数几何对象都有一个成员函数 transform(Aff_transformationt),它将变化应用于对象实例。

CGAL 还提供了一组函数,用于检测或计算 2D 内核的对象与 3D 内核中的许多对象之间的交集,以及计算它们的平方距离的函数。此外,内核对象的一些成员函数是计算构造。

所以有计算欧几里得距离平方的例程,但没有计算距离本身的例程。为什么?首先,这两个值可以很容易地相互推导(通过取平方根或取平方)。因此,只提供一个而不提供另一个对用户来说只是一个小小的不便。其次,通常可以使用任一值。例如,比较(平方)距离时就是这种情况。第三,图书馆希望激发使用平方距离而不是距离。在更多情况下可以计算平方距离,并且计算成本更低。我们通过不提供可能更自然的例程来做到这一点,距离例程的问题是它需要 sqrt 操作。这有两个缺点:

  • sqrt 操作可能代价高昂。及时对于特定的数据类型和平台来说成本不是很高,但避免它总是更便宜。
  • 有些数字类型没有定义 sprt 操作,尤其是整数类型和有理数。

4.3交集和变体返回类型

一些函数,例如intersection(),可以返回不同类型的对象。为了以类型安全的方式实现这一点,CGAL 使用 boost::optional< boost::variant< T... > > , T... 类型的返回值是所有可能的结果几何对象的列表。交集的确切结果类型可以通过元函数 cpp11::result_of<Kernel::Intersect_2(Type1, Type2)> 或 cpp11::result_of<Kernel::Intersect_3(Type1, Type2)> 确定,其中 Type1 和 Type2 是交集计算中使用的对象的类型。

4.4实例

在以下示例中,result_of 用于查询交集计算的返回值的类型:

typedef Cartesian<double> K;
typedef K::Point_2 Point_2;
typedef K::Segment_2 Segment_2;
Segment_2 segment_1, segment_2;
std::cin >> segment_1 >> segment_2;
 
/* C++11 */
// auto v = intersection(segment_1, segment_2);
/* C++03 */
cpp11::result_of<K::Intersect_2(Segment_2, Segment_2)>::type
v = intersection(segment_1, segment_2); 
if(v) {
  /* not empty */
  if (const Point_2 *p = boost::get<Point_2>(&*v) ) {
    /* do something with *p */
  } else {
    const Segment_2 *s = boost::get<Segment_2>(&*v);
    /* do something with *s */
  }
} else {
  /* empty intersection */
}

4.5计算结构判断

为了测试点 p 相对于由三个点 q、r 和 s 定义的平面的位置,可能会尝试构建平面 Kernel::Plane_3(q,r,s) 并使用方法oriented_side(p)。如果对平面进行许多测试,这可能会得到回报。然而,除非数字类型是精确的,否则构造的平面只是近似的,并且舍入误差可能导致oriented_side(p) 返回一个与p、q、r 和s 的真实方向不同的方向。

在 CGAL 中,我们提供了判断,在这些判断中,此类几何决策是直接参考输入点 p、q、r、s 做出的,而不需要像平面这样的中间对象。对于上述测试,获得结果的推荐方法是使用orientation(p,q,r,s)。对于精确数字类型,情况有所不同。如果要对同一平面进行多个测试,则构建平面并使用 orientated_side(p) 是值得的。

5、可扩展内核

本手册部分描述了用户如何将自定义的几何类插入现有的 CGAL 内核。这最好用一个例子来说明。

5.1介绍

CGAL 定义了几何内核的概念。这样的内核提供类型、计算构造对象和广义判断。 CGAL 基本库中的计算几何算法和数据结构的大多数实现都是以可以使用几何特征类参数化类或函数的方式完成的。

在大多数情况下,这个几何特征类必须是 CGAL 几何内核概念的模型(但也有一些例外)。

5.2一个扩展的例子

假设我们有以下点类,其中坐标存储在一个 double 数组中,其中我们添加了另一个数据成员 color,它显示在构造函数中。

#ifndef MY_POINTC2_H
#define MY_POINTC2_H
#include <CGAL/Origin.h>
#include <CGAL/Bbox_2.h>
class MyPointC2 {
private:
  double vec[2];
  int col;
public:
  MyPointC2()
    : col(0)
  {
    *vec = 0;
    *(vec+1) = 0;
  }
  MyPointC2(const double x, const double y, int c = 0)
    : col(c)
  {
    *vec = x;
    *(vec+1) = y;
  }
  const double& x() const  { return *vec; }
  const double& y() const { return *(vec+1); }
  double & x() { return *vec; }
  double& y() { return *(vec+1); }
  int color() const { return col; }
  int& color() { return col; }
  bool operator==(const MyPointC2 &p) const
  {
    return ( *vec == *(p.vec) )  && ( *(vec+1) == *(p.vec + 1) && ( col == p.col) );
  }
  bool operator!=(const MyPointC2 &p) const
  {
      return !(*this == p);
  }
};
#endif // MY_POINTC2_H

如前所述,该类非常简约,例如它没有 bbox() 方法。有人可能会假设计算边界框(例如,计算多边形的边界框)的基本库算法不会编译。幸运的是它会,因为它不使用几何对象的成员函数,但它使用了仿函数 Kernel::Construct_bbox_2

为了让 MyPointC2 正确执行,我们必须提供以下仿函数。

#ifndef MYCONSTRUCT_BBOX_2_H
#define MYCONSTRUCT_BBOX_2_H
template <class ConstructBbox_2>
class MyConstruct_bbox_2 : public ConstructBbox_2 {
public:
  using ConstructBbox_2::operator();
  CGAL::Bbox_2 operator()(const MyPointC2& p) const {
    return CGAL::Bbox_2(p.x(), p.y(), p.x(), p.y());
  }
};
#endif //MYCONSTRUCT_BBOX_2_H

随机访问一个点的笛卡尔坐标也是类似的。由于坐标存储在双精度数组中,我们可以使用double* 作为随机访问迭代器。

#ifndef MYCONSTRUCT_COORD_ITERATOR_H
#define MYCONSTRUCT_COORD_ITERATOR_H
class MyConstruct_coord_iterator {
public:
  const double* operator()(const MyPointC2& p)
  {
    return &p.x();
  }
  const double* operator()(const MyPointC2& p, int)
  {
    const double* pyptr = &p.y();
    pyptr++;
    return pyptr;
  }
};
#endif //MYCONSTRUCT_COORD_ITERATOR_H

我们必须提供的最后一个函子是构造点的仿函数。也就是说,您不必将带有 Origin 作为参数的构造函数添加到您的类中,也不必强制添加具有齐次坐标的构造函数。仿函数是 CGAL 算法和你的类之间的一种粘合层。

#ifndef MYCONSTRUCT_POINT_2_H
#define MYCONSTRUCT_POINT_2_H
template <typename K, typename OldK>
class MyConstruct_point_2
{
  typedef typename K::RT         RT;
  typedef typename K::Point_2    Point_2;
  typedef typename K::Line_2     Line_2;
  typedef typename Point_2::Rep  Rep;
public:
  typedef Point_2                result_type;
  // Note : the CGAL::Return_base_tag is really internal CGAL stuff.
  // Unfortunately it is needed for optimizing away copy-constructions,
  // due to current lack of delegating constructors in the C++ standard.
  Rep // Point_2
  operator()(CGAL::Return_base_tag, CGAL::Origin o) const
  { return Rep(o); }
  Rep // Point_2
  operator()(CGAL::Return_base_tag, const RT& x, const RT& y) const
  { return Rep(x, y); }
  Rep // Point_2
  operator()(CGAL::Return_base_tag, const RT& x, const RT& y, const RT& w) const
  { return Rep(x, y, w); }
  Point_2
  operator()(const CGAL::Origin&) const
  { return MyPointC2(0, 0, 0); }
  Point_2
  operator()(const RT& x, const RT& y) const
  {
    return MyPointC2(x, y, 0);
  }
  const Point_2&
  operator()(const Point_2 & p) const
  {
    return p;
  }
  Point_2
  operator()(const Line_2& l) const
  {
    typename OldK::Construct_point_2 base_operator;
    Point_2 p = base_operator(l);
    return p;
  }
  Point_2
  operator()(const Line_2& l, int i) const
  {
    typename OldK::Construct_point_2 base_operator;
    return base_operator(l, i);
  }
  // We need this one, as such a functor is in the Filtered_kernel
  Point_2
  operator()(const RT& x, const RT& y, const RT& w) const
  {
    if(w != 1){
      return MyPointC2(x/w, y/w, 0);
    } else {
      return MyPointC2(x,y, 0);
    }
  }
};
#endif //MYCONSTRUCT_POINT_2_H

现在我们准备把程序块放在一起。我们不会详细解释它,但是您会看到新的点类和仿函数都有 typedef。所有其他类型都是继承的。

#ifndef MYKERNEL_H
#define MYKERNEL_H
#include <CGAL/Cartesian.h>
#include "MyPointC2.h"
#include "MySegmentC2.h"
#include "MyConstruct_bbox_2.h"
#include "MyConstruct_coord_iterator.h"
#include "MyConstruct_point_2.h"
// K_ is the new kernel, and K_Base is the old kernel
template < typename K_, typename K_Base >
class MyCartesian_base
  : public K_Base::template Base<K_>::Type
{
  typedef typename K_Base::template Base<K_>::Type   OldK;
public:
  typedef K_                                Kernel;
  typedef MyPointC2                         Point_2;
  typedef MySegmentC2<Kernel>               Segment_2;
  typedef MyConstruct_point_2<Kernel, OldK>       Construct_point_2;
  typedef const double*                     Cartesian_const_iterator_2;
  typedef MyConstruct_coord_iterator        Construct_cartesian_const_iterator_2;
  typedef MyConstruct_bbox_2<typename OldK::Construct_bbox_2>
                                            Construct_bbox_2;
  Construct_point_2
  construct_point_2_object() const
  { return Construct_point_2(); }
  Construct_bbox_2
  construct_bbox_2_object() const
  { return Construct_bbox_2(); }
  Construct_cartesian_const_iterator_2
  construct_cartesian_const_iterator_2_object() const
  { return Construct_cartesian_const_iterator_2(); }
  template < typename Kernel2 >
  struct Base { typedef MyCartesian_base<Kernel2, K_Base>  Type; };
};
template < typename FT_ >
struct MyKernel
  : public CGAL::Type_equality_wrapper<
                MyCartesian_base<MyKernel<FT_>, CGAL::Cartesian<FT_> >,
                MyKernel<FT_> >
{};
#endif // MYKERNEL_H

最后,我们举例说明如何使用这个新内核。判断和计算构造与新点一起使用,它们可用于构造线段和三角形,以及基本库中的数据结构,因为 Delaunay 三角剖分与它们一起使用。

内核本身可以通过将其插入到 Filtered_kernel 中而变得健壮。

#include <CGAL/basic.h>
#include <CGAL/Filtered_kernel.h>
#include <CGAL/Delaunay_triangulation_2.h>
#include <CGAL/squared_distance_2.h>
#include <cassert>
#include "MyKernel.h"
#include "MyPointC2_iostream.h"
typedef MyKernel<double>                   MK;
typedef CGAL::Filtered_kernel_adaptor<MK>  K;
typedef CGAL::Delaunay_triangulation_2<K>  Delaunay_triangulation_2;
typedef K::Point_2         Point;
typedef K::Segment_2       Segment;
typedef K::Ray_2           Ray;
typedef K::Line_2          Line;
typedef K::Triangle_2      Triangle;
typedef K::Iso_rectangle_2 Iso_rectangle;
const int RED= 1;
const int BLACK=2;
int main()
{
  Point a(0,0), b(1,0), c(1,1), d(0,1);
  a.color()=RED;
  b.color()=BLACK;
  d.color()=RED;
  Delaunay_triangulation_2 dt;
  dt.insert(a);
  K::Orientation_2 orientation;
  orientation(a,b,c);
  Point p(1,2), q;
  p.color() = RED;
  q.color() = BLACK;
  std::cout << p << std::endl;
  K::Compute_squared_distance_2 squared_distance;
  std::cout << "squared_distance(a, b) == "
            << squared_distance(a, b) << std::endl;
  Segment s1(p,q), s2(a, c);
  K::Construct_midpoint_2 construct_midpoint_2;
  Point mp = construct_midpoint_2(p,q);
  std::cout << "midpoint(" << p << " , " << q << ") == " << mp << std::endl;
  assert(s1.source().color() == RED);
  K::Intersect_2 intersection;
  CGAL::cpp11::result_of<K::Intersect_2(Segment, Segment)>::type 
    intersect = intersection(s1, s2);
  K::Construct_cartesian_const_iterator_2 construct_it;
  K::Cartesian_const_iterator_2  cit = construct_it(a);
  assert(*cit == a.x());
  cit = construct_it(a,0);
  cit--;
  assert(*cit == a.y());
  Line l1(a,b), l2(p, q);
  intersection(l1, l2);
  intersection(s1, l1);
  Ray r1(d,b), r2(d,c);
  intersection(r1, r2);
  intersection(r1, l1);
  squared_distance(r1, r2);
  squared_distance(r1, l2);
  squared_distance(r1, s2);
  Triangle t1(a,b,c), t2(a,c,d);
  intersection(t1, t2);
  intersection(t1, l1);
  intersection(t1, s1);
  intersection(t1, r1);
  Iso_rectangle i1(a,c), i2(d,p);
  intersection(i1, i2);
  intersection(i1, s1);
  intersection(i1, r1);
  intersection(i1, l1);
  t1.orientation();
  std::cout << s1.source() << std::endl;
  std::cout << t1.bbox() << std::endl;
  std::cout << "done" << std::endl;
  return 0;
}

5.3、限制

点类必须具有成员函数 x() 和 y() (以及3D点的z() )。我们可能会介绍处理坐标访问的函数对象。

当我们在 MyKernel::Point_2 和 Point_2 之间强制类型相等时,以颜色所为第三个参数的构造函数不可用。

6、投影特征类

将 2D 算法应用于平面上 3D 点的投影有时很有用。示例是三角地形,它们是具有高程的点,或者是从平行切片中重建的表面,人们想要检查多边形的简单性或方向。

为此,CGAL 提供了几个投影特征类,它们是 2D 三角剖分、2D 多边形和 2D 凸包特征类的特征类概念的模型。投影特征类在概念的“是模型”部分中列出。

标签:4.11,Kernel,typedef,const,Linear,Point,CGAL,内核,类型
来源: https://www.cnblogs.com/dxuan369/p/16096381.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有