Making the frames
My interpretation of your original post is that you wanted more control of when, and how many, frames were output from some kind of ongoing simulation. I thought the ‘snapshot’ feature was probably the way to go. Here’s a more complete version of my first example:
using Luxor
function run_it()
Drawing(300, 300, :rec)
origin()
background("black")
redcircle, greencircle, purplecircle =
ngon(O + (0, 10), 70, 3, π / 6, vertices=true)
radius = 40
drawing_number = 1
for i in 1:1000
frame_worthy = false
pt2 = rand(BoundingBox())
if distance(pt2, redcircle) < radius
sethue("red")
frame_worthy = true
elseif distance(pt2, greencircle) < radius
sethue("green")
frame_worthy = true
elseif distance(pt2, purplecircle) < radius
sethue("purple")
frame_worthy = true
else
frame_worthy = false
end
if frame_worthy
circle(pt2, 4, :fill)
framename = string("/tmp/",
lpad(drawing_number, 10, "0"), ".png")
snapshot(fname=framename)
drawing_number += 1
end
end
finish()
Luxor.FFMPEG.ffmpeg_exe(`
-v 0
-r 24
-f image2
-i /tmp/%10d.png -c:v libx264 -pix_fmt yuv420p
-y /tmp/dotty.mp4
`);
end
run_it()
which makes this:
(GIF version if Discourse doesn’t like the video format)
and this approach lets you handle the “what if I don’t know the number of frames beforehand”, the “unknown outcome”, and “some stop condition” requirements you mentioned. Replace the for
with a while
- you could run it for weeks until some condition is met…
However, your example code initializes a new empty drawing on each iteration, so it’s not using this incremental snapshot feature.
And I’m not sure what finish() and preview() are doing for me. Moving them into the for loop, what I somehow would expect to be necessary, gives a compilation error.
With only a single drawing, you need just one finish()
(to tidy up) and preview()
(to see the finished drawing). I couldn’t see any compilation errors if you made a new drawing in every loop (preview()
would obviously not be useful). But perhaps if you’re leaving a few thousand drawings open and unattended you might eventually see some other errors…
Making the video
I don’t know anything about VideoIO.jl, since Luxor predates it by a few years, and I’ve stuck with FFMPEG. You should probably open a new issue/post to get help with that package from someone who knows more.
You don’t show the errors you see, but perhaps they’re the same as mine:
ERROR: MethodError: no method matching VideoIO.VideoWriter(::String, ::Vector{…}; encoder_options::@NamedTuple{…}, framerate::Int64)
Closest candidates are:
VideoIO.VideoWriter(::AbstractString, ::Type{T}, ::Tuple{Integer, Integer}; codec_name, framerate, scanline_major, container_options, container_private_options, encoder_options, encoder_private_options, swscale_options, target_pix_fmt, pix_fmt_loss_flags, input_colorspace_details, allow_vio_gray_transform, sws_color_options, thread_count) where T
@ VideoIO ~/.julia/packages/VideoIO/ZM7RD/src/encoding.jl:234
VideoIO.VideoWriter(::Any, ::AbstractMatrix{T}; kwargs...) where T
@ VideoIO ~/.julia/packages/VideoIO/ZM7RD/src/encoding.jl:379
The second argument should apparently be a Type or a Matrix:
julia-1.10> firstimg
1-element Vector{PermutedDimsArray{ColorTypes.RGB{FixedPointNumbers.N0f8}, 2, (2, 1), (2, 1), Matrix{ColorTypes.RGB{FixedPointNumbers.N0f8}}}}:
[RGB{N0f8}(0.0,0.0,0.0) RGB{N0f8}(0.0,0.0,0.0) … RGB{N0f8}(0.0,0.0,0.0) RGB{N0f8}(0.0,0.0,0.0); RGB{N0f8}(0.0,0.0,0.0) RGB{N0f8}(0.0,0.0,0.0) … RGB{N0f8}(0.0,0.0,0.0) RGB{N0f8}(0.0,0.0,0.0); … ; RGB{N0f8}(0.0,0.0,0.0) RGB{N0f8}(0.0,0.0,0.0) … RGB{N0f8}(0.0,0.0,0.0) RGB{N0f8}(0.0,0.0,0.0); RGB{N0f8}(0.0,0.0,0.0) RGB{N0f8}(0.0,0.0,0.0) … RGB{N0f8}(0.0,0.0,0.0) RGB{N0f8}(0.0,0.0,0.0)]
VideoIO.load
loads a 'vector of image arrays`, so perhaps you need to pass just the first element. I’m just guessing now, and video stuff is complicated…