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
トラブルシューティング(ブートストラップ時)
ブートストラップ時に以下のようなエラーが発生する場合があります:
-
権限エラー:
- 管理者権限でコマンドプロンプトを開いて実行してください
- アンチウイルスソフトが干渉している可能性があります
-
ネットワークエラー:
- プロキシ設定を確認してください
- 企業ネットワーク内の場合、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 インストール時)
-
ビルドエラー:
- Visual Studioの再インストールが必要な場合があります
- C++開発ツールが正しくインストールされているか確認してください
-
依存関係エラー:
-
vcpkg install boost:x64-windows
を実行して、Boostを個別にインストールしてみてください - Visual Studio再起動が必要な場合があります
-
応用例の紹介
特にSiv3DのPolygonクラスとCGALのPolygon_2クラスの相互変換を実装することで、両者の利点を活かした幾何計算と視覚化が可能になります。今回はミンコフスキー差を用いてPolygon同士の当たり判定をしました。他にも様々な連携をためしてみてください。
# 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);
}
}
}