aboutsummaryrefslogtreecommitdiff
path: root/src/ui.cpp
blob: 8e4e27519123a3b99de7d1513e3c3b0b30975e0c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include "ui.hpp"
#include "ogl.hpp"
#include "sandbox.hpp"

#include <imgui.h>
#include <memory>

static void AddSand(Sandbox& sb) {
    for (int y = 80; y <= 90; ++y) {
        for (int x = 10; x <= 13; ++x) {
            sb.set_sand(x, y, Tile{ .so = Tile::SAND });
        }
    }
}

struct ImRect {
    ImVec2 top_left;
    ImVec2 bottom_right;
};

/// Helpers for displaying and coordinate space tracking of the sandbox image display.
struct SandboxDisplay {
    float tile_size;
    int sandbox_width = 40;
    int sandbox_height = 100;

    ImVec2 size_screenspace;
    ImVec2 origin;

    SandboxDisplay(float tile_size, const Sandbox& sb)
        : tile_size(tile_size)
        , sandbox_width{ sb.width }
        , sandbox_height{ sb.height }
        , size_screenspace(sandbox_width * tile_size, sandbox_height * tile_size) {}

    ImVec2 tr_sandbox2screen(Pt pt) const {
        return ImVec2(origin.x + tile_size * pt.x, origin.y - tile_size * pt.y);
    }

    // Assuming inclusive-inclusive rectangle
    ImRect tr_sandbox2screen(Rect r) const {
        return {
            ImVec2(origin.x + tile_size * r.bl.x, origin.y - tile_size * (r.tr.y + 1)),
            ImVec2(origin.x + tile_size * (r.tr.x + 1), origin.y - tile_size * r.bl.y),
        };
    }

    void show_image(ImTextureID texid) {
        auto img_topleft = ImGui::GetCursorScreenPos();
        ImGui::Image(texid, size_screenspace, ImVec2(0, 1), ImVec2(1, 0));
        // bottom-left corner of the sandbox, in screen space
        origin = img_topleft;
        origin.y += size_screenspace.y;
    }
};

void ShowEverything() {
    ImGui::Begin("Options");

    static float tile_size = 4.0f;
    static std::unique_ptr<Sandbox> sandbox = std::make_unique<Sandbox>(40, 100);
    static OglImage gl;

    ImGui::InputFloat("Tile size", &tile_size);

    {
        constexpr auto kModify = "Modify sandbox parameters";
        static int new_width, new_height;

        ImGui::Text("size = (%d, %d)", sandbox->width, sandbox->height);
        if (ImGui::Button("Modify")) {
            ImGui::OpenPopup(kModify);
            new_width = sandbox->width;
            new_height = sandbox->height;
        }
        if (ImGui::BeginPopup(kModify)) {
            ImGui::InputInt("Sandbox width", &new_width);
            ImGui::InputInt("Sandbox height", &new_height);
            if (ImGui::Button("Confirm")) {
                sandbox = std::make_unique<Sandbox>(new_width, new_height);
                ImGui::CloseCurrentPopup();
            }
            ImGui::SameLine();
            if (ImGui::Button("Cancel")) {
                ImGui::CloseCurrentPopup();
            }
            ImGui::EndPopup();
        }
    }

    static bool running = false;
    ImGui::Checkbox("Run", &running);
    static bool show_dirty_rect = true;
    ImGui::Checkbox("Show dirty rects", &show_dirty_rect);

    ImGui::Text("ncycle = %d", sandbox->ncycle);
    ImGui::SameLine();
    bool step = ImGui::Button("Step");

    if (step || running) {
        sandbox->simulate_step();
    }

    if (ImGui::Button("Add sand")) {
        AddSand(*sandbox);
    }

    ImGui::End();

    ImGui::Begin("Sandbox");
    // Update texture to the current state of the the sandbox.
    // Do this every frame, because it's easier than trying to track when did the sandbox change, which may be caused by:
    // - Initialization
    // - Simulation step
    // - User interaction (painting, save/load, etc.)
    gl.upload(reinterpret_cast<const char*>(sandbox->bitmap), sandbox->width, sandbox->height);

    SandboxDisplay dis(tile_size, *sandbox);
    dis.show_image(gl.as_imgui());

    if (show_dirty_rect) {
        auto dl = ImGui::GetWindowDrawList();
        auto [top_left, bottom_right] = dis.tr_sandbox2screen(sandbox->dirty_writeto);
        dl->AddRect(top_left, bottom_right, IM_COL32(255, 0, 0, 255));
    }
    ImGui::End();

    ImGui::ShowDemoWindow();
}