Creation date: 2021-08-09

Yesterday I heard the third episode of the 3b1b podcast which is an interview of the famous Steven Strogatz. There he mentions two interesting puzzles at the beginning of the conversation. The first is about a geometry problem and the second one is what I want to "tackle" (visualize) in this blog post today.

Before I explain the problem I would like to link you to the JuliaCon video of the presentation that Jacob Zelko and I made to present the latest state of Javis .

Mostly due to the excellent work of Arsh Sharma we now have quite some more functionality in Javis than the last time I've wrote about it back in June.

We have layers now as well as easy functions and macros to define objects without using the weird anonymous syntax functionality. I would like to visualize interesting problems or concepts on this blog and talk about the code to create those in the next couple of weeks.

If you want to keep getting updated please join the newsletter such that you don't miss one of those 😉

Okay let's start now with the problem formulation:

Let's assume we have a square and in each of the corners there is a dog. Each dog is trying to chase a dog which is their clockwise neighbor. How long does it take each dog to catch its neighbor?

Now they don't just run along the side of the edge as that would be quite boring 😄 They try to use the fastest way possible and continuously update the direction they are running in. Furthermore the speed they are running is a constant value.

Now I want to visualize the dogs and the path they are taking. Of course as I'm one of the creators of Javis I want to use that tool 😄

Let's start with drawing the square.

```
using Javis
function ground(args...)
background("black")
sethue("white")
end
function main()
video = Video(1000, 1000)
nframes = 300
square_len = 800
Background(1:nframes, ground)
Object(1:nframes, JRect(Point(-square_len/2, -square_len/2), square_len, square_len; color="white"))
render(video; pathname = "chase.gif")
end
```

This might partly look familiar if you have seen Javis code before but as I intend this series for newcomers I want to go over everything in as much detail as needed. For this I keep the animations short in general. Nevertheless I use quite some advanced and sometimes undocumented features of Javis so I'm sure there is something to learn for everyone.

Now I first of all need to have `using Javis`

at some point and I like to have a `main`

function to avoid having everything in the global scope.

I define the `video`

with 1000x1000 pixels and then the number of frames and the size of the square.

For defining the background of the animation I have the `ground`

function which takes in some arguments which aren't relevant which is the reason why I choose `args...`

here as the parameters. I define a black background and the default color to being white (even though I never use it 😄).

Next up I define my object which should draw the square. In v0.6.1 of Javis some new convenience functions were introduced including JRect which draws a rectangle. We define the frames of the object which is the full number of frames here and then the upper left corner position of our square as well as the width and height. Additionally we define the color of the square with `color`

as we want to have only the square outline we don't need to add an `action`

keyword. If we would like to fill it we could have used `action = :fill`

after defining the color.

Okay let's place the dogs in each corner.

For this we add the following lines before the `render`

function:

```
dog_colors = Colors.JULIA_LOGO_COLORS
dog_positions = [
Point(-square_len / 2, -square_len / 2),
Point(square_len / 2, -square_len / 2),
Point(square_len / 2, square_len / 2),
Point(-square_len / 2, square_len / 2),
]
dogs = [
Object(
1:nframes,
JCircle(dog_positions[i], 15; action = :fill, color = dog_colors[i]),
) for i in 1:4
]
```

We start with defining the colors of the circles that we'll use to represent the dogs. I used the four colors of the Julialang logo for this. You need to also add `using Colors`

at the top of the file.

Then we define the corners first and then we create four objects. Each of them is defined using the function JCircle which takes in the center of the circle and the radius plus some keyword arguments like before. We do the creation of the objects inside a list comprehension. In that we iterate `i`

between 1 and 4 and can use that to access both the position of the dog as well as the color.

Now we're going into the complicated part of this. How do we move the dogs?

Well each dog wants to move into the direction of the neighboring dog and that with a certain constant speed. In various tutorials of Javis we only tackle simple movements like translate by a fixed value or rotate. We showed how to rotate around another object in the first tutorial which at least uses the `pos`

function that I'll also use in a moment. However calculating the vector and then moving along that changing direction isn't that simple with the basic functionality provided by Javis. That said there is a way which one can use when one understands how Javis works from the ground up.

That is something I want to show you here. Once you understand this simple example you'll be able to create much more powerful animations yourself. Why isn't that documented then? Well... I would like to create an easier process for the user but at least will link to this post in the docs 😉

Alright let's check out a simple action first maybe.

```
for i in 1:4
act!(dogs[i], Action(1:nframes, anim_translate(100, 0)))
end
```

Now the dogs are moving very slowly to the right. We do this by applying an `Action`

to each of the dogs which is here defined for all frames as well and
` anim_translate `

just tells in which direction the dogs should move. Each dog will end up at `dogs_positions .+ Point(100, 0)`

.

Now `anim_translate(100, 0)`

is in the backend just a function which calls an anonymous function. This means we can define our own action as well as long as it has the anonymous function style.

Let me show you what I mean. We add the following two functions:

```
function chase(dogs, from, to)
(args...) -> _chase(dogs, from, to)
end
function _chase(dogs, from, to)
println("$from chases $to")
end
```

and change our act! from before to:

`act!(dogs[i], Action(1:nframes, chase(dogs, i, mod1(i+1, 4))))`

When we call `main()`

we will get an output of repeated:

```
1 chases 2
2 chases 3
3 chases 4
4 chases 1
```

⚠ Note

The

` mod1 `

function is very useful when working with modulo in the 1 index based language Julia. It basically also just wraps around but instead of going from 0 to 3 it goes from 1 to 4 which then can be used to index our array at a later stage.The functionality that we need now to actually visualize the chasing positions is what is provided by the
` change `

method under the hood. We want to change the center of the dogs objects.

Therefore we need to call `dogs[from].change_keywords[:center] = new_pos`

and need to compute the new position of the dog. And yes I hear you all: This should be a documented in the official documentation.

Okay now let's set the `new_pos`

to the origin just to see that it works:

```
function _chase(dogs, from, to)
dogs[from].change_keywords[:center] = O
end
```

⚠ Note

The

`O`

is the letter `O`

which stands for the origin and is the same as `Point(0,0)`

.Well that isn't really an animation is it? Let's compute the actual value of the dogs position. For this we also introduce the variable `speed`

which I set to `speed = 3`

right after defining the `square_len`

.

Then we replace the `chase`

function again:

```
function _chase(dogs, from, to, speed)
animal = dogs[from]
chases = dogs[to]
diff = pos(chases) - pos(animal)
diff /= sqrt(diff.x^2 + diff.y^2)
new_pos = pos(animal) + speed * diff
animal.change_keywords[:center] = new_pos
end
```

So we take the current position of the dog we want to chase as well as the position of the current dog. Then compute the difference and normalize that and calculate the new position by adding it to the current position while also combining the "vector" `diff`

with the speed.

The `chase`

function for the extra parameter `speed`

:

```
function chase(dogs, from, to, speed)
(args...) -> _chase(dogs, from, to, speed)
end
```

as well as passing `speed`

into the `chase`

function:

`act!(dogs[i], Action(1:nframes, chase(dogs, i, mod1(i+1, 4), speed)))`

Unfortunately we get:

```
ERROR: MethodError: no method matching get_position(::Nothing)
Closest candidates are:
get_position(::Javis.Layer) at /home/ole/Julia/Javis/src/layers.jl:183
get_position(::Point) at /home/ole/Julia/Javis/src/object_values.jl:17
```

The problem here is that the dogs don't have their initial position calculated as this would draw the dogs directly. We also compute the action before we draw the dog so before we call the object itself. This way the `pos`

calls in our `chase`

function return nothing.

We could either check if `pos`

returns something and if it does we do our calculation or as I decided: We simply set them to the corner in frame one and let them chase starting at frame 2.

Therefore we use:

`act!(dogs[i], Action(2:nframes, chase(dogs, i, mod1(i+1, 4), speed)))`

This creates the hardest part of a lovely animation:

For this animation:

Had to visualize the problem chase problem mentioned by @stevenstrogatz in the @3blue1brown podcast.

— OpenSourcES (@opensourcesblog) August 8, 2021

As a true #julialang fan with the @JuliaLanguage colors ;) pic.twitter.com/Vfb3anUbBB

we want to do a bit more.

We first of all remove the square by commenting out the Object JRect line.

Then we create the path which is also done in our first tutorial.

Inside our `act!`

for loop we'll add:

`Object(1:nframes, (args...) -> path!(dogs_paths[i], pos(dogs[i]), dog_colors[i]))`

and then we need to define the `path!`

function as well as the `dogs_paths`

vector.

⚠ Note

Here we can use the frames starting from 1 as the dogs are already evaluated. This is the case as we define the object after the

`dogs`

objects and it's an object itself and not an action acting on the dogs.The `dogs_paths`

vector will hold the path each of the dogs took and is initialized with:

`dogs_paths = [Point[] for _ in 1:4]`

Our path function is simply copied from the tutorial:

```
function path!(points, cpos, color)
sethue(color)
push!(points, cpos) # add pos to points
circle.(points, 2, :fill) # draws a circle for each point using broadcasting
end
```

It adds the position to the vector of points and used the broadcast function to draw a small circle at each position.

Well you can still work out the solution to the original problem on your own 😉 I don't want to spoil anything and was much more interested in animating the problem at this stage.

Thanks for reading and share it with your friends if you like and think about subscribing to the newsletter or directly to Patreon to get everthing 2 days earlier 😉

```
using Colors
using Javis
function ground(args...)
background("black")
sethue("white")
end
function chase(dogs, from, to, speed)
(args...) -> _chase(dogs, from, to, speed)
end
function _chase(dogs, from, to, speed)
animal = dogs[from]
chases = dogs[to]
diff = pos(chases) - pos(animal)
diff /= sqrt(diff.x^2 + diff.y^2)
new_pos = pos(animal) + speed * diff
animal.change_keywords[:center] = new_pos
end
function path!(points, cpos, color)
sethue(color)
push!(points, cpos) # add pos to points
circle.(points, 2, :fill) # draws a circle for each point using broadcasting
end
function main()
video = Video(1000, 1000)
nframes = 300
speed = 3
square_len = 800
Background(1:nframes, ground)
# Object(1:nframes, JRect(Point(-square_len/2, -square_len/2), square_len, square_len; color="white"))
dog_colors = Colors.JULIA_LOGO_COLORS
dogs_paths = [Point[] for _ in 1:4]
dog_positions = [
Point(-square_len / 2, -square_len / 2),
Point(square_len / 2, -square_len / 2),
Point(square_len / 2, square_len / 2),
Point(-square_len / 2, square_len / 2),
]
dogs = [
Object(
1:nframes,
JCircle(dog_positions[i], 15; action = :fill, color = dog_colors[i]),
) for i in 1:4
]
for i in 1:4
act!(dogs[i], Action(2:nframes, chase(dogs, i, mod1(i+1, 4), speed)))
Object(1:nframes, (args...) -> path!(dogs_paths[i], pos(dogs[i]), dog_colors[i]))
end
render(video; pathname = "chase.gif")
end
```

**Thanks to my 12 patrons!**

Special special thanks to my >4$ patrons. The ones I thought couldn't be found 😄

Anonymous

Kangpyo

Gurvesh Sanghera

Szymon Bęczkowski

Colin Phillips

Jérémie Knuesel

For a donation of a **single** dollar per month you get early access to these posts. Your support will increase the time I can spend on working on this blog.

There is also a special tier if you want to get some help for your own project. You can checkout my mentoring post if you're interested in that and feel free to write me an E-mail if you have questions: o.kroeger <at> opensourc.es

I'll keep you updated on Twitter OpenSourcES.

Want to be updated? Consider subscribing and receiving a mail whenever a new post comes out.

© Ole Kröger. Last modified: September 03, 2021. Website built with Franklin.jl.