[ANN] ImPlot.jl -- FAST real-time plotting for CImGui.jl

I wrapped a really nice plotting extension called implot, a C++ plugin for Dear ImGui. With some clutch help from @Gnimuc and the magic of BinaryBuilder.jl this package can be imported into a Julia session that’s using CImGui.jl and everything should “JustWork”.

The plotting is straight forward and, notably, quite fast even with 100s of thousands of points. There’s also optional arguments to do strided/offset array access for interleaved data. Generally a nice solution for anyone that wants to do real-time visualization of analog or digital sensor data (in my case, electrophysiology signals).

If there’s interest from the community, I might go to the effort of documenting it a little more thoroughly and throwing it up on the registry. Update: ImPlot.jl is now on the registry–you can just ]add ImPlot now. The underlying C++ lib has also been updated to include a couple extra features (namely shaded line plots).

55 Likes

This is great. Also, not so great as I am now thoroughly confused about what plotting library to switch over to from ggplot. There is Plots.jl which is the most native and FTOP is reduced significantly in 1.5, there is VegaLite.jl which somewhat follows the Grammar of Graphics of ggplot, there is gnuplot.jl which is my top contender to switch to, Makie.jl which seems more tedious than necessary, but packs some serious power, and a few others I can’t recall.

But this is great. Will definitely try it out in the next few days :grinning:

1 Like

Implot, along with ImGui, is mainly for writing GUI tools. You could play with it online. :wink:

https://traineq.org/imgui_manual/src/imgui_manual.html

https://traineq.org/implot_demo/src/implot_demo.html

5 Likes

Would it make sense to integrate ImPlot.jl into CImGui.jl ?

This is great. I needed a real-time vizualisation lib, so today I decided “Let me ask on the Julia discourse about the situation of real-time vizualisation libs. No, first let me search to see what has currently been announced.”

So I found this thread. Started 11 hours ago! How lucky :slight_smile:

Now excuse my breathtaking ignorance, but… From what I understand this is a wrapper to some C or C++ libs (great!). But the demo (which @gnimuc posted) is on the browser. How does that work?

1 Like

Those demos are done independently by some other people, who got it running in the browser with emscripten + WebGL afaik. The Julia libraries here are calling natively compiled binaries and use an OpenGL backend. The performance is slower in the browser, but it’s also easy to give someone a link to see what’s going on.

1 Like

That’s exactly what I wanted to hear, thank you :smile:

Thankyou. I bought a pulse oximeter sensor and a three lead heart sensor for my Pi or Jetson Nano. This will be ideal to display traces from these.

Di dI say traces? Yes indeed - I am old enough to have used pen plotters.

7 Likes

My lab has rolls of old chart paper in the back of a cabinet somewhere. Still call them traces from time to time :slight_smile:

2 Likes

I find this package really useful combined with CImGui. Any chance of registering it? Thank you for the great work!

1 Like

Sure. I was waiting for another tag from implot since there’s some added plot styles and options. That has now been done and the maintainer of cimplot has regenerated bindings to match. The implot C++ API was changing a lot, so that’s why I hesitated registering it–there were breaking changes happening. However, it looks like the dust has settled now. I have to rebuild the JLL, add in the extra API functions and it should be more or less ready for registration. I’ll post an update when I get around to it.

2 Likes

ImPlot.jl is now on the public registry :slight_smile:

9 Likes

Awesome! Thank you so much! I’m using it to develop an oscilloscope front panel at the moment:)

It would be nice to have the same demo with ImPlot.jl!

3 Likes

What exactly is the difference between ImPlot.jl and CImGui.jl? Looking at the demo.jl files of both, it seems they both CImGui’s interface.

I have an application that produces real-time metrics and I would like to use ImPlot.jl/CImGui.jl to plot them. Question: what is the lowest effort way of putting my real-time metrics into a real-time plot? I was looking at ImPlot’s demo.jl trying to figure out how the interface works, but it looks like all the interesting plots in that example are actually implemented in some C++ code. How do I plot real-time from Julia?

Copy pasting from a sample somewhere I could get a basic real-time plot working in Julia:

rt_buffer = CircularBuffer{Float64}(1000)

( ... )

if show_main_window
    @c CImGui.Begin("Plot Window", &show_main_window)
    push!(rt_buffer, rand())
    y = collect(rt_buffer)
    ImPlot.SetNextPlotLimits(0.0,1000,0.0,1.0, ImGuiCond_Once)
    if (ImPlot.BeginPlot("Foo", "x1", "y1", CImGui.ImVec2(-1,300)))
        ImPlot.PlotLine(y)
        ImPlot.EndPlot()
    end
    CImGui.End()
end

However, I’d rather not have to copy my CircularBuffer to a Vector every time I want to plot it. Is it possible to avoid copying? Doing ImPlot.PlotLine(rt_buffer) throws:

┌ Error: Error in renderloop!
│   exception = conversion to pointer not defined for CircularBuffer{Float64}
└ @ Main ~/demo.jl:147

Alright, I just discovered that all the functions that demo.jl uses are implemented in files in examples/.

Excuse the drivel.

Ok, another question. What is the recommended way of integrating an app with the display?

My app has a loop, and so does the display (e.g. this).

So should I put the display update inside the app’s loop and implicitly let the app’s loop control the display rate? Or should I have the app running in one thread, in display on another thread, and pass data from the former to the latter by using some shared data structure? Or how?

The simplest way is to use coroutine(yield() in your app loop like this)

If you’d like to support macOS, please make sure GLFW runs on the main thread, that’s a limitation of macOS.

1 Like

Thanks for the response. I still haven’t figured it out though, I guess because I don’t understand yield().

My application loop is schematically:

initialize!(state)
while true
    update!(state)
end

Now, by copy pasting code around I’ve made it work by not using my app loop and using your renderer loop instead. I.e. this works:

initialize!(state)

try
    while !GLFW.WindowShouldClose(window)
        GLFW.PollEvents()
        ImGui_ImplOpenGL3_NewFrame()
        ImGui_ImplGlfw_NewFrame()
        CImGui.NewFrame()

        # below is my code (schematically)
        # inspired by copy pasting from your examples
        update!(state)
        y = collect(state)
        ImPlot.SetNextPlotLimits(0.0,1000,0.0,1.0, ImGuiCond_Once)
        if (ImPlot.BeginPlot("Foo", "x1", "y1", CImGui.ImVec2(-1,300)))
            ImPlot.PlotLine(y)
            ImPlot.EndPlot()
        end
        CImGui.End()
        # end of my code

        CImGui.Render()
        GLFW.MakeContextCurrent(window)
        display_w, display_h = GLFW.GetFramebufferSize(window)
        glViewport(0, 0, display_w, display_h)
        glClearColor(0.2, 0.2, 0.2, 1)
        glClear(GL_COLOR_BUFFER_BIT)
        ImGui_ImplOpenGL3_RenderDrawData(CImGui.GetDrawData())

        GLFW.MakeContextCurrent(window)
        GLFW.SwapBuffers(window)
    end
catch e
    @error "Error in renderloop!" exception=e
    Base.show_backtrace(stderr, catch_backtrace())
finally
    ImGui_ImplOpenGL3_Shutdown()
    ImGui_ImplGlfw_Shutdown()
    CImGui.DestroyContext(ctx)
    GLFW.DestroyWindow(window)
end

Obviously I would like to decouple my update! for your renderer. Exactly how should this be re-organized? From the example that you linked to (the one with the renderloop with a yield()) I’m guessing that somehow I’m supposed to wrap my code into a ui() function and pass it to the renderloop, but I can’t see exactly how. Additionally, where would I put the initialize!(state) line? If I just put it inside ui() then it would be run on every loop.

@freeman I just wrote a short step-by-step post to demonstrate how to do state-management with CImGui.jl. Hopefully this will answer your questions. Feel free to ask questions at Github Discussions.

7 Likes