playing with trigonometry sin in pygame

Ok I am so far from understanding, so don't take this as any kind of math lesson.
Also, every time I thought I knew what I was doing.... pygame would show me differently. Still a work in progress, but for now, something to play with.
Update: SinWaveTime class added to produce that particular weaving effect
Tinkering with the different sin wave variables:
I wrote this piece separate from my 3D experiment to see how to manipulate the sin wave.
While trying to use time as the factor to move my pygame draw circle on the x-axis, i got this crazy little doo-dad :






I don't think it's drawing two circles at once, but if I figure out what the heck it's doing I'll let you know.  Or hey if you know, drop a comment.  I don't know if my comments are just not working, or no one wants to.

So, here's some pygame code to play with the sin wave.  Alter the different variables to see it in action.

picture of SinWave.move_with_time()
see changes, SinWaveTime class set up to specifically produce this effect






picture of SinWave.move_point2()



picture of SinWave.move_point():






code:
There are a ton of comment in the code. It helps me remember, and my guess is they're helpful to learners like me. *or people who just go: "What the hell was she thinking!?"*


#!usr/bin/python3
# -*- coding: utf-8 -*-

import pygame as pg
import math
from sys import exit



class SkullGlobals(object):
    """ class container for globally used variables """
    def __init__(self):
        self.WIDTH = 600
        self.HEIGHT = 600
        self.background_color = (130, 120, 100)
        self.SAND = (130, 120, 100)
        self.BLUE = (40, 60, 200)
        self.LBLUE = (100, 100, 255)
        self.RED = (230, 50, 50)
        self.GREEN = (50, 230, 50)
        self.D_WHITE = (200,200,200)
        self.CENTER = (self.WIDTH//2, self.HEIGHT//2)
        self.FULL_SIZE = (self.WIDTH//4 + self.HEIGHT//4)//2
        self.SMALL_SIZE = (self.WIDTH//50 + self.HEIGHT//50)//2
        self.base_color = 150


class SinWave(object):
    def __init__(self, screen, globals):
        self.screen = screen
        self.globals = globals
        self.x_pos = 0
        self.max_x = self.globals.WIDTH
        self.start_y_pos = self.globals.HEIGHT
        self.y_pos = self.globals.HEIGHT
        self.max_y = self.globals.HEIGHT
        self.step = .015    # nice sharp 0.2 with 0.03 step
        self.blue = 228
        self.red = 10
        self.green = 10
        self.move = 11

    def move_point2(self):
        """This was made to introduce a second wave with different parameters
 """
        frequency = 0.3 # less frequency less wave intensity
        self.x_pos += 2 # increases breadth of wave
        # point position = pp
        pp_y = self.y_pos
        # smaller amp = more solid, small decent waves
        amplitude = 10 # amp higher is taller

        # sine wave formula = swf
        swf = int(-1 * math.sin(self.step * frequency) * amplitude)
        if self.x_pos < self.max_x:
            self.x_pos += 1  # speed?  Most formulas use time for the x position
           # if change in step is not = original step, y position changes   
            self.step += 0.3
        else:
            # reset step, x_pos, move y axis up/down screen
            self.step = 0.03
            self.x_pos = 0
            self.move += 1
            # moving y upscreen:
            self.y_pos -= self.move

            if self.move > 10:
                self.move = 2

        if self.y_pos > -400:
            self.y_pos = self.y_pos + swf
        else:
            # wave out of screen parameters
            self.y_pos = self.start_y_pos

            if self.blue <= 100:
                self.blue += 15
            elif self.blue > 100 and self.blue < 240:
                self.blue +=  10
            else:
                self.blue = 0
            if self.green <= 100:
                self.green += 15
            elif self.green < 240 and self.blue > 100:
                self.green += 10
            else:
                self.green = 0
        #changes here override color changes in above else:
        # they are happening on every circle draw

        if self.red <= 50:
            self.red += 2
        if self.red <= 150:
            self.red += 1
        if self.red > 150 and self.red >= 50:
            self.red -= 3


    def move_point(self):
        """ First play around with altering sin wave
           color change is extremely gradual
           y's position is changed in separate if/else clause.
           x position is change constant. x+= 1 """

        frequency = 0.3 # less frequency less wave intensity
        # smaller amp = more solid, small decent waves
        amplitude = 10 # amp higher is taller

        # sine wave formula = swf
        swf = int(-1 * math.sin(self.step * frequency) * amplitude)
        if self.x_pos < self.max_x:
            self.x_pos += 1
            self.step += 0.15
# if change in step is not = original step, y position changes


        else:
            # reset step, x_pos, move y axis up/down screen
            self.step = 0.03
            self.x_pos = 0
            self.y_pos -= self.move


        if self.y_pos > -300:
            self.y_pos = self.y_pos + swf
        else:
            self.y_pos = self.start_y_pos

            if self.green < 50:
                self.green += 10
            elif self.green < 150:
                self.green += 15
            elif self.green < 240:
                self.green += 5
            else:
                self.green = 0

        if self.blue <= 50:
            self.blue += 175
        if self.blue >= 150:
            self.blue -= 1
        if self.blue >= 225:
            self.blue -= 1

    def move_with_time(self):
        """
        A couple of the original pieces I found online said to
        use time to change the x coordinate.
        That'll be this one, with just a white circle.
        It'll be set up to not alter where y draws the circle
        except for the sin wave
        I got some really fun results tinkering with this
         """

         # define a frequency and amplitude:
         # if either of these ends up 0, swf will = 0

        amplitude = 20
        frequency = 10
        ##############################
        # cool little weavy pattern:
        #frequency = 4
        #amplitude = 40
        #step = 0.027
        #self.y_pos = self.globals.HEIGHT//2
        #####################################

        # set up y position to middle of screen
        self.y_pos = self.globals.HEIGHT//2
        #http://pygametutorials.wikidot.com/book-time
        # pygame.time is measured in 1/1000 of a second
        #time = (pg.time.get_ticks())//1000
        # the above gradually increases, so wave speeds up visually
        # it is not a constant range

        time = (pg.time.get_ticks()//1000)
        # modulate:
        # modulating will mean the distance between x points
        # can be 0 - 3, as below
        # at 0 x does not move, stalling the movement across x
        # so I took that out with the little if below

        time = time % 3
        if time == 0:
            time += 1
        # sin formula:
        swf = int(-1 * math.sin(self.step * frequency) * amplitude)

        # set the changes
        if self.x_pos < self.max_x:
            self.x_pos = self.x_pos + time
            self.y_pos = self.y_pos + swf
            # weavy step:
            #self.step += 0.27

            self.step += 0.3

        else:
            # reset step, x_pos, move y axis up/down screen
            # I'm not sure if resetting the step is necessary
            # it seems step can reach to infinite without messing too much
            # with the wave
            # still need more research on trigonometry

            black = (0, 0, 0)
            self.screen.fill(black)
            #weavy step:
            #self.step = 0.027

            self.step = 0.3
            self.x_pos = 0
            self.y_pos = self.start_y_pos


    def draw_point(self, second=False, third=False):
        """ Activate movement, draw circle accordingly """
        # set a color and radius, change as desired
        radius = 15
        # setting the color this way allows me to change it in the movement method
        color = (self.red, self.green, self.blue)
        # I like to dull the colors a smidge so it doesn't scream at my eyeballs
        # full white is (255, 255, 255)

        white = (230, 230, 230)
        # I'm sure there's a better way to do the part below
        # trying to make sure with each call, it only grabs one of the methods

        # Or None if they try to draw more then one with a call to it.
        if second == False and third == False:
            pg.draw.circle(self.screen, color, (self.x_pos, self.y_pos), radius, 0)
            self.move_point()
        if second == True and third == False:
            radius = 10
            pg.draw.circle(self.screen, color, (self.x_pos, self.y_pos), radius, 0)
            self.move_point2()
        if third == True and second == False:
            radius = 10
            # fill screen to watch circle only
            #black = (0, 0, 0)
            #self.screen.fill(black)

            pg.draw.circle(self.screen, white, (self.x_pos, self.y_pos), radius, 0)
            self.move_with_time()


#######  SinWaveTime  --- weaving pattern #########
####### cleared out comments, I only changed a little bit##

 class SinWaveTime(object):
    """ Separate SinWave container for time alterations
        this one stays center screen, so the other class interferes
        with translation when you try to set it to center on each call"""

    def __init__(self, screen, globals):
        self.screen = screen
        self.globals = globals
        self.x_pos = 0
        self.max_x = self.globals.WIDTH
        self.start_y_pos = self.globals.HEIGHT//2 # <--- changed
        self.y_pos = self.globals.HEIGHT//2 #<---- changed
        self.max_y = self.globals.HEIGHT
        self.center = self.globals.HEIGHT // 2
        self.step = 0.027    # nice sharp 0.2 with 0.03 step
        self.blue = 228
        self.red = 10
        self.green = 10
        self.move = 11

    def move_with_time(self):
        """
        A couple of the original pieces I found online said to
        use time to change the x coordinate.
        That'll be this one, with just a white circle.
        It'll be set up to not alter where y draws the circle
        except for the sin wave
        I got some really fun results tinkering with this
         """

        # variables set up to show the cool weaving effect
        amplitude = 100
        frequency = 4
        time = (pg.time.get_ticks()//1000)
        time = time % 4
        if time == 0:
            time += 1
        # sin formula:
        swf = int(-1 * math.sin(self.step * frequency) * amplitude)
        # to see the weave better set time = 3, comment out to go back to time
        time = 3
        # set the changes
        if self.x_pos < self.max_x:
            self.x_pos = self.x_pos + time
            self.y_pos = self.y_pos + swf
            # weavy step:
            self.step += 0.27
            #self.step += 0.15

        else:
            # reset step, x_pos, move y axis up/down screen
            # remove self.screen.fill(black) to let other methods from SinWave() 
            # draw also
            black = (0, 0, 0)
            self.screen.fill(black)
            #weavy step:
            self.step = 0.027
            #self.step = 0.03
            self.x_pos = 0
            self.y_pos = self.start_y_pos

    def draw_point(self):
        """ Activate movement, draw circle accordingly """
        # set a color and radius, change as desired
        radius = 10
        # setting the color this way allows me to change it in the movement method
        # not currently in use, but future use
        color = (self.red, self.green, self.blue)
        white = (230, 230, 230)
        # unlike original, we're only allowing one circle per method call
        # fill screen to watch circle only
        #black = (0, 0, 0)
        #self.screen.fill(black)
        pg.draw.circle(self.screen, white, (self.x_pos, self.y_pos), radius, 0)
        self.move_with_time()






class PyDrawObjects(object):
    """Container for running the movement and drawing onto screen.
       Remove comments to see each movement method"""

    def __init__(self, screen, globals):
        self.screen = screen
        self.globals = globals
        self.point = SinWave(self.screen, self.globals)
        #self.point2 = SinWave(self.screen, self.globals)
        #self.point3 = SinWave(self.screen, self.globals)

        self.point4 = SinWaveTime(self.screen, self.globals)

    def run_objects(self):
        """
        self.point4 when it reaches edge of window, x maximum will
        refill background so wave is redrawn on new surface
        """
        self.point4.draw_point()
        #self.point3.draw_point(third=True)
        #self.point2.draw_point(second=True)

        #self.point.draw_point()




def start_pygame():
    """ start all pygame dependants.  Return screen, timer"""
    pg.init()  # Not sure the proper place to put this
    globals = SkullGlobals()
    screen = pg.display.set_mode((globals.WIDTH, globals.HEIGHT))
    pg.display.set_caption(('Pygame Display'))
    animation_timer = pg.time.Clock()
    pg.display.flip()

    return screen, animation_timer



def pygame_loop():
    """
        The loop that will run drawing and responding to events
    """

    #  pg.init()  could go here instead of in the start_pygame()
    globals = SkullGlobals()
    w = globals.WIDTH
    h = globals.HEIGHT
    screen, timer = start_pygame()
    black = (0, 0, 0)
    screen.fill(black)

    back_drop = PyDrawObjects(screen, globals)

    running = True
    test_color = (0, 255, 255)
    test_location = (200, 200)
    test_radius = 20

    while running:
        #screen.fill(globals.background_color)
        # I comment the screen fill out, and do it above in the PyDrawObjects class 
        timer.tick(60)  ##  WITHOUT TIMER  < MY CPU runs as fast as it can >!!!!
        events = pg.event.get()
        for event in events:
            if event.type == pg.QUIT:
                running = False
                pg.QUIT
                exit(0)


        back_drop.run_objects()
        pg.display.update()


pygame_loop()





Comments

Popular posts from this blog

Statistics, Python, Making a Stem Plot

Pandas Python and a little probability

JavaScript Ascii animation with while loops and console.log