LoginSignup
3
0

More than 3 years have passed since last update.

Polygonからの距離を算出する関数を活用する【OpenSiv3D】

Last updated at Posted at 2020-12-13

OpenSiv3D には Polygon からの距離を取得する関数 Geometry2D::Distance があります。この関数を使うと様々なエフェクトを作成したり、数値最適化に活用できます。今回は、マウスで描いたポリゴンから距離を算出して、等高線を描くサンプルを紹介します。

20201208-154801-025.png

Main.cpp
# include <Siv3D.hpp>

void Main()
{
    // キャンバスのサイズ
    constexpr Size canvasSize(800, 600);

    // ペンの太さ
    constexpr int32 thickness = 4;

    // 書き込み用の画像データを用意
    Image image(canvasSize, Color(0, 0));

    // 表示用のテクスチャ(内容を更新するので DynamicTexture)
    DynamicTexture texture(image);

    // 作成した Polygon の配列
    Array<Polygon> polygons;


    //等高線
    Array<MultiPolygon> contours;

    //等高線の数
    constexpr size_t contourNum = 20;

    //等高線の感覚
    constexpr double offset = 20.0;

    while (System::Update())
    {
        // ペンの色
        const Color penColor = HSV(polygons.size() * 20);

        if (MouseL.pressed())
        {
            // 書き込む線の始点は直前のフレームのマウスカーソル座標
            // (初回はタッチ操作時の座標のジャンプを防ぐため、現在のマウスカーソル座標にする)
            const Point from = MouseL.down() ? Cursor::Pos() : Cursor::PreviousPos();

            // 書き込む線の終点は現在のマウスカーソル座標
            const Point to = Cursor::Pos();

            // image に線を書き込む
            Line(from, to).overwrite(image, thickness, penColor);

            // 書き込み終わった image でテクスチャを更新
            texture.fill(image);
        }
        else if (MouseL.up())
        {
            // 画像の非透過部分から Polygon を作成(穴無し)
            if (const Polygon polygon = image.alphaToPolygon(1, false))
            {
                // 少し単純化してから追加
                polygons << polygon.simplified(2.0);
            }                           


            {               
                Array<double> distances;
                Grid<double> distanceGrid(image.size());
                contours.clear();

                for (auto p : step(distanceGrid.size()))
                {
                    for (const auto& polygon : polygons)
                    {
                        distances << Geometry2D::Distance(p, polygon);
                    }

                    const double distance = *std::min_element(distances.begin(), distances.end());

                    distanceGrid[p.y][p.x] = distance;

                    distances.clear();
                }

                for (size_t i = 0; i < contourNum; ++i)
                {
                    // 画像データをリセット
                    image.fill(Color(0, 0));

                    for (auto p : step(distanceGrid.size()))
                    {
                        if (distanceGrid[p] < offset * i)
                        {
                            image[p] = penColor;
                        }
                    }

                    // 画像の非透過部分から Polygon を作成(穴無し)
                    if (const MultiPolygon polygon = image.alphaToPolygons(1, false))
                    {
                        // 少し単純化してから追加
                        contours << polygon.simplified(2.0);
                    }
                }
            }


            image.fill(Color(0, 0));
            // テクスチャを更新
            texture.fill(image);
        }


        //それぞれの 等高線を描画
        for (size_t i = 0; i < contours.size(); ++i)
        {
            contours[contours.size() - 1 - i].draw(HSV(i * 20, 0.4, 1.0)).drawFrame(4, HSV(i * 20));
        }


        // それぞれの Polygon を描画
        for (auto [i, polygon] : Indexed(polygons))
        {
            polygon.draw(HSV(i * 20, 0.4, 1.0))
                .drawFrame(4, HSV(i * 20));
        }

        // テクスチャを表示
        texture.draw();
    }
}
3
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
3
0