[imgui] 2. Plotter 구현

[imgui] 2. Plotter 구현

2023년 7월 5일
imgui

목표 #

  • ImPlot을 사용해서 sin(x), cos(x) 데이터 플로팅.
  • ImPlot : 그래프를 플로팅 하는 ImGui 기반의 데이터 시각화 라이브러리이다.
    Plotter

구현코드 #

메인함수 #

ImPlot 라이브러리의 자원과 컨텍스트 초기화, 정리를 수행하는 코드

 1// main.cpp
 2ImPlot::CreateContext();        // ImPlot 초기화
 3while (!glfwWindowShouldClose(window))
 4{
 5    start_cycle();
 6
 7    ImGui::NewFrame();
 8    render(window_obj);
 9    ImGui::Render();
10
11    end_cycle(window);
12}
13ImPlot::DestroyContext();       // ImPlot 정리

Plotter의 구현 코드

 1#include <cstdint>
 2#include <string_view>
 3#include <array>
 4#include <set>
 5
 6class WindowClass
 7{
 8public:
 9    // 표시할 그래프 이름들을 지정. 체크박스 label로도 사용된다. 
10    constexpr static auto functionNames = std::array<std::string_view, 3>{
11        "unk", "sin(x)", "cos(x)",
12    };
13    // 함수 인덱스 순서 enum값
14    enum class Function
15    {
16        NONE,
17        SIN,
18        COS,
19    };
20
21
22public:
23    WindowClass() : selectedFunctions({}) {};
24    void Draw(std::string_view label);
25
26private:
27    void DrawSelection();   // 체크박스를 그리는 함수
28    void DrawPlot();        // ImPlot을 이용해서 그래프를 그리는 함수
29
30    // 이름과 enum 매핑
31    Function functionNameMapping(std::string_view function_name);
32
33    // 그래프를 그리기 위해 y값을 계산하는 함수
34    double evaluateFunction(const Function function, const double x);
35
36public:
37    // 현재 선택된 함수 리스트
38    std::set<Function> selectedFunctions;
39
40};
41
42void render(WindowClass &window_obj);


DrawSelection #

plotter_checkbox 체크박스를 그려주는 함수이다.

 1// 함수 이름을 enum으로 변경
 2WindowClass::Function WindowClass::functionNameMapping(
 3    std::string_view function_name)
 4{
 5    if (function_name == std::string_view{"sin(x)"})
 6        return WindowClass::Function::SIN;
 7    if (function_name == std::string_view{"cos(x)"})
 8        return WindowClass::Function::COS;
 9    return WindowClass::Function::NONE;
10}
11
12// 실제로 체크박스를 그리는 작업
13void WindowClass::DrawSelection()
14{
15    // 전체 정의된 함수이름들에서 하나씩 가져다가 체크박스로 그려준다.
16    // 이때 selectedFunctions에 포함되어 있으면 체크된 체크박스를 그린다. 
17    for (const auto func_name : functionNames)
18    {
19        // 함수이름을 전달해서 enum값으로 변경
20        const auto curr_function = functionNameMapping(func_name);
21
22        // selectedFunctions에 포함되어 있는지 확인.
23        auto selected = selectedFunctions.contains(curr_function);
24
25        // 클릭 시 리턴값은 true
26        // 2번째 인자로 bool* 를 넘겨서 체크 여부 업데이트
27        if (ImGui::Checkbox(func_name.data(), &selected)) {
28            // 이번에 눌러서 체크가 됐다면 selectedFunctions에 추가 아니라면 지우기
29            if (selected) {
30                selectedFunctions.insert(curr_function);
31            }
32            else {
33                selectedFunctions.erase(curr_function);
34            }
35        }
36    }
37}

DrawPlot #

plotter_plot x, y 좌표를 계산해서 그래프로 그려주는 함수이다.

 1// x,y 좌표를 받아서 Plot을 그린다. 
 2void WindowClass::DrawPlot()
 3{
 4    static constexpr auto num_points = 10'000;      // 최대 10000개의 Plot
 5    static constexpr auto x_min = -100.0;           // -100.0 ~ 100.0 사이의 Plot을 표시할 예정
 6    static constexpr auto x_max = 100.0;            
 7    static constexpr auto x_step = (x_max - x_min) / num_points;  // Plot의 간격
 8
 9    static auto xs = std::array<double, num_points>{};            // 각 Plot의 x, y좌표
10    static auto ys = std::array<double, num_points>{};
11
12    // ImPlot의 시작을 알리는 함수.
13    // 2번째 인자로 {-1, -1}을 전달하면 Window에서 남은 크기를 다 채운다는 뜻
14    ImPlot::BeginPlot("###Plot", ImVec2(-1.0F, -1.0F), ImPlotFlags_NoTitle);
15
16    // 만약 선택된 그래프가 없다면, 아무것도 그리지 않고 종료
17    if (selectedFunctions.size() == 0 ||
18        (selectedFunctions.size() == 1 &&
19         *selectedFunctions.begin() == Function::NONE))
20    {
21        ImPlot::EndPlot();
22        return;
23    }
24
25    for (const auto& function : selectedFunctions)
26    {
27        auto x = x_min;
28        // -100 ~ 100 사이의 Plot 데이터 추가
29        for (std::size_t i = 0; i < num_points; ++i)
30        {
31            xs[i] = x;
32            ys[i] = evaluateFunction(function, x);    // x에 해당하는 y값 계산
33            x += x_step;
34        }
35
36        const auto plot_label =
37            fmt::format("##function{}", static_cast<int>(function));
38
39        // Plot을 선으로 그리기
40        ImPlot::PlotLine(plot_label.data(), xs.data(), ys.data(), num_points);
41        // Plot을 점으로 그리기
42        // ImPlot::PlotScatter(plot_label.data(), xs.data(), ys.data(), num_points);
43    }
44    ImPlot::EndPlot();
45}
comments powered by Disqus