Create Unit Circle with its Sin Cos Animation

I want to create animation that a circle will rotate around a unit circle, and the second plot the circle move along the sine function, and the third plot the circle move along the cosine function. Which package for animation can make this?
The main point is at time 0 the circle will start at (0,0), sine 0 and cosine 0

Here is an example that I have found:
https://upload.wikimedia.org/wikipedia/commons/3/3b/Circle_cos_sin.gif

No need to be exactly the same copy.

2 Likes

I suggest you check out Luxor.jl

4 Likes

I’m getting a lot of deja vu today… :slight_smile:

Javis is probably suitable, but I think you’re familiar with that… :rofl:?

6 Likes

@20akshay00 made something very similar you could Probably use as a starting point: ⚡ Pluto.jl ⚡ with Javis.jl. also, I think I am rememberring but @arsh or @Ved_Mahajan, did you make some thing like this with Javis?

I know Javis, but I want to know other package that is capable of doing circle with trigonometry simulation. The circle that can follow the path of a function not only rotating / circling.

I know that animation. :joy:

@cormullion is this using Luxor only?

@TheCedarPrince thanks for the code.

Why people are using Pluto.jl instead of Jupyter Notebook?

@arsh and @Ved_Mahajan feel free to share your thoughts. I am into Full bullets of Mathematics and Physics simulation with Julia now.

1 Like

I played around with this a bit earlier to see how simple it would be. It didn’t take long to put together the gif below with the @play macro from Luxor.jl (which I’m quite fond of, personally). The starter code for it is below and should get you most of the way towards recreating the original if that’s your goal.

Code
using Luxor, MiniFB
if !(@isdefined var"@play")
    include(dirname(pathof(Luxor)) * "/play.jl")
end


let
    w, h = 600, 400

    ntrig = 360
    sinarray = sind.(1:ntrig)
    cosarray = cosd.(1:ntrig)
    i = 1

    @play w h begin
        background("black"); sethue("white")

        # translate near bottom right corner
        origin(0.8w, 0.8h)
        setline(1); setopacity(1)
        cr = 0.15h
        circle(0, 0, cr, :stroke)
        circle_marker_pos = getworldposition(Point(cr*cosarray[i], cr*sinarray[i]))

        # translate to lower 20%, on the left
        origin(0, 0.8h)
        setline(1); setopacity(1)
        scale(1.0, cr)
        # draw a sin wave
        for j in 1:ntrig-1
            p1 = Point(j, sinarray[mod(j+i, ntrig)+1])
            p2 = Point((j+1), sinarray[mod(j+i+1, ntrig)+1])
            line(p1, p2, :stroke)
        end
        # record the last point of the sin wave
        sin_marker_pos = getworldposition(Point(ntrig,sinarray[mod(ntrig+i, ntrig)+1]))

        # translate to top left corner, then down 20%
        origin(0, 0.2h)
        scale(1.0, cr)
        setline(1); setopacity(1)
        # draw a sin wave
        for j in 1:ntrig-1
            p1 = Point(j, cosarray[mod(j+i, ntrig)+1])
            p2 = Point((j+1), cosarray[mod(j+i+1, ntrig)+1])
            line(p1, p2, :stroke)
        end
        # record the last point of the sin wave
        cos_marker_pos = getworldposition(Point(ntrig, cosarray[mod(ntrig+i, ntrig)+1]))

        # mark the last point of the sin and cos waves
        origin()
        setline(2); setopacity(1);
        circle(sin_marker_pos, 2, :stroke)
        circle(cos_marker_pos, 2, :stroke)
        circle(circle_marker_pos, 2, :stroke)
        # draw connecting lines between the wave and the circle
        setline(1); setopacity(0.5)
        line(sin_marker_pos, circle_marker_pos, :stroke)
        # line(cos_marker_pos, circle_marker_pos, :stroke)

        i %= ntrig
        i += 1
    end
end

trigcircle

3 Likes

Nice work!

@tomerarnon wow thanks a lot.

I am in a middle of modifying this to make like that one

I will share if I am finished. More people using it, I think that Luxor.jl could provide more than Javis.jl

1 Like

I don’t think this is true - Javis provides much more powerful tools for organizing animations with objects, and it requires less code as well. For a discussion about these topics, I’d recommend the discussion area on Zulip…

Why I get this error:
Could not create correct visual window

What is the code to save the gif file of the code? Can I use something like this:

gif(anim, "sincos.gif", fps=30)

To make it into a gif, put all of the code in the begin-end block into a function with signature frame(scene, i). The

movie = Movie(600, 400, "myname")
animate(movie, [Scene(demo, frame, 0:359)], creategif=true, pathname = "path_to_file.gif")

No idea…

I know why:

Is there any replacement for MiniFB? Can I use Plots and plotly() instead?

I don’t think so; I think @play works specifically with MiniFB. The gif creation probably doesn’t depend on that though, although I’m not sure either way.

I read this:

I know the MiniFB needs a browser, I use dual boot OS, one(LFS OS) has no browser thus has a problem with MiniFB, one (CAELinux) has standard browser firefox thus can use Pluto.jl and can show the visual window like yours for the simulation.

I just want to show the result without browser, not depending much on browser. Julia REPL and Plots shall be enough. I will try to convert your code from @play

Btw, I try this code and the gif is not showing up:

using Luxor, MiniFB
if !(@isdefined var"@play")
    include(dirname(pathof(Luxor)) * "/play.jl")
end

let
    w, h = 600, 400

    ntrig = 360
    sinarray = sind.(1:ntrig)
    cosarray = cosd.(1:ntrig)
    i = 1

    @play w h begin
        background("gray"); sethue("white")

        # translate near bottom right corner
        origin(0.8w, 0.8h)
        setline(1); setopacity(1)
        cr = 0.15h
        circle(0, 0, cr, :stroke)
        circle_marker_pos = getworldposition(Point(cr*cosarray[i], cr*sinarray[i]))

        # translate to lower 20%, on the left
        origin(0, 0.8h)
        setline(1); setopacity(1)
        scale(1.0, cr)
        # draw a sin wave
        for j in 1:ntrig-1
            p1 = Point(j, sinarray[mod(j+i, ntrig)+1])
            p2 = Point((j+1), sinarray[mod(j+i+1, ntrig)+1])
            line(p1, p2, :stroke)
        end
        # record the last point of the sin wave
        sin_marker_pos = getworldposition(Point(ntrig,sinarray[mod(ntrig+i, ntrig)+1]))

        # translate to top left corner, then down 20%
        origin(0, 0.2h)
        scale(1.0, cr)
        setline(1); setopacity(1)
        # draw a sin wave
        for j in 1:ntrig-1
            p1 = Point(j, cosarray[mod(j+i, ntrig)+1])
            p2 = Point((j+1), cosarray[mod(j+i+1, ntrig)+1])
            line(p1, p2, :stroke)
        end
        # record the last point of the sin wave
        cos_marker_pos = getworldposition(Point(ntrig, cosarray[mod(ntrig+i, ntrig)+1]))

        # mark the last point of the sin and cos waves
        origin()
        setline(2); setopacity(1);
        circle(sin_marker_pos, 2, :stroke)
        circle(cos_marker_pos, 2, :stroke)
        circle(circle_marker_pos, 2, :stroke)
        # draw connecting lines between the wave and the circle
        setline(1); setopacity(0.5)
        line(sin_marker_pos, circle_marker_pos, :stroke)
        # line(cos_marker_pos, circle_marker_pos, :stroke)

        i %= ntrig
        i += 1
    end
end

begin
function frame(scene, i)
movie = Movie(600, 400, "Lasthrim")
animate(movie, [Scene(demo, frame, 0:359)], creategif=true, pathname = "sincoscircleluxor.gif")
end

Hi again!

Is there any replacement for MiniFB?

Well probably, but I don’t know and nothing else has been tested.

Can I use Plots and plotly() instead?

Yes. I’m sure both Plots.jl and plotly and Makie.jl could easily do this kind of animation. I’m only commenting on this one though. I’m fairly sure you can’t - although I hate saying it - get Plots,jl and Luxor to work together.

I read this: …

er … thread is too long for me really and I’m not that interested :slight_smile:

I know the MiniFB needs a browser, I use dual boot OS, one(LFS OS) has no browser thus has a problem with MiniFB, one (CAELinux) has standard browser firefox thus can use Pluto.jl and can show the visual window like yours for the simulation.

MiniFB doesn’t use a browser, if you’re talking about a web browser.The B in MiniFB means “Buffer”. It’s a way to display a chunk of memory (buffer) on your screen. Pluto.jl uses a web browser.

I just want to show the result without browser, not depending much on browser. Julia REPL and Plots shall be enough. I will try to convert your code from @play

I think you can use either solution, but the code for each will be very different. I don’t think it’s possible to mix the two.

Btw, I try this code and the gif is not showing up:

@tomerarnon’s advice was to move the animation code (“all of the code in the begin-end block”) to a function, and what you should have done was this:

using Luxor

let
    w, h = 600, 400

    ntrig = 360
    sinarray = sind.(1:ntrig)
    cosarray = cosd.(1:ntrig)
    i = 1

    function frame(scene, i)
       # this is "all of the code in the begin-end block moved 
       # into a function with signature frame(scene, i)
       background("gray")
        ...
        ... code omitted for brevity!
        ...
        i += 1
       #
    end

    demo = Movie(600, 400, "sincos trig")
    animate(demo, [Scene(demo, frame, 1:360)], creategif=true, pathname="sincostrig.gif")
end

As I’ve personally discovered many times ( :slight_smile: ), the problem with pasting chunks of code you’ve not seen before and expecting them run perfectly first time is that, if/when they don’t, you have no idea how to fix it, because you didn’t really understand it anyway. So it’s better to start with small pieces of code, written with the help of the documentation, and then assure yourself that pieces of code you copy from elsewhere makes sense to you before you run them.

Good luck!

4 Likes

Yes. I’m sure both Plots.jl and plotly and Makie.jl could easily do this kind of animation. I’m only commenting on this one though. I’m fairly sure you can’t - although I hate saying it - get Plots,jl and Luxor to work together.

I get it, so Plots and Luxor can’t work together.

Thanks for all the information, I was thinking that MiniFB needs Browser since I cannot launch the window at another OS.

Btw, I use your code, it is working. I just need to create the GIF it works at everywhere.
But… mathematically, I think the marker point at the circle needs to be mirrored. I think when the marker point at (0,1), (1,0), (-1,0), (0,-1) they all in reverse / mirrored place of the unit circle.

This code works:

using Luxor

let
    w, h = 600, 400
    ntrig = 360
    sinarray = sind.(1:ntrig)
    cosarray = cosd.(1:ntrig)
    i = 1

    function frame(scene, i)
        
        background("green"); sethue("white")

        # translate near bottom right corner
        origin(0.8w, 0.8h)
        setline(1); setopacity(1)
        cr = 0.15h
        circle(0, 0, cr, :stroke)
        circle_marker_pos = getworldposition(Point(cr*cosarray[i], cr*sinarray[i]))
        
        text(string("cos $i"),
        Point(O.x-73, O.y-290),
        halign=:center)
            
        text(string("sin $i"),
        Point(O.x-73, O.y),
        halign=:center)    
            
        # translate to lower 20%, on the left
        origin(0, 0.8h)
        setline(1); setopacity(1)
        scale(1.0, cr)
        # draw a sin wave
        for j in 1:ntrig-1
            p1 = Point(j, sinarray[mod(j+i, ntrig)+1])
            p2 = Point((j+1), sinarray[mod(j+i+1, ntrig)+1])
            line(p1, p2, :stroke)
        end
        # record the last point of the sin wave
        sin_marker_pos = getworldposition(Point(ntrig,sinarray[mod(ntrig+i, ntrig)+1]))

        # translate to top left corner, then down 20%
        origin(0, 0.2h)
        scale(1.0, cr)
        setline(1); setopacity(1)
        # draw a sin wave
        for j in 1:ntrig-1
            p1 = Point(j, cosarray[mod(j+i, ntrig)+1])
            p2 = Point((j+1), cosarray[mod(j+i+1, ntrig)+1])
            line(p1, p2, :stroke)
        end
        # record the last point of the sin wave
        cos_marker_pos = getworldposition(Point(ntrig, cosarray[mod(ntrig+i, ntrig)+1]))

        # mark the last point of the sin and cos waves
        origin()
        setline(2); setopacity(1);
        circle(sin_marker_pos, 2, :stroke)
        circle(cos_marker_pos, 2, :stroke)
        circle(circle_marker_pos, 2, :stroke)
        # draw connecting lines between the wave and the circle
        setline(1); setopacity(0.5)
        line(sin_marker_pos, circle_marker_pos, :stroke)
        # line(cos_marker_pos, circle_marker_pos, :stroke)

        i %= ntrig
        i += 1
    end
    demo = Movie(600, 400, "sincos trig")
    animate(demo, [Scene(demo, frame, 1:360)], creategif=true)
end

sincos trig

@cormullion,

I want to ask, do you know how to create the text circling like the marker as well? instead of a fix place for the text.

Is this Add text · Luxor helpful?

I am using this code, but the for loop for sin and cos is really getting into my head. How to stop the k for loop after 360 back to 0 again instead of continuing to 540?

let
    w, h = 800, 600
    ntrig = 360
    sinarray = sind.(1:ntrig)
    cosarray = cosd.(1:ntrig)
    i = 1
    l = 180
    function frame(scene, i)
        
        background("green"); sethue("white")

        # translate near bottom right corner
        origin(0.8w, 0.5h)
        setline(1); setopacity(1)
        cr = 0.15h
        circle(0, 0, cr, :stroke)
        circle_marker_pos = getworldposition(Point(cr*-cosarray[i], cr*sinarray[i]))

	for k = i + 180
	setfont("Georgia Bold", 73)
	sethue("orange")
        text(string("cos $k"),
        Point(O.x-83, O.y-108),
        halign=:center)

	setfont("Georgia Bold", 73)
	sethue("red")
        text(string("sin $k"),
        Point(O.x-83, O.y+108),
        halign=:center)    
	end
		
        # translate to lower 20%, on the left
        origin(0, 0.8h)
        setline(1); setopacity(1)
		sethue("red")
        scale(1.0, cr)
        # draw a sin wave
        for j in 1:ntrig-1
            p1 = Point(j, sinarray[mod(j+i, ntrig)+1])
            p2 = Point((j+1), sinarray[mod(j+i+1, ntrig)+1])
            line(p1, p2, :stroke)
        end
        # record the last point of the sin wave
        sin_marker_pos = getworldposition(Point(ntrig,sinarray[mod(ntrig+i, ntrig)+1]))

        # translate to top left corner, then down 20%
        origin(0, 0.2h)
        scale(1.0, cr)
        setline(1); setopacity(1)
		sethue("orange")
        # draw a cos wave
        for j in 1:ntrig-1
            p1 = Point(j, cosarray[mod(j+i, ntrig)+1])
            p2 = Point((j+1), cosarray[mod(j+i+1, ntrig)+1])
            line(p1, p2, :stroke)
        end
        # record the last point of the cos wave
        cos_marker_pos = getworldposition(Point(ntrig, cosarray[mod(ntrig+i, ntrig)+1]))

        # mark the last point of the sin and cos waves
        origin()
        setline(2); setopacity(1);
        circle(sin_marker_pos, 2, :stroke)
        circle(cos_marker_pos, 2, :stroke)
        circle(circle_marker_pos, 2, :stroke)
        # draw connecting lines between the wave and the circle
        setline(1); setopacity(0.5)
        line(sin_marker_pos, circle_marker_pos, :stroke)
        line(cos_marker_pos, circle_marker_pos, :stroke)

        i %= ntrig
        i += 1
	l += 1	
    end
    demo = Movie(800, 600, "sincos trig")
    animate(demo, [Scene(demo, frame, 1:360)], creategif=true)
end

sincos trig