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

This is beautiful. So simple, so clean. Thank you! <3

How should I change marker style, like in scatter plot from this c++ demo:

        if (ImPlot::BeginPlot("Scatter Plot", NULL, NULL)) {
            ImPlot::PlotScatter("Data 1", xs1, ys1, 100);
            ImPlot::PushStyleVar(ImPlotStyleVar_FillAlpha, 0.25f);
            ImPlot::SetNextMarkerStyle(ImPlotMarker_Square, 6, ImVec4(0,1,0,0.5f), IMPLOT_AUTO, ImVec4(0,1,0,1));
            ImPlot::PlotScatter("Data 2", xs2, ys2, 50);
            ImPlot::PopStyleVar();
            ImPlot::EndPlot();
        }

Looks like some ImPlotStyleVar_... and SetNext... methods are missing?

Hi all,

First of all thanks for the amazing work on ImPlot.

I’m trying to use use CImGui/ImPlot to display/process data I receive from a UDP socket. Baslically, my infinite_loop function (which I renamed listen!) looks like this :

function listen!(cb::Listener)
    cb.stopped = false
    @async while true
        cb.stopped && break
        data    = process(copy(recv(cb.socket)))
        push!(cb, data)
        yield()
    end
end

I’ve setup everything following this example with @async / yield() in the renderloop and everything goes smoothly with low UDP framerate (about 5 UDP frames per seconds).

However I’m loosing UDP frames when the rate at which data is sent to the UDP socket is increased. In the real life application I need to receive and display 3200 bytes every 64 µs. I guess that the Julia scheduler does not yield to the listen! function often enough for the data to be recv and processed in time.

Things get better when I yield() after every instruction in the rendering loop but I’m still loosing a lot of data.

I also tried to get things working with Threads.@spawn as in this example but without any sucess.

Any hints ?
Thanks !

What if you had the render thread always plot the latest data from an array, while another thread updates that array whenever data is available?

I use that pattern to show updates at 60fps while the data itself updates at 200hz.

1 Like

What’s the definition of Listener? Have you tried using Channel with spawn = true?

Are you sure the data need to be displayed at this high framerate? That’s about 15000fps. Common monitors have a refresh rate of up to 240Hz.

If what you meant is displaying at a common fps but processing the data every 64us, then I believe the Channel is enough for this use case.

Data is coming in at about 15 kHz but indeed processing and display is performed at common fps rates (every 20 to 100 ms).

It basically consists in :

  • a UDP socket,
  • a 2D circular buffer for data, and a 1D circular buffer for storing UDP frames indices (updated every 64 µs)
  • 2 arrays which store the current data to be displayed (updated every ~20 ms)
mutable struct Listener <: AbstractArray{Cfloat, 2}

    # UDP data
    IP_adress   :: IPv4
    port        :: Int 
    socket      :: UDPSocket

    # Buffer 
    capacity    :: Int
    nb_channels :: Int
    first       :: Int
    length      :: Int
    buffer      :: Array{Cfloat, 2}
    indices     :: Vector{Int64}

    # Current data
    current_data    :: Array{Cfloat, 2}
    current_indices :: Vector{Int64}
    counter         :: Int64

    # State
    stopped     :: Bool
end

Indeed, that’s what I’m trying to do with @async tasks and yield(), but I guess Channel or Threads@spawn better suit my needs. Thank you to both of you for the tips; I’ll give it a try and keep you posted.

Got it working with Threads.@spawn and lock()/unlock() in both rendering/listening loops.
Thanks @sefffal and @Gnimuc !

2 Likes

Would you mind publishing a simplified example in a gist? I believe this will benefit others in the future.

4 Likes