So I’m using the excellent package Javis.jl where I’m making a small simulation to teach my kids about entropy. Basically I want a box with a number of of balls bouncing around in it. I want to be able to detect when the balls collide with each other or with the walls of the box but i cannot see how to do it using the existing translate function. Has anyone tried something similar? The only way that came to me was to precalculate all the trajectories but there must be a better way i believe.
I confess I’ve not used Javis, but I think you’d just update and collision-check the positions of each ball after each frame. I’ve done similar with Luxor:
so it should be easier in Javis.
@Wikunia or @TheCedarPrince would be happy to help, so I’ll mention them here.
Thank you so much for the reply, this sets me on the right path I think. I have a small example for 1 ball here
using Javis
function ground(args...)
background("white")
sethue("black")
end
function object(p=O, color="black")
sethue(color)
circle(p, 25, :fill)
return p
end
function updatedirection!(bpos)
end
myvideo = Video(500, 500)
Background(1:70, ground)
redball = Object(1:70, (args...) -> object(O, "red"), Point(-250, -250))
Object(1:70, (args...) -> updatedirection!(pos(redball)))
act!(redball, Action(anim_translate(300, 300)))
render(myvideo; pathname="entropy.gif")
But the thing here is that I don’t know what to put in updatedirection!
I’m assuming it should update the position of the ball but just accessing the x and y fields in the struct is not allowed so I guess I need a function to mutate these states somehow.
Not sure about Javis per se, but you can change points stored in a mutable struct:
ball.pos = Point(ball.pos.x + 1, ball.pos.y)
Ahh… I was trying to update the x
directly. Thanks!
I was too fast, upon testing this I cannot get it to work.
function updatedirection!(ball)
ball.pos = Point(-ball.pos.x, ball.pos.y)
ball
end
myvideo = Video(500, 500)
Background(1:70, ground)
redball = Object(1:70, (args...) -> object(O, "red"), Point(-250, -250))
Object(1:70, (args...) -> updatedirection!(redball))
act!(redball, Action(anim_translate(500, 500)))
render(myvideo; pathname="entropy.gif")
It seems that redball
does not have a .pos
field so I cannot set it to a new Point
. I must misunderstand completely how the underlying state is handled in Javis.
This is the error:
ERROR: type Object has no field pos
Stacktrace:
[1] getproperty
@ ./Base.jl:38 [inlined]
[2] updatedirection!(ball::Object)
@ Main ./REPL[73]:2
[3] (::var"#29#30")(::Video, ::Vararg{Any})
@ Main ./REPL[77]:1
[4] draw_object(object::Object, video::Video, frame::Int64, origin_matrix::Matrix{Float64}, layer_frames::Nothing)
@ Javis ~/.julia/packages/Javis/UaLm7/src/Javis.jl:666
[5] macro expansion
@ ~/.julia/packages/Javis/UaLm7/src/Javis.jl:416 [inlined]
[6] macro expansion
@ ~/.julia/packages/Luxor/HNu4m/src/basics.jl:576 [inlined]
[7] render_objects(objects::Vector{Javis.AbstractObject}, video::Video, frame::Int64; layer_frames::Nothing)
@ Javis ~/.julia/packages/Javis/UaLm7/src/Javis.jl:415
[8] render_objects
@ ~/.julia/packages/Javis/UaLm7/src/Javis.jl:387 [inlined]
[9] get_javis_frame(video::Video, objects::Vector{Javis.AbstractObject}, frame::Int64; layers::Vector{Javis.AbstractObject})
@ Javis ~/.julia/packages/Javis/UaLm7/src/Javis.jl:609
[10] render(video::Video; framerate::Int64, pathname::String, liveview::Bool, streamconfig::Nothing, tempdirectory::String, ffmpeg_loglevel::String, rescale_factor::Float64, postprocess_frames_flow::typeof(identity), postprocess_frame::typeof(Javis.default_postprocess))
@ Javis ~/.julia/packages/Javis/UaLm7/src/Javis.jl:291
[11] top-level scope
@ REPL[79]:1
cormullion was just giving an example , but Javis objects dont have a pos field.
the position of the object is obtained using the get_position function on the object.
while there is no “set_position” the position is changed through the actions.
I think anim_translate
is probably not the right function since it calculates the starting position and the ending position of the translation before the rendering starts . I dont know if there is a way currently to change the direction of the anim_translate midway through the animation.
What you could try doing is , move every object by a small amount - frame by frame.
every object can have a “velocity” the distance and direction it moves every frame.
updatedirection! can then run through all pairs of objects detect collisions and change the “velocity”
You can hold velocity state in the opts
field which is a dictionary
The following code only checks for collision with the vertical walls .
using Javis
video = Video(500,500)
nframes=250
Background(1:nframes, (v,o,f)-> background("white"))
obj = Object(1:nframes , (v,o,f)-> begin
circle(O,50,:fill)
return O
end)
obj.opts[:velocity] = (10,1)
#not really an obj , as in draws nothing , but runs a function
updaterobj = Object(1:nframes , (v,o,f) -> begin
#hardcoded boundaries of the video
if !(-250+25 < get_position(obj).x < 250-25)
v = obj.opts[:velocity]
obj.opts[:velocity] = (-v[1],v[2])
end
end)
move() = (v,o,a,f)-> begin
if f == 1
#need this if since get_position doesnt work on frame 1
translate(o.start_pos + o.opts[:velocity] )
else
translate(get_position(o) + o.opts[:velocity] )
end
end
act!(obj , Action(1:nframes,move()))
render(video,pathname="vid.mp4")
run(`mpv vid.mp4`)
Wow great, that was really far away from what I was trying. Didn’t know that I could use the opts
for state management. Thank you for this example it would have taken me forever to figure this out.
Discourse was bugged out a bit , couldnt upload the gif earlier ,
Anyway just be careful while using opts , its used internally in Javis for some purposes ,
opts[:result]
is one which i know is used .
So dont use keys with those names. In the future if you want to be safe(er) give a prefix to your keys something like
pfx_keyname
@DoktorMike if you’re willing to , then do submit what you come up with for the example gallery on Javis.jl github repo .