OpenSiv3DでCGALを使う

CGALは2D/3Dの幾何処理に特化したライブラリです。OpenSiv3Dと連携すれば自作のアプリケーションにより高度な処理を組み込めるはずです。

# 前提条件

以下の環境が必要です：
- Windows 10/11
- Visual Studio 2019または2022（C++開発環境）
- Git
- 十分なディスク容量（最低10GB程度）

# vcpkgのインストール

## vcpkgの取得とブートストラップ

```batch
# 適当なディレクトリを作成（例：C:\dev）
mkdir C:\dev
cd C:\dev

# vcpkgをクローン
git clone https://github.com/microsoft/vcpkg

# vcpkgディレクトリに移動
cd vcpkg

# ブートストラップスクリプトを実行
.\bootstrap-vcpkg.bat
```

## トラブルシューティング（ブートストラップ時）

ブートストラップ時に以下のようなエラーが発生する場合があります：

1. **権限エラー**：
   - 管理者権限でコマンドプロンプトを開いて実行してください
   - アンチウイルスソフトが干渉している可能性があります

2. **ネットワークエラー**：
   - プロキシ設定を確認してください
   - 企業ネットワーク内の場合、IT部門に確認が必要な場合があります

# CGALのインストール

## 事前準備（重要）

GMPのビルドに必要なYASMをインストールします：
```batch
.\vcpkg.exe install yasm-tool:x86-windows
```

## CGAL本体のインストール

```batch
# 64bit版のインストール（推奨）
.\vcpkg.exe install cgal:x64-windows

# 必要に応じて32bit版もインストール可能
.\vcpkg.exe install cgal:x86-windows
```

## システム統合

```batch
.\vcpkg.exe integrate install
```

## トラブルシューティング（CGAL インストール時）

1. **ビルドエラー**：
   - Visual Studioの再インストールが必要な場合があります
   - C++開発ツールが正しくインストールされているか確認してください

2. **依存関係エラー**：
   - `vcpkg install boost:x64-windows`を実行して、Boostを個別にインストールしてみてください
   - Visual Studio再起動が必要な場合があります

# 応用例の紹介

特にSiv3DのPolygonクラスとCGALのPolygon_2クラスの相互変換を実装することで、両者の利点を活かした幾何計算と視覚化が可能になります。今回はミンコフスキー差を用いてPolygon同士の当たり判定をしました。他にも様々な連携をためしてみてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/34295/46fbde52-3f7f-78ec-531e-531fd54a8138.png)


```cpp
# include <Siv3D.hpp> // Siv3D v0.6.15


# include <CGAL/Exact_predicates_exact_constructions_kernel.h>
# include <CGAL/minkowski_sum_2.h>
# include <CGAL/Polygon_vertical_decomposition_2.h>

using Kernel = CGAL::Exact_predicates_exact_constructions_kernel;
using Point_2 = Kernel::Point_2;
using CGAL_Polygon_2 = CGAL::Polygon_2<Kernel>;
using CGAL_Polygon_with_holes_2 = CGAL::Polygon_with_holes_2<Kernel>;


// Siv3D の Polygon から CGAL の Polygon_with_holes_2 への変換
[[nodiscard]] auto SivToCGAL(const Polygon& sivPolygon) -> CGAL_Polygon_with_holes_2 {
	std::vector<Point_2> outer_vertices;
	outer_vertices.reserve(sivPolygon.outer().size());

	for (const auto& point : sivPolygon.outer()) {
		outer_vertices.emplace_back(point.x, point.y);
	}

	std::vector<CGAL_Polygon_2> holes;
	holes.reserve(sivPolygon.inners().size());

	for (const auto& hole : sivPolygon.inners()) {
		std::vector<Point_2> hole_vertices;
		hole_vertices.reserve(hole.size());

		for (const auto& point : hole) {
			hole_vertices.emplace_back(point.x, point.y);
		}
		holes.emplace_back(hole_vertices.begin(), hole_vertices.end());
	}

	return CGAL_Polygon_with_holes_2(
		CGAL_Polygon_2(outer_vertices.begin(), outer_vertices.end()),
		holes.begin(),
		holes.end()
	);
}

// CGAL の Polygon_with_holes_2 から Siv3D の Polygon への変換
[[nodiscard]] auto CGALToSiv(const CGAL_Polygon_with_holes_2& cgalPolygon) -> Polygon {
	const auto& outer = cgalPolygon.outer_boundary();
	Array<Vec2> outer_vertices;
	outer_vertices.reserve(std::distance(outer.vertices_begin(), outer.vertices_end()));

	for (const auto& vertex : outer.vertices()) {
		outer_vertices.emplace_back(CGAL::to_double(vertex.x()),
			CGAL::to_double(vertex.y()));
	}

	Array<Array<Vec2>> holes;
	holes.reserve(cgalPolygon.number_of_holes());

	for (const auto& hole : cgalPolygon.holes()) {
		Array<Vec2> hole_vertices;
		hole_vertices.reserve(std::distance(hole.vertices_begin(), hole.vertices_end()));

		for (const auto& vertex : hole.vertices()) {
			hole_vertices.emplace_back(CGAL::to_double(vertex.x()),
				CGAL::to_double(vertex.y()));
		}
		holes.push_back(std::move(hole_vertices));
	}

	return Polygon{ outer_vertices, holes };
}

// Minkowskiの和を計算する関数
Polygon MinkowskiSum(const Polygon& polygon1, const Polygon& polygon2)
{
	// CGALのポリゴンに変換
	CGAL_Polygon_with_holes_2 cgal_p1 = SivToCGAL(polygon1);
	CGAL_Polygon_with_holes_2 cgal_p2 = SivToCGAL(polygon2);

	// Minkowski和を計算
	CGAL::Polygon_vertical_decomposition_2<Kernel>::Traits_2 traits;
	CGAL::Polygon_vertical_decomposition_2<Kernel> vertical_decomp(traits);
	CGAL_Polygon_with_holes_2 sum = minkowski_sum_by_reduced_convolution_2(cgal_p1, cgal_p2);// , vertical_decomp, traits);

	// 結果をSiv3Dのポリゴンに変換して返す
	return CGALToSiv(sum);
}

// ポリゴンの頂点をすべて負にする関数
[[nodiscard]] Polygon NegativePolygon(const Polygon& polygon)
{
	// 外周の頂点を反転
	Array<Vec2> outer_vertices;
	outer_vertices.reserve(polygon.outer().size());

	for (const auto& point : polygon.outer()) {
		outer_vertices.emplace_back(-point.x, -point.y);
	}

	// 穴の頂点を反転
	Array<Array<Vec2>> holes;
	holes.reserve(polygon.inners().size());

	for (const auto& hole : polygon.inners()) {
		Array<Vec2> hole_vertices;
		hole_vertices.reserve(hole.size());

		for (const auto& point : hole) {
			hole_vertices.emplace_back(-point.x, -point.y);
		}
		holes.push_back(std::move(hole_vertices));
	}

	return Polygon{ outer_vertices, holes };
}

// Minkowskiの差を計算する関数
[[nodiscard]] Polygon MinkowskiDifference(const Polygon& polygon1, const Polygon& polygon2)
{
	// polygon2 の頂点をすべて負にしたポリゴンを作成
	Polygon negative_polygon2 = NegativePolygon(polygon2);

	// Minkowski和を計算
	return MinkowskiSum(polygon1, negative_polygon2);
}

void Main()
{
	// 固定の多角形
	const Polygon polygon1{
		{ Vec2{ 400, 100 }, Vec2{ 600, 300 }, Vec2{ 500, 500 },
		  Vec2{ 400, 400 }, Vec2{ 300, 500 }, Vec2{ 200, 300 } },
		{ { Vec2{ 450, 250 }, Vec2{ 350, 250 }, Vec2{ 350, 350 }, Vec2{ 450, 350 } } }
	};

	// マウスで動かせる小さな四角形
	const Polygon basePolygon2 = Shape2D::Star(80).asPolygon();

	Font font(50);
	
	while (System::Update())
	{
		// マウス位置に四角形を配置
		const Polygon polygon2 = basePolygon2.movedBy(Cursor::Pos());
		
		// 2つのポリゴンを描画
		polygon1.draw(Palette::Skyblue);
		polygon2.draw(Palette::Pink);
		
		// Minkowski差を計算
		const Polygon difference = MinkowskiDifference(polygon1, polygon2);
		
		// 原点（0,0）が Minkowski差の中に含まれているかをチェック
		const bool isIntersecting = difference.contains(Vec2{ 0, 0 });

		difference.drawFrame(3.0, Palette::Aliceblue);
		
		// 交差状態を表示
		if (isIntersecting)
		{
			
			font(U"Intersecting!").drawAt(Scene::Center().x, 50, Palette::Red);
		}
		else
		{
			font(U"Not intersecting").drawAt(Scene::Center().x, 50, Palette::Green);
		}
	}
}


```