[imgui] 2. Plotter 구현
2023년 7월 5일
목표 #
- ImPlot을 사용해서 sin(x), cos(x) 데이터 플로팅.
- ImPlot : 그래프를 플로팅 하는 ImGui 기반의 데이터 시각화 라이브러리이다.

구현코드 #
메인함수 #
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 #
체크박스를 그려주는 함수이다.
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 #
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}