Fast, simple graphics?

I’m running a simulation and would like to watch my agents moving around as the simulation runs. Currently, I’m using Plots, just running plot(...) in a loop. However, this is very slow - plotting each frame takes at least as much time as running the simulation for one frame.

So I’m looking for a faster option. I don’t need to make nice antialiased print-quality plots - I’m just looking to draw a few hundreds or thousands of simple 2D polygons to the screen as quickly as possible. It’s important that I can do this on the screen in a while loop while my simulation runs, rather than first making an animation and then displaying it.

I know that OpenGL is an option, and I’ll use that if I have to, but I’m hoping to avoid going quite that low level. Is there an existing package that would be an easier option?

1 Like

Makie.jl

In particular, take a look at this page of the Makie documentation:

https://makie.juliaplots.org/stable/documentation/nodes/index.html#observables_interaction

How does Makie fare speed-wise? I was aware of it but at first glance it seemed more geared towards publishable quality images than fast animation.

The GR backend is farely fast for such purposes.

I guess that was meant to be GL backend? I’ll give it a try.

1 Like

GLMakie is very fast. One of the reasons why the compile time is high, is due to aggressive use of Observables which allow for fast updates to the scene.

1 Like

Can you make your work compatible with InteractiveDynamics?

I’m going to try GLMakie, but just in case, I’m really hoping for something with more of a graphics/drawing API rather than a plotting API. Ideally, on each frame I’d like to just clear the screen, draw a bunch of triangles and then swap the buffers. My code already keeps track of everything that needs to be drawn, so I’d be happiest with a package that just focuses on drawing and otherwise keeps out of the way. If there’s something that simple that exists, while also being fast, I’d be really happy to know about it.
.

You can try SDL2, there is Julia wrapper GitHub - JuliaMultimedia/SimpleDirectMediaLayer.jl: SDL2. Should be reasonably fast; in Julia I’ve only used it in very simple example which builds and renders one big texture.

2 Likes

I am currently doing this using Luxor + MiniFB.
I played around a bit and made some improvements for Luxor, which is currently a PR waiting to be accepted. Have a look at this example, there is a video if you scroll down. I think the code isn’t working anymore as given. What you see is 60 FPS, live rendering, not just creating the video. I recorded the video from the screen, so that it can be shown without installing my branch at this time:

Here is working code with one window. It’s already in Luxor at Live graphics and snapshots · Luxor if you scroll down a bit below the clock example. To stop the live graphics press “q” and “return”, not ctrl-c, as this can stop the window update @task by chance:

(@v1.7) pkg> add Luxor#master
using Colors
using Luxor
using MiniFB

const WIDTH = 800
const HEIGHT = 600

function window_update_task(window, buffer, showFPS=false)
	sb = buffer[1:105, 1:55]
    state = mfb_update(window, buffer)
	updateCount = 0
	startTime = floor(Int, time())
	fps = "0"
	while state == MiniFB.STATE_OK
		if showFPS
			elapsedTime = floor(Int, time()) - startTime
			if elapsedTime > 1
				fps = string(round(Int, updateCount/elapsedTime))
				startTime = floor(Int, time())
				updateCount = 0
			end
			sb .= buffer[1:105, 1:55]
			@layer begin
				(dx,dy) = Point(0.0, 0.0) - getworldposition(Point(0.0, 0.0); centered=false)
				setcolor((1.0, 0, 0, 0.5))
				fontsize(50)
				text(fps, Point(5+dx, 5+dy), halign=:left, valign=:top)
			end
		end
		state = mfb_update(window, buffer)
		if showFPS
			buffer[1:105, 1:55] .= sb
		end
        sleep(1.0/120.0)
		updateCount += 1
    end
	println("\nWindow closed\n")
end

buffer = zeros(ARGB32, WIDTH, HEIGHT)
d = Drawing(buffer)

window = mfb_open_ex("MiniFB", WIDTH, HEIGHT, MiniFB.WF_RESIZABLE)
@async window_update_task(window, buffer, true)

mutable struct Ball
    position::Point
    velocity::Point
end
function sticks(w, h)
    channel = Channel(10)
    #enter "q" and "return" to stop the while loop
    @async while true
        kb = readline(stdin)
        if contains(kb, "q")
            put!(channel, 1)
            break
        end
    end
	colors = [ rand(1:255), rand(1:255), rand(1:255) ]
	newcolors  = [ rand(1:255), rand(1:255), rand(1:255) ]
	c = ARGB(colors[1]/255, colors[2]/255, colors[3]/255, 1.0)
	balls = [ Ball( rand(BoundingBox(Point(-w/2, -h/2), Point(w/2, h/2))), rand(BoundingBox(Point(-10, -10), Point(10, 10))) ) for _ in 1:2 ] 
	while true
		background(0, 0, 0, 0.05)
		if colors == newcolors
			newcolors = [ rand(1:255), rand(1:255), rand(1:255) ]
		end
		for (index, (col, newcol)) in enumerate(zip(colors, newcolors))
			if col != newcol
				col > newcol ? col -= 1 : col += 1
				colors[index] = col
			end
		end
		c = ARGB(colors[1]/255, colors[2]/255, colors[3]/255, 1.0)
		for ball in balls
			if !(-w/2 < ball.position.x < w/2)
				ball.velocity = Point(-ball.velocity.x, ball.velocity.y)
			end
			if !(-h/2 < ball.position.y < h/2)
				ball.velocity = Point(ball.velocity.x, -ball.velocity.y)
			end
			ball.position = ball.position + ball.velocity
		end
		setcolor(c)
		line(balls[1].position, balls[2].position, :stroke)
		sleep(1.0/120.0)
        if isready(channel)
            break
        end
	end
end

origin()
sticks(WIDTH, HEIGHT)

So, what you want to do is currently work in progress on Luxor.

1 Like

Sorry, you need Luxor#master for my code example!

(@v1.7) pkg> add Luxor#master

Could UnicodePlots.jl be sufficient for your usecase?