Python >> Python tutorial >  >> Python

Ved du virkelig, hvad sinus og cosinus er? Visualisering af matematik ved hjælp af Python og Turtle

Sinus og cosinus er overalt. Men ikke alle forstår rigtigt, hvad de er. I denne artikel skriver du et Python-program ved hjælp af turtle modul for at visualisere, hvordan de er relateret til en cirkel. Dog går beskeden med hjem ud over sines og cosinus. Det handler om at visualisere matematik ved hjælp af Python-kode mere generelt.

Visualisering af matematiske sammenhænge eller videnskabelige processer kan hjælpe med at studere og forstå disse emner. Det er et værktøj, jeg har brugt meget i min tidligere arbejde som videnskabsmand. Faktisk er det en af ​​de ting, der fik mig til at forelske mig i programmering.

Her er animationen, du vil oprette i slutningen af ​​denne artikel:

Hvordan er sinus og cosinus knyttet til en cirkel?

Før du dykker ned i at skrive koden, lad os se, hvorfor det kan være nyttigt at visualisere matematik ved hjælp af Python. I dette tilfælde udforsker du en af ​​nøglebyggestenene i mange områder af matematikken - sinus og cosinus.

Du ved hvad synd(x ) og cos(x ) ligner. Men hvordan er de knyttet til cirklen?

Se videoen ovenfor en gang til...

Den blå prik går rundt om omkredsen af ​​en cirkel med en konstant vinkelhastighed. Dens bevægelse er jævn, og prikken bliver ikke langsommere eller hurtigere.

Se nu på den gule prik. Kan du se sammenhængen mellem den gule prik og den blå prik?

Hvis du ser godt efter, vil du bemærke, at den gule prik bevæger sig langs en lodret linje, op og ned. Ignorer sporet, der ser ud til at bevæge sig ud fra prikken. Bare fokuser på prikken indtil videre.

Den lodrette position af den gule prik sporer den blå priks lodrette position. Den gule prik er x- koordinat ændres ikke, men dens y- koordinaten er altid den samme som den blå prik.

Her er den næste ting, du skal være opmærksom på. Er hastigheden af ​​den gule prik konstant? Eller går den hurtigere og langsommere?

Du har sikkert bemærket, at den gule prik bevæger sig hurtigst, når den er midt i sin op- og ned-bevægelse, når den krydser midterlinjen. Så sænker den farten, når den når toppen eller bunden, ændrer retning og accelererer derefter mod midten igen.

Dette giver mening, når du husker, at den blå prik bevæger sig med konstant hastighed. Når den gule prik er øverst eller nederst, bevæger den blå prik sig for det meste vandret. Derfor er den blå priks lodrette bevægelse lille eller nul, når den er nær toppen og bunden af ​​cirklen.

y- koordinat af den gule prik følger synd(𝜃). Diagrammet nedenfor viser vinklen 𝜃 :

Vinklen måles fra x- akse til den radius, der forbinder prikken med midten.

Du kan se formen af ​​sinuskurven komme frem fra sporet, som den gule prik efterlader.

Jeg har diskuteret den gule prik længe. Hvad med den røde prik? Denne prik sporer den blå priks vandrette position. Dens x- koordinat følger cos(𝜃).

Okay, så hvis du kun er interesseret i at visualisere sinus og cosinus, og hvordan de er relateret til cirklen, så er vi færdige.

Men hvis du også er interesseret i at visualisere matematik ved hjælp af Python, så læs videre...

Opsætning af Turtle-programmet

Du skal bruge turtle modul i dette program til visualisering af matematik ved hjælp af Python. Dette modul giver dig mulighed for at tegne på en forholdsvis ligetil måde. Det er en del af Pythons standardbibliotek, så du vil allerede have dette modul på din computer, hvis du har installeret Python.

Bare rolig, hvis du aldrig har brugt dette modul før. Jeg vil forklare de funktioner og metoder, du vil bruge.

Du kan starte med at importere modulet og oprette dit vindue, hvor din animation skal køre:

import turtle

window = turtle.Screen()
window.bgcolor(50 / 255, 50 / 255, 50 / 255)

turtle.done()

turtle.Screen() call opretter en forekomst af skærmen kaldet window .

Du ændrer også dens baggrundsfarve. Som standard er turtle repræsenterer de røde, grønne og blå komponenter i en farve som en flyder mellem 0 og 1 . Selvom du kan ændre tilstanden til at bruge heltal mellem 0 og 255 , er det lige så nemt at dividere RGB-værdierne med 255 . Vi går efter et mørkt udseende i denne animation!

Den sidste linje, turtle.done() , holder vinduet åbent, når programmet har tegnet alt andet. Uden denne linje vil programmet afslutte og lukke vinduet, så snart det når slutningen.

Hovedtegningsobjektet i turtle modul er turtle.Turtle() . Du kan flytte en skildpadde rundt på skærmen for at tegne. For at tegne cirklen i animationen skal du oprette en skildpadde. Du kan også indstille en variabel for radius af cirklen:

import turtle

radius = 100

window = turtle.Screen()
window.bgcolor(50 / 255, 50 / 255, 50 / 255)

main_dot = turtle.Turtle()
main_dot.pensize(5)
main_dot.shape("circle")
main_dot.color(0, 160 / 255, 193 / 255)
main_dot.penup()
main_dot.setposition(0, -radius)
main_dot.pendown()

turtle.done()

Når du har oprettet skildpadden, ændrer du "pennen"-størrelsen, så når skildpadden tegner linjer senere i programmet, vil disse linjer være tykkere end standardværdien. Du ændrer også formen på selve skildpadden. Du bruger argumentet "circle" når du bruger .shape() , som sætter formen på skildpadden til en prik.

Dernæst indstiller du farven til blå og løfter pennen op, så du kan flytte skildpadden til dens startposition uden at tegne en streg, mens skildpadden bevæger sig. Du holder skildpadden ved x=0 men skift y- koordinere til den negative værdi af radius. Dette flytter prikken nedad. Dette trin sikrer, at cirklens centrum er i midten af ​​vinduet, da prikken, når du tegner en cirkel, vil gå rundt mod uret (mod uret).

Når du kører denne kode, vil du se den blå prik på den mørkegrå baggrund:

Gå rundt i en cirkel

Dernæst kan du få prikken til at gå rundt i en cirkel. Der er flere måder, du kan opnå dette ved at bruge turtle modul. Du skal dog bruge .circle() metode her. Denne metode har et påkrævet argument, radius , som bestemmer størrelsen af ​​cirklen.

Du kan skrive main_dot.circle(radius) . Dette vil dog tegne hele cirklen på én gang. Du ønsker at opdele dette i mindre trin, da du bliver nødt til at udføre andre opgaver ved hver position af dette hovedpunkt.

Du kan bruge det valgfrie argument extent i .circle() . Dette argument bestemmer, hvor meget af cirklen, der tegnes. Eksperimenter med main_dot.circle(radius, 180) , som tegner en halvcirkel, og prøv så andre vinkler.

I dette projekt kan du indstille en variabel kaldet angular_speed og tegn derefter en lille del af cirklen i hver iteration af en while sløjfe:

import turtle

radius = 100
angular_speed = 2

window = turtle.Screen()
window.tracer(0)
window.bgcolor(50 / 255, 50 / 255, 50 / 255)

main_dot = turtle.Turtle()
main_dot.pensize(5)
main_dot.shape("circle")
main_dot.color(0, 160 / 255, 193 / 255)
main_dot.penup()
main_dot.setposition(0, -radius)
main_dot.pendown()

while True:
    main_dot.circle(radius, angular_speed)

    window.update()

turtle.done()

Prikken vil tegne en lille bue af cirklen i hver iteration af while sløjfe. Siden du har indstillet angluar_speed til 2 , vil skildpadden tegne 2º af cirklen i hver iteration.

Du har også indstillet window.tracer(0) så snart du opretter vinduet. Dette stopper hvert skridt, hver skildpadde tager, fra at blive tegnet på skærmen. I stedet giver det dig kontrol over, hvornår du skal opdatere skærmen. Du tilføjer window.update() i slutningen af ​​while sløjfe for at opdatere skærmen én gang for hver gentagelse. Én iteration af loopet svarer til én frame af animationen.

Bruger window.tracer(0) og window.update() giver dig mere kontrol over animationen og fremskynder også tegningen, især når programmet skal tegne en masse ting.

Når du kører koden, vil du se prikken gå rundt i en cirkel:

Du skal ikke bekymre dig om hastigheden af ​​prikken på dette tidspunkt. Du vil håndtere den overordnede hastighed af animationen mod slutningen, når du har alt andet allerede på plads. Du kan dog bruge en mindre værdi for angular_speed hvis du vil bremse det.

Sporing af den blå prik's lodrette bevægelse

Du kan nu oprette en anden skildpadde, som vil spore den blå priks lodrette bevægelse. Du vil gøre denne prik gul og flytte den til højre for cirklen:

import turtle

radius = 100
angular_speed = 2

window = turtle.Screen()
window.tracer(0)
window.bgcolor(50 / 255, 50 / 255, 50 / 255)

main_dot = turtle.Turtle()
main_dot.pensize(5)
main_dot.shape("circle")
main_dot.color(0, 160 / 255, 193 / 255)
main_dot.penup()
main_dot.setposition(0, -radius)
main_dot.pendown()

vertical_dot = turtle.Turtle()
vertical_dot.shape("circle")
vertical_dot.color(248 / 255, 237 / 255, 49 / 255)
vertical_dot.penup()
vertical_dot.setposition(
    main_dot.xcor() + 2 * radius,
    main_dot.ycor(),
)

while True:
    main_dot.circle(radius, angular_speed)

    vertical_dot.sety(main_dot.ycor())

    window.update()

turtle.done()

Du indstiller vertical_dot 's oprindelige position ved at bruge positionen main_dot som reference. Metoderne main_dot.xcor() og main_dot.ycor() returner x- og y- skildpaddens koordinater, når de bliver kaldt. Hvis du vælger at flytte cirklen til en anden del af skærmen, vertical_dot vil flytte med det, da du linker vertical_dot 's position til main_dot .

Du tilføjer 2 * radius til main_dot 's x- koordinere, så vertical_dot er altid dobbelt så langt fra midten af ​​cirklen som cirklens omkreds på x- akse.

I while loop, ændrer du vertical_dot 's y- koordinere ved hjælp af vertical_dot.sety() . Denne prik sporer main_dot 's y- coordinate, hvilket betyder, at main_dot og vertical_dot vil altid have samme højde på skærmen.

Du kan se den gule prik spore den blå priks lodrette position, når du kører denne kode:

Du kan i øvrigt også fjerne turtle.done() ring i slutningen af ​​koden nu, da du har en uendelig løkke kørende, så programmet vil aldrig afslutte. Jeg fjerner dette opkald i det næste kodestykke.

Sporing af den blå prik's vandrette bevægelse

Du kan følge det samme mønster ovenfor for at spore den blå priks vandrette bevægelse. I dette tilfælde kan du indstille prikkens farve til rød og placere den under cirklen:

import turtle

radius = 100
angular_speed = 2

window = turtle.Screen()
window.tracer(0)
window.bgcolor(50 / 255, 50 / 255, 50 / 255)

main_dot = turtle.Turtle()
main_dot.pensize(5)
main_dot.shape("circle")
main_dot.color(0, 160 / 255, 193 / 255)
main_dot.penup()
main_dot.setposition(0, -radius)
main_dot.pendown()

vertical_dot = turtle.Turtle()
vertical_dot.shape("circle")
vertical_dot.color(248 / 255, 237 / 255, 49 / 255)
vertical_dot.penup()
vertical_dot.setposition(
    main_dot.xcor() + 2 * radius,
    main_dot.ycor(),
)

horizontal_dot = turtle.Turtle()
horizontal_dot.shape("circle")
horizontal_dot.color(242 / 255, 114 / 255, 124 / 255)
horizontal_dot.penup()
horizontal_dot.setposition(
    main_dot.xcor(),
    main_dot.ycor() - radius,
)

while True:
    main_dot.circle(radius, angular_speed)

    vertical_dot.sety(main_dot.ycor())

    horizontal_dot.setx(main_dot.xcor())

    window.update()

Du indstiller horizontal_dot ’s startposition i en afstand lig med radius lavere end main_dot . Husk, at den blå prik begynder at tegne cirklen fra dets laveste punkt. I while loop, horizontal_dot sporer main_dot 's x- koordinere. Derfor deler de blå og røde prikker altid den samme position langs x- akse.

Animationen har nu både gule og røde prikker, der sporer den blå prik:

Du kan allerede se, at selvom den blå prik bevæger sig med konstant hastighed rundt om cirklens omkreds, er de gule og røde prikker hurtigere og langsommere, mens de bevæger sig.

Du kan stoppe her. Eller du kan læse videre for at tilføje et spor til de gule og røde prikker for at vise, hvordan deres hastighed ændrer sig mere detaljeret.

Tilføjelse af et spor til de gule og røde prikker

Dernæst får du de gule og røde prikker til at efterlade et mærke på tegnelærredet i hver loop-iteration. Disse mærker vil derefter bevæge sig udad for at gøre plads til de nye mærker tegnet af prikkerne i den næste iteration.

Lad os først fokusere på den gule prik. Du kan oprette en ny skildpadde ved at klone vertical_dot og gemmer selve skildpadden. Du kan derefter oprette et sæt x- værdier for at repræsentere alle punkterne mellem x- placering af den gule prik og højre sidekant af vinduet:

import turtle

radius = 100
angular_speed = 2

window = turtle.Screen()
window.tracer(0)
window.bgcolor(50 / 255, 50 / 255, 50 / 255)

main_dot = turtle.Turtle()
main_dot.pensize(5)
main_dot.shape("circle")
main_dot.color(0, 160 / 255, 193 / 255)
main_dot.penup()
main_dot.setposition(0, -radius)
main_dot.pendown()

vertical_dot = turtle.Turtle()
vertical_dot.shape("circle")
vertical_dot.color(248 / 255, 237 / 255, 49 / 255)
vertical_dot.penup()
vertical_dot.setposition(
    main_dot.xcor() + 2 * radius,
    main_dot.ycor(),
)

vertical_plot = vertical_dot.clone()
vertical_plot.hideturtle()
start_x = int(vertical_plot.xcor())
# Get range of x-values from position of dot to edge of screen
x_range = range(start_x, window.window_width() // 2 + 1)
# Create a list to store the y-values to draw at each
# point in x_range.
vertical_values = [None for _ in x_range]
# You can populate the first item in this list
# with the dot's starting height
vertical_values[0] = vertical_plot.ycor()

horizontal_dot = turtle.Turtle()
horizontal_dot.shape("circle")
horizontal_dot.color(242 / 255, 114 / 255, 124 / 255)
horizontal_dot.penup()
horizontal_dot.setposition(
    main_dot.xcor(),
    main_dot.ycor() - radius,
)

while True:
    main_dot.circle(radius, angular_speed)

    vertical_dot.sety(main_dot.ycor())

    horizontal_dot.setx(main_dot.xcor())

    window.update()

Variablen x_range gemmer alle punkter fra x- placeringen af ​​den gule prik til kanten af ​​skærmen. I dette eksempel bruger jeg alle pixels. Du kan dog bruge det valgfrie argument step når du bruger range() hvis du foretrækker at bruge en delmængde af disse punkter, for eksempel én ud af hver to pixels. Brug af færre punkter kan fremskynde animationen, hvis den bliver for træg, men det vil også ændre frekvensen af ​​sinuskurven, som animationen viser.

Du har også oprettet listen vertical_values hvis længde bestemmes af antallet af punkter i x_range . Hvert element på denne liste vil indeholde y- koordinater, der skal tegnes. Alle disse værdier er sat til None i første omgang undtagen det første punkt.

Den blå prik vil bevæge sig i hver iteration i while sløjfe. Derfor vil den gule prik også. Dernæst skal du registrere den nye y- koordinat for den gule prik i vertical_values liste. Men først skal du flytte alle de eksisterende værdier fremad. Det første element bliver det andet, det andet element bliver det tredje, og så videre.

Lad os prøve denne tilgang først:

import turtle

radius = 100
angular_speed = 2

window = turtle.Screen()
window.tracer(0)
window.bgcolor(50 / 255, 50 / 255, 50 / 255)

main_dot = turtle.Turtle()
main_dot.pensize(5)
main_dot.shape("circle")
main_dot.color(0, 160 / 255, 193 / 255)
main_dot.penup()
main_dot.setposition(0, -radius)
main_dot.pendown()

vertical_dot = turtle.Turtle()
vertical_dot.shape("circle")
vertical_dot.color(248 / 255, 237 / 255, 49 / 255)
vertical_dot.penup()
vertical_dot.setposition(
    main_dot.xcor() + 2 * radius,
    main_dot.ycor(),
)

vertical_plot = vertical_dot.clone()
vertical_plot.hideturtle()
start_x = int(vertical_plot.xcor())
# Get range of x-values from position of dot to edge of screen
x_range = range(start_x, window.window_width() // 2 + 1)
# Create a list to store the y-values to draw at each
# point in x_range.
vertical_values = [None for _ in x_range]
# You can populate the first item in this list
# with the dot's starting height
vertical_values[0] = vertical_plot.ycor()

horizontal_dot = turtle.Turtle()
horizontal_dot.shape("circle")
horizontal_dot.color(242 / 255, 114 / 255, 124 / 255)
horizontal_dot.penup()
horizontal_dot.setposition(
    main_dot.xcor(),
    main_dot.ycor() - radius,
)

while True:
    main_dot.circle(radius, angular_speed)

    vertical_dot.sety(main_dot.ycor())
    vertical_plot.clear()
    # Shift all values one place to the right
    vertical_values[2:] = vertical_values[
        : len(vertical_values) - 1
    ]
    # Record the current y-value as the first item
    # in the list
    vertical_values[0] = vertical_dot.ycor()
    # Plot all the y-values
    for x, y in zip(x_range, vertical_values):
        if y is not None:
            vertical_plot.setposition(x, y)
            vertical_plot.dot(5)

    horizontal_dot.setx(main_dot.xcor())

    window.update()

Når du flyttede alle y- værdier i vertical_values ved ét sted til højre og tilføjede det nye y- koordinere i starten af ​​listen, plotter du alle punkterne på skærmen. Du bruger Pythons zip() funktion til at gå gennem x_range og vertical_values på samme tid.

Du tegner en prik ved y- koordinater gemt i listen for hver x- position på tegnelærredet. Bemærk, at du også ringer til vertical_plot.clear() i hver iteration for at rydde plottet fra den forrige frame af animationen.

Du flytter værdier til højre på listen, så du kan tilføje et nyt element i begyndelsen. Dette er ikke en effektiv proces med lister. Du kommer tilbage til dette punkt senere i denne artikel for at bruge en datastruktur, der er bedre egnet til denne opgave. Men indtil videre kan du holde fast i denne tilgang.

Animationen ser i øjeblikket sådan ud:

Tilføjelse af et spor til den røde prik

Processen med at tilføje et spor til den røde prik er meget ens. Den eneste forskel er, at du bevæger dig lodret nedad for at få sporet i stedet for mod højre.

Du kan replikere processen ovenfor for den røde prik:

import turtle

radius = 100
angular_speed = 2

window = turtle.Screen()
window.tracer(0)
window.bgcolor(50 / 255, 50 / 255, 50 / 255)

main_dot = turtle.Turtle()
main_dot.pensize(5)
main_dot.shape("circle")
main_dot.color(0, 160 / 255, 193 / 255)
main_dot.penup()
main_dot.setposition(0, -radius)
main_dot.pendown()

vertical_dot = turtle.Turtle()
vertical_dot.shape("circle")
vertical_dot.color(248 / 255, 237 / 255, 49 / 255)
vertical_dot.penup()
vertical_dot.setposition(
    main_dot.xcor() + 2 * radius,
    main_dot.ycor(),
)

vertical_plot = vertical_dot.clone()
vertical_plot.hideturtle()
start_x = int(vertical_plot.xcor())
# Get range of x-values from position of dot to edge of screen
x_range = range(start_x, window.window_width() // 2 + 1)
# Create a list to store the y-values to draw at each
# point in x_range.
vertical_values = [None for _ in x_range]
# You can populate the first item in this list
# with the dot's starting height
vertical_values[0] = vertical_plot.ycor()

horizontal_dot = turtle.Turtle()
horizontal_dot.shape("circle")
horizontal_dot.color(242 / 255, 114 / 255, 124 / 255)
horizontal_dot.penup()
horizontal_dot.setposition(
    main_dot.xcor(),
    main_dot.ycor() - radius,
)

horizontal_plot = horizontal_dot.clone()
horizontal_plot.hideturtle()
start_y = int(horizontal_plot.ycor())
y_range = range(start_y, -window.window_height() // 2 - 1, -1)
horizontal_values = [None for _ in y_range]
horizontal_values[0] = horizontal_plot.xcor()

while True:
    main_dot.circle(radius, angular_speed)

    vertical_dot.sety(main_dot.ycor())
    vertical_plot.clear()
    # Shift all values one place to the right
    vertical_values[2:] = vertical_values[
        : len(vertical_values) - 1
    ]
    # Record the current y-value as the first item
    # in the list
    vertical_values[0] = vertical_dot.ycor()
    # Plot all the y-values
    for x, y in zip(x_range, vertical_values):
        if y is not None:
            vertical_plot.setposition(x, y)
            vertical_plot.dot(5)

    horizontal_dot.setx(main_dot.xcor())
    horizontal_plot.clear()
    horizontal_values[2:] = horizontal_values[
        : len(horizontal_values) - 1
    ]
    horizontal_values[0] = horizontal_dot.xcor()
    for x, y in zip(horizontal_values, y_range):
        if x is not None:
            horizontal_plot.setposition(x, y)
            horizontal_plot.dot(5)

    window.update()

Bemærk, at når du bruger range() for at oprette y_range , bruger du det tredje, valgfrie argument step og indstil den til -1 . Du gør dette siden y_range er faldende, da det handler om negative værdier.

Brug af Deques i stedet for lister

Lad mig starte med et forord til dette afsnit. Du kan ignorere det og gå videre til næste afsnit af denne artikel. Ændringen, du vil foretage i din kode her, vil ikke påvirke dit program meget. Det er dog en mulighed for at udforske Pythons deque datastruktur.

Jeg har skrevet lidt detaljeret om deques og hvordan de bruges i stakke og køer i det allerførste indlæg på denne blog. Du kan også læse mere om dette emne i denne artikel.

I en nøddeskal er det ikke effektivt at blande elementer på en liste. Python tilbyder en dobbeltkødatastruktur kendt som deque . Denne datastruktur er en del af collections modul.

En deque giver dig mulighed for effektivt at tilføje et element i begyndelsen af ​​en sekvens ved hjælp af .appendleft() metode. Når du bruger .pop() på en deque, er det sidste element i sekvensen "poppet ud".

Du kan derfor ændre din kode til at bruge deque . Jeg indstiller også vinduet til at være firkantet:

import collections
import turtle

radius = 100
angular_speed = 2

window = turtle.Screen()
window.setup(1000, 1000)
window.tracer(0)
window.bgcolor(50 / 255, 50 / 255, 50 / 255)

main_dot = turtle.Turtle()
main_dot.pensize(5)
main_dot.shape("circle")
main_dot.color(0, 160 / 255, 193 / 255)
main_dot.penup()
main_dot.setposition(0, -radius)
main_dot.pendown()

vertical_dot = turtle.Turtle()
vertical_dot.shape("circle")
vertical_dot.color(248 / 255, 237 / 255, 49 / 255)
vertical_dot.penup()
vertical_dot.setposition(
    main_dot.xcor() + 2 * radius,
    main_dot.ycor(),
)

vertical_plot = vertical_dot.clone()
vertical_plot.hideturtle()
start_x = int(vertical_plot.xcor())
# Get range of x-values from position of dot to edge of screen
x_range = range(start_x, window.window_width() // 2 + 1)
# Create a list to store the y-values to draw at each
# point in x_range.
vertical_values = collections.deque(None for _ in x_range)
# You can populate the first item in this list
# with the dot's starting height
vertical_values[0] = vertical_plot.ycor()

horizontal_dot = turtle.Turtle()
horizontal_dot.shape("circle")
horizontal_dot.color(242 / 255, 114 / 255, 124 / 255)
horizontal_dot.penup()
horizontal_dot.setposition(
    main_dot.xcor(),
    main_dot.ycor() - radius,
)

horizontal_plot = horizontal_dot.clone()
horizontal_plot.hideturtle()
start_y = int(horizontal_plot.ycor())
y_range = range(start_y, -window.window_height() // 2 - 1, -1)
horizontal_values = collections.deque(None for _ in y_range)
horizontal_values[0] = horizontal_plot.xcor()

while True:
    main_dot.circle(radius, angular_speed)

    vertical_dot.sety(main_dot.ycor())
    vertical_plot.clear()
    # Add new value at the start, and delete last value
    vertical_values.appendleft(vertical_dot.ycor())
    vertical_values.pop()
    # Plot all the y-values
    for x, y in zip(x_range, vertical_values):
        if y is not None:
            vertical_plot.setposition(x, y)
            vertical_plot.dot(5)

    horizontal_dot.setx(main_dot.xcor())
    horizontal_plot.clear()
    horizontal_values.appendleft(horizontal_dot.xcor())
    horizontal_values.pop()
    for x, y in zip(horizontal_values, y_range):
        if x is not None:
            horizontal_plot.setposition(x, y)
            horizontal_plot.dot(5)

    window.update()

Animationen er næsten færdig nu:

Bemærk, at videoerne vist i denne artikel er fremskyndet til visningsformål. Hastigheden af ​​animationen vil variere afhængigt af din opsætning. Du bemærker måske, at animationen bliver langsommere efter et stykke tid, da der er flere punkter, som den skal tegne i de to spor. Hvis du ønsker det, kan du sænke hvert billede til en langsommere billedhastighed for at undgå dette. Det følgende afsnit vil guide dig gennem én måde at gøre dette på.

Indstilling af animationens billedhastighed

For at opnå en mere jævn animation kan du vælge en billedhastighed og sikre, at hvert enkelt billede aldrig kører hurtigere end nødvendigt. På nuværende tidspunkt afhænger den tid, hver frame tager, af, hvor hurtigt programmet kan udføre alle operationerne i while sløjfe. En væsentlig bidragyder til den tid, det tager hvert billede, er visningen af ​​grafikken på skærmen.

Du kan indstille en timer, der starter i begyndelsen af ​​hvert billede og derefter sørge for, at der er gået nok tid, før du går videre til næste iteration af animationsløkken:

import collections
import time
import turtle

radius = 100
angular_speed = 2

fps = 12  # Frames per second
time_per_frame = 1 / fps

window = turtle.Screen()
window.setup(1000, 1000)
window.tracer(0)
window.bgcolor(50 / 255, 50 / 255, 50 / 255)

main_dot = turtle.Turtle()
main_dot.pensize(5)
main_dot.shape("circle")
main_dot.color(0, 160 / 255, 193 / 255)
main_dot.penup()
main_dot.setposition(0, -radius)
main_dot.pendown()

vertical_dot = turtle.Turtle()
vertical_dot.shape("circle")
vertical_dot.color(248 / 255, 237 / 255, 49 / 255)
vertical_dot.penup()
vertical_dot.setposition(
    main_dot.xcor() + 2 * radius,
    main_dot.ycor(),
)

vertical_plot = vertical_dot.clone()
vertical_plot.hideturtle()
start_x = int(vertical_plot.xcor())
# Get range of x-values from position of dot to edge of screen
x_range = range(start_x, window.window_width() // 2 + 1)
# Create a list to store the y-values to draw at each
# point in x_range.
vertical_values = collections.deque(None for _ in x_range)
# You can populate the first item in this list
# with the dot's starting height
vertical_values[0] = vertical_plot.ycor()

horizontal_dot = turtle.Turtle()
horizontal_dot.shape("circle")
horizontal_dot.color(242 / 255, 114 / 255, 124 / 255)
horizontal_dot.penup()
horizontal_dot.setposition(
    main_dot.xcor(),
    main_dot.ycor() - radius,
)

horizontal_plot = horizontal_dot.clone()
horizontal_plot.hideturtle()
start_y = int(horizontal_plot.ycor())
y_range = range(start_y, -window.window_height() // 2 - 1, -1)
horizontal_values = collections.deque(None for _ in y_range)
horizontal_values[0] = horizontal_plot.xcor()

while True:
    frame_start = time.time()
    main_dot.circle(radius, angular_speed)

    vertical_dot.sety(main_dot.ycor())
    vertical_plot.clear()
    # Add new value at the start, and delete last value
    vertical_values.appendleft(vertical_dot.ycor())
    vertical_values.pop()
    # Plot all the y-values
    for x, y in zip(x_range, vertical_values):
        if y is not None:
            vertical_plot.setposition(x, y)
            vertical_plot.dot(5)

    horizontal_dot.setx(main_dot.xcor())
    horizontal_plot.clear()
    horizontal_values.appendleft(horizontal_dot.xcor())
    horizontal_values.pop()
    for x, y in zip(horizontal_values, y_range):
        if x is not None:
            horizontal_plot.setposition(x, y)
            horizontal_plot.dot(5)

    # Wait until minimum frame time reached
    while time.time() - frame_start < time_per_frame:
        pass
    window.update()

Antallet af billeder pr. sekund er 12 , hvilket betyder, at minimumstiden pr. frame er 1/12 =0,083s. Du starter timeren i begyndelsen af ​​animationen while sløjfe. Derefter tilføjer du endnu en while sløjfe, der venter, indtil den påkrævede tid er gået, før den afslutter hoved-while loop iteration.

Bemærk, at dette ikke garanterer, at rammelængden vil være 0,083s. Hvis operationerne i while loop tage længere tid at køre, så holder rammen i længere tid. Det gør garantere, at en frame ikke kan være kortere end 0,083s.

Hvis du stadig bemærker, at din animation bliver langsommere efter de første billeder, skal du indstille din billedhastighed til en lavere værdi. Du kan kontrollere, om dine rammer overskrider, ved at tilføje en hurtig bekræftelse til din kode:

# ...

while True:
    frame_start = time.time()
    frame_overrun = True
    main_dot.circle(radius, angular_speed)

    vertical_dot.sety(main_dot.ycor())
    vertical_plot.clear()
    # Add new value at the start, and delete last value
    vertical_values.appendleft(vertical_dot.ycor())
    vertical_values.pop()
    # Plot all the y-values
    for x, y in zip(x_range, vertical_values):
        if y is not None:
            vertical_plot.setposition(x, y)
            vertical_plot.dot(5)

    horizontal_dot.setx(main_dot.xcor())
    horizontal_plot.clear()
    horizontal_values.appendleft(horizontal_dot.xcor())
    horizontal_values.pop()
    for x, y in zip(horizontal_values, y_range):
        if x is not None:
            horizontal_plot.setposition(x, y)
            horizontal_plot.dot(5)

    # Wait until minimum frame time reached
    while time.time() - frame_start < time_per_frame:
        frame_overrun = False
    if frame_overrun:
        print("Frame overrun")
    window.update()

Genseer matematikken

Visualisering af matematik ved hjælp af Python hjælper med at forstå de matematiske begreber bedre. Lad os nu forbinde observationerne fra denne animation med den matematik, vi kender.

Ud fra de generelle definitioner af sinus og cosinus kan vi forbinde den modsatte, tilstødende og hypotenusen af ​​en retvinklet trekant med vinklen 𝜃:

I diagrammet ovenfor falder trekantens toppunkt sammen med den blå prik, der går rundt i en cirkel. Cirklens radius er R . Højden af ​​den blå kugle fra x- aksen er R sin(𝜃) og den vandrette afstand fra y- aksen er R cos(𝜃).

Den animation, du skrev, gentager dette resultat.

Du kan ændre amplituden af ​​sinus og cosinus ved at ændre variablen radius . Du kan ændre frekvensen ved at ændre angular_speed :

Afsluttende ord

I denne artikel har du skrevet et program ved hjælp af Pythons turtle modul for at udforske, hvordan sinus og cosinus er knyttet til cirklen. Ved at spore de lodrette og vandrette positioner af prikken, der går rundt i en cirkel, har du demonstreret sinus- og cosinusfunktionerne. Disse funktioner optræder meget ofte i mange matematikapplikationer.

turtle modul er ikke altid det bedste værktøj til at visualisere matematik ved hjælp af Python. Det er det sjældent!

Visualiseringsbiblioteker som Matplotlib er bedst egnede til dette ved hjælp af pakker som NumPy. Så hvis du planlægger at lave mere visualisering af matematik ved hjælp af Python, bør du blive mere fortrolig med disse biblioteker!