Skip to content

Instantly share code, notes, and snippets.

@michalpelka
Created January 23, 2026 21:38
Show Gist options
  • Select an option

  • Save michalpelka/ae39d8884ee9065a9a2266c4fd59d6f9 to your computer and use it in GitHub Desktop.

Select an option

Save michalpelka/ae39d8884ee9065a9a2266c4fd59d6f9 to your computer and use it in GitHub Desktop.
Example with raylib + imgui + implot
cmake_minimum_required(VERSION 4.0)
project(Mid360PatternModel)
# ---------- raylib ----------
set(CMAKE_C_STANDARD 99)
add_subdirectory(external/raylib)
# ---------- ImGui ----------
set(IMGUI_DIR ${CMAKE_SOURCE_DIR}/external/imgui)
add_library(imgui STATIC
${IMGUI_DIR}/imgui.cpp
${IMGUI_DIR}/imgui_demo.cpp
${IMGUI_DIR}/imgui_draw.cpp
${IMGUI_DIR}/imgui_tables.cpp
${IMGUI_DIR}/imgui_widgets.cpp
)
target_include_directories(imgui PUBLIC
${IMGUI_DIR}
)
# ---------- rlImGui ----------
set(RLIMGUI_DIR ${CMAKE_SOURCE_DIR}/external/rlimgui)
add_library(rlimgui STATIC
${RLIMGUI_DIR}/rlImGui.cpp
)
target_include_directories(rlimgui PUBLIC
${RLIMGUI_DIR}
${IMGUI_DIR}
)
target_link_libraries(rlimgui PUBLIC raylib imgui)
# ---------- ImPlot ----------
set(IMPLOT_DIR ${CMAKE_SOURCE_DIR}/external/implot)
add_library(implot STATIC
${IMPLOT_DIR}/implot.cpp
${IMPLOT_DIR}/implot_demo.cpp
${IMPLOT_DIR}/implot_items.cpp
)
target_include_directories(implot PUBLIC
${IMPLOT_DIR}
${IMGUI_DIR}
)
target_link_libraries(implot PUBLIC imgui)
# ---------- Main Executable ----------
set(CMAKE_CXX_STANDARD 20)
add_executable(Mid360PatternModel main.cpp)
target_link_libraries(Mid360PatternModel PRIVATE raylib imgui rlimgui implot)
#include <cmath>
#include <cstdio>
#include <raylib.h>
#include "imgui.h"
#include "implot.h"
#include "rlImGui.h"
// Simple ring buffer for time series plotting
struct ScrollingBuffer
{
int MaxSize = 2000;
int Offset = 0;
ImVector<ImVec2> Data;
ScrollingBuffer(int max_size = 2000)
{
MaxSize = max_size;
Data.reserve(MaxSize);
}
void AddPoint(float x, float y)
{
if (Data.size() < MaxSize)
{
Data.push_back(ImVec2(x, y));
}
else
{
Data[Offset] = ImVec2(x, y);
Offset = (Offset + 1) % MaxSize;
}
}
void Erase()
{
Data.shrink(0);
Offset = 0;
}
};
int main()
{
InitWindow(1280, 720, "raylib + rlImGui + ImPlot (complete example)");
SetTargetFPS(60);
// Setup ImGui for raylib
rlImGuiSetup(true);
// Setup ImPlot context (important)
ImPlot::CreateContext();
bool show_imgui_demo = false;
bool show_implot_demo = false;
// Plot state
ScrollingBuffer buf(2000);
float t = 0.0f;
// UI state
float freq = 1.0f;
float noise = 0.1f;
float amplitude = 1.0f;
bool pause = false;
while (!WindowShouldClose())
{
// --- simulation update ---
if (!pause)
{
t += GetFrameTime();
// fake telemetry signal: sin + noise
float y = amplitude * sinf(2.0f * PI * freq * t);
y += noise * ((float)GetRandomValue(-1000, 1000) / 1000.0f);
buf.AddPoint(t, y);
}
// --- draw ---
BeginDrawing();
ClearBackground(DARKGRAY);
DrawText("raylib background drawing is still available", 20, 20, 20, RAYWHITE);
rlImGuiBegin();
// ---------------- Main UI ----------------
ImGui::Begin("Controls");
ImGui::Text("FPS: %d", GetFPS());
ImGui::Checkbox("Pause", &pause);
ImGui::SliderFloat("Frequency (Hz)", &freq, 0.1f, 10.0f, "%.2f");
ImGui::SliderFloat("Amplitude", &amplitude, 0.0f, 3.0f, "%.2f");
ImGui::SliderFloat("Noise", &noise, 0.0f, 1.0f, "%.3f");
if (ImGui::Button("Clear plot"))
buf.Erase();
ImGui::Separator();
ImGui::Checkbox("ImGui demo", &show_imgui_demo);
ImGui::Checkbox("ImPlot demo", &show_implot_demo);
ImGui::End();
// ---------------- Plot window ----------------
ImGui::Begin("Telemetry");
ImGui::Text("Real-time scrolling plot");
ImGui::Separator();
// Plot config
static float history = 10.0f; // seconds of visible history
ImGui::SliderFloat("History (sec)", &history, 1.0f, 60.0f, "%.1f");
float t_now = t;
float t_min = t_now - history;
if (ImPlot::BeginPlot("Signal", ImVec2(-1, 400)))
{
// X axis = time, scroll with latest data
ImPlot::SetupAxis(ImAxis_X1, "time (s)", ImPlotAxisFlags_AutoFit);
ImPlot::SetupAxis(ImAxis_Y1, "value", ImPlotAxisFlags_AutoFit);
ImPlot::SetupAxisLimits(ImAxis_X1, t_min, t_now, ImGuiCond_Always);
// Draw ring buffer in correct order
if (buf.Data.size() > 0)
{
if (buf.Offset == 0)
{
ImPlot::PlotLine("y(t)", &buf.Data[0].x, &buf.Data[0].y, buf.Data.size(), 0, 0, sizeof(ImVec2));
}
else
{
// two segments due to wrap-around
int size1 = buf.Data.size() - buf.Offset;
int size2 = buf.Offset;
ImPlot::PlotLine("y(t)", &buf.Data[buf.Offset].x, &buf.Data[buf.Offset].y,
size1, 0, 0, sizeof(ImVec2));
ImPlot::PlotLine("y(t)##2", &buf.Data[0].x, &buf.Data[0].y,
size2, 0, 0, sizeof(ImVec2));
}
}
ImPlot::EndPlot();
}
ImGui::End();
// demo windows
if (show_imgui_demo)
ImGui::ShowDemoWindow(&show_imgui_demo);
if (show_implot_demo)
ImPlot::ShowDemoWindow(&show_implot_demo);
rlImGuiEnd();
EndDrawing();
}
// Cleanup
ImPlot::DestroyContext();
rlImGuiShutdown();
CloseWindow();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment