1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Siv3DAdvent Calendar 2024

Day 5

OpenSiv3DでCGALを使う

Last updated at Posted at 2024-12-05

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

前提条件

以下の環境が必要です:

  • Windows 10/11
  • Visual Studio 2019または2022(C++開発環境)
  • Git
  • 十分なディスク容量(最低10GB程度)

vcpkgのインストール

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

# 適当なディレクトリを作成(例: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をインストールします:

.\vcpkg.exe install yasm-tool:x86-windows

CGAL本体のインストール

# 64bit版のインストール(推奨)
.\vcpkg.exe install cgal:x64-windows

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

システム統合

.\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

# 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);
		}
	}
}


1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?