1. 程式人生 > 其它 >CGAL 4.11.3 - 2D and 3D Linear Geometry Kernel

CGAL 4.11.3 - 2D and 3D Linear Geometry Kernel

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 凸包特徵類的特徵類概念的模型。投影特徵類在概念的“是模型”部分中列出。