交互式 Voronoi 多边形?

Interactive Voronoi polygons?

我正在尝试创建一个如下所示的 Voronoi 图(这也是我目前的输出):

我的问题是如何使它具有交互性。 我的意思是我希望能够设置黑点,然后它应该适应地图并创建一个 Voronoi 多边形。

我的项目的代码结构包含 2 header 类 一个是计算 Voronoi 的地方,第二个是位图,最后一个 main.cpp.

类 如下所示:

voronoi.h

    #pragma once

#include "stdafx.h"
#include "MyBitmap.h"
#include <windows.h>
#include <vector>
#include <string>

static int DistanceSqrd(const Point& point, int x, int y) {
    int xd = x - point.x;
    int yd = y - point.y;
    return (xd * xd) + (yd * yd);
}

class Voronoi {
public:
    void Make(MyBitmap* bmp, int count) {
        bmp_ = bmp;
        CreatePoints(count);
        CreateColors();
        CreateSites();
        SetSitesPoints();
    }

    void CreateSites() { //voronoi triangulation mathematics
        int w = bmp_->width(), h = bmp_->height(), d;

        for (int hh = 0; hh < h; hh++) {
            for (int ww = 0; ww < w; ww++) {

                int ind = -1, dist = INT_MAX;

                for (size_t it = 0; it < points_.size(); it++) {
                    const Point& p = points_[it];
                    d = DistanceSqrd(p, ww, hh); //lines between points
                    if (d < dist) {
                        dist = d;
                        ind = it;
                    }
                }

                if (ind > -1)
                    SetPixel(bmp_->hdc(), ww, hh, colors_[ind]);
                else
                    __asm nop // should never happen!
            }
        }
    }
    void SetSitesPoints() {
        for (const auto& point : points_) {
            int x = point.x, y = point.y;

            for (int i = -1; i < 2; i++)
                for (int j = -1; j < 2; j++)
                    SetPixel(bmp_->hdc(), x + i, y + j, 0);
        }
    }

    void CreatePoints(int count) {
        const int w = bmp_->width() - 20, h = bmp_->height() - 20;

        for (int i = 0; i < count; i++) {
            points_.push_back({ rand() % w + 10, rand() % h + 10 }); //where the black points are placed.
        }
    }

    void CreateColors() {
        for (size_t i = 0; i < points_.size(); i++) {
            DWORD c = RGB(255, 0,0); //red
            DWORD d = RGB(0, 0, 255); //blue

            colors_.push_back(c);
            colors_.push_back(d);
        }
    }

    vector<Point> points_;
    vector<DWORD> colors_;
    MyBitmap* bmp_;
};

这是负责 voronoi 算法。

下一个 MyBitmap.h:

    #pragma once
#include "stdafx.h"
#include <windows.h>
#include <vector>
#include <string>

using namespace std;

struct Point {
    int x, y;
};

class MyBitmap {
public:
    MyBitmap() : pen_(nullptr) {}
    ~MyBitmap() {
        DeleteObject(pen_);
        DeleteDC(hdc_);
        DeleteObject(bmp_);
    }

    bool Create(int w, int h) {
        BITMAPINFO  bi;
        ZeroMemory(&bi, sizeof(bi));

        bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
        bi.bmiHeader.biBitCount = sizeof(DWORD) * 8;
        bi.bmiHeader.biCompression = BI_RGB;
        bi.bmiHeader.biPlanes = 1;
        bi.bmiHeader.biWidth = w;
        bi.bmiHeader.biHeight = -h;

        void *bits_ptr = nullptr;
        HDC dc = GetDC(GetConsoleWindow());
        bmp_ = CreateDIBSection(dc, &bi, DIB_RGB_COLORS, &bits_ptr, nullptr, 0);
        if (!bmp_) return false;

        hdc_ = CreateCompatibleDC(dc);
        SelectObject(hdc_, bmp_);
        ReleaseDC(GetConsoleWindow(), dc);

        width_ = w;
        height_ = h;

        return true;
    }

    void SetPenColor(DWORD clr) {
        if (pen_) DeleteObject(pen_);
        pen_ = CreatePen(PS_SOLID, 1, clr);
        SelectObject(hdc_, pen_);
    }

    bool SaveBitmap(const char* path) {
        HANDLE file = CreateFileA(path, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
        if (file == INVALID_HANDLE_VALUE) {
            return false;
        }

        BITMAPFILEHEADER fileheader;
        BITMAPINFO infoheader;
        BITMAP bitmap;
        GetObject(bmp_, sizeof(bitmap), &bitmap);

        DWORD* dwp_bits = new DWORD[bitmap.bmWidth * bitmap.bmHeight];
        ZeroMemory(dwp_bits, bitmap.bmWidth * bitmap.bmHeight * sizeof(DWORD));
        ZeroMemory(&infoheader, sizeof(BITMAPINFO));
        ZeroMemory(&fileheader, sizeof(BITMAPFILEHEADER));

        infoheader.bmiHeader.biBitCount = sizeof(DWORD) * 8;
        infoheader.bmiHeader.biCompression = BI_RGB;
        infoheader.bmiHeader.biPlanes = 1;
        infoheader.bmiHeader.biSize = sizeof(infoheader.bmiHeader);
        infoheader.bmiHeader.biHeight = bitmap.bmHeight;
        infoheader.bmiHeader.biWidth = bitmap.bmWidth;
        infoheader.bmiHeader.biSizeImage = bitmap.bmWidth * bitmap.bmHeight * sizeof(DWORD);

        fileheader.bfType = 0x4D42;
        fileheader.bfOffBits = sizeof(infoheader.bmiHeader) + sizeof(BITMAPFILEHEADER);
        fileheader.bfSize = fileheader.bfOffBits + infoheader.bmiHeader.biSizeImage;

        GetDIBits(hdc_, bmp_, 0, height_, (LPVOID)dwp_bits, &infoheader, DIB_RGB_COLORS);

        DWORD wb;
        WriteFile(file, &fileheader, sizeof(BITMAPFILEHEADER), &wb, nullptr);
        WriteFile(file, &infoheader.bmiHeader, sizeof(infoheader.bmiHeader), &wb, nullptr);
        WriteFile(file, dwp_bits, bitmap.bmWidth * bitmap.bmHeight * 4, &wb, nullptr);
        CloseHandle(file);

        delete[] dwp_bits;
        return true;
    }

    HDC hdc() { return hdc_; }
    int width() { return width_; }
    int height() { return height_; }

private:
    HBITMAP bmp_;
    HDC hdc_;
    HPEN pen_;
    int width_, height_;
};

最后但同样重要的是 main.cpp:

    // Voronoi Diagram Game.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <windows.h>
#include <SDL_image.h>
#include <iostream>
#include <stdio.h>
#include <vector>
#include <string>
#include <SDL.h>
#include "MyBitmap.h"
#include "Voronoi.h"

//Screen dimension constants
const int SCREEN_WIDTH = 740;
const int SCREEN_HEIGHT = 550;

bool init();

//Loads media
bool loadMedia();

//Frees media and shuts down SDL
void close();

//Loads individual image
SDL_Surface* loadSurface(std::string path);

//The window we'll be rendering to
SDL_Window* gWindow = NULL;

//The surface contained by the window
SDL_Surface* gScreenSurface = NULL;

//Current displayed PNG image
SDL_Surface* gImageSurface = NULL;

SDL_Rect sourceRect;
SDL_Rect destRect;
//InputHandler * handler = new InputHandler();

int m_count = 0;

bool init()
{
    //Initialization flag
    bool success = true;
    //where on the screen it is shown
    sourceRect.x = 20;
    sourceRect.y = -10;
    sourceRect.w = 10;
    sourceRect.y = 10;
    //how big the window is that the sprite is shown in
    destRect.x = 0;
    destRect.y = 0;
    destRect.w = 740;
    destRect.h = 550;

    //Initialize SDL
    if (SDL_Init(SDL_INIT_VIDEO) < 0)
    {
        printf("SDL could not initialize! SDL Error: %s\n", SDL_GetError());
        success = false;
    }
    else
    {
        //Create window
        gWindow = SDL_CreateWindow("SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
        if (gWindow == NULL)
        {
            printf("Window could not be created! SDL Error: %s\n", SDL_GetError());
            success = false;
        }
        else
        {
            //Initialize PNG loading
            int imgFlags = IMG_INIT_PNG;
            if (!(IMG_Init(imgFlags) & imgFlags))
            {
                printf("SDL_image could not initialize! SDL_image Error: %s\n", IMG_GetError());
                success = false;
            }
            else
            {
                //Get window surface
                gScreenSurface = SDL_GetWindowSurface(gWindow);
            }
        }
    }
    return success;
}

bool loadMedia()
{
    //Loading success flag
    bool success = true;

    //Load PNG surface
    gImageSurface = SDL_LoadBMP("v.bmp");
    if (gImageSurface == NULL)
    {
        printf("Failed to load PNG image!\n");
        success = false;
    }

    return success;
}

void close()
{
    //Free loaded image
    SDL_FreeSurface(gImageSurface);
    gImageSurface = NULL;

    //Destroy window
    SDL_DestroyWindow(gWindow);
    gWindow = NULL;

    //Quit SDL subsystems
    IMG_Quit();
    SDL_Quit();
}

SDL_Surface* loadSurface(std::string path)
{
    //The final optimized image
    SDL_Surface* optimizedSurface = NULL;
    //Load image at specified path
    SDL_Surface* loadedSurface = IMG_Load(path.c_str());
    if (loadedSurface == NULL)
    {
        printf("Unable to load image %s! SDL_image Error: %s\n", path.c_str(), IMG_GetError());
    }
    else
    {
        //Convert surface to screen format
        optimizedSurface = SDL_ConvertSurface(loadedSurface, gScreenSurface->format, NULL);
        if (optimizedSurface == NULL)
        {
            printf("Unable to optimize image %s! SDL Error: %s\n", path.c_str(), SDL_GetError());
        }
        //Get rid of old loaded surface
        SDL_FreeSurface(loadedSurface);
    }

    return optimizedSurface;
}

int main(int argc, char* args[])
{
    MyBitmap bmp;
    Voronoi v;

    srand(GetTickCount());
    int clickCount = 0;

    bmp.Create(512, 512); //how big the bitmap is
    bmp.SetPenColor(0);
    v.Make(&bmp, 50);

    //Start up SDL and create window
    if (!init())
    {
        printf("Failed to initialize!\n");
    }
    else
    {
        // create the newbmp
        bmp.SaveBitmap("v.bmp");
        //Load media
        if (!loadMedia())
        {
            printf("Failed to load media!\n");
        }
        else
        {
            //Main loop flag
            bool quit = false;

            SDL_Event e;
            while (!quit)
            {
                //Handle events on queue
                while (SDL_PollEvent(&e) != 0)
                {
                    if (e.type == SDL_QUIT)
                    {
                        quit = true;
                    }
                    else if (e.type == SDL_MOUSEBUTTONDOWN) {
                        if (e.button.button == SDL_BUTTON_LEFT) {
                            //TO DO: create a black dot
                            //TO DO: add voronoi polygon according to the others
                            cout << "left button pressed" << endl;
                            clickCount++;
                        }
                    }
                }
                //BitBlt(GetDC(GetConsoleWindow()), 20, 20, 512, 512, bmp.hdc(), 0, 0, SRCCOPY); //shows it in the cmd
                //Apply the image
                SDL_BlitSurface(gImageSurface, &destRect, gScreenSurface, &sourceRect);

                //Update the surface
                SDL_UpdateWindowSurface(gWindow);

            }
        }
    }
    //Free resources and close SDL
    close();
    return 0;
}

总而言之,我希望能够放置适应其他 Voronoi 多边形的黑色 dots/Voronoi 多边形,但我还没有找到一种方法来做到这一点。

它必须通过鼠标点击和我现在正在处理的鼠标位置来完成。

如果在没有任何 Voronoi 多边形的情况下启动 Blanco 也可以。

我从 here 那里得到了一个很好的例子。

我对它的作用有粗略的了解(MyBitmap.h 除外),但我从 here.

获得了源代码

我终于得到了答案。 我在 voronoi 中创建了一个方法,它获取鼠标位置上的点,如下所示:

void GetPointsOnMousePosition() {
        int x, y;
        SDL_GetMouseState(&x, &y);
        int d;
        isClicked = true;
        cout << x << " and " << y << endl;

        points_.push_back({ x, y }); //where the black points are placed.
        CreateColors();
        CreateSites();
        SetSitesPoints();
    }

这会获取鼠标位置并将其设置在 x 和 y 上并将其推入点数组。 在 main.cpp 中,我要求使用 pointsOnMousePosition 方法重新加载 bmp 并将其保存如下:

if (e.button.button == SDL_BUTTON_LEFT) {
                        //bmp.~MyBitmap();
                        cout << "left button pressed" << endl;
                        v.GetPointsOnMousePosition();

                        //adjusts the voronoi map
                        bmp.SaveBitmap("v.bmp");
                        gImageSurface = SDL_LoadBMP("v.bmp");
                        cout << "points added!" << endl;
                        isClicked = false;
                    }

希望这对以后的人有所帮助。