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?
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.
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.
.
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 Interactive graphics and Threads · 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.