# Examples of the math.sin() and math.cos() trig functions # Al Sweigart al@inventwithpython.com # You can learn more about Pygame with the # free book "Making Games with Python & Pygame" # # http://inventwithpython.com/pygame # import sys, pygame, math from pygame.locals import * # set up a bunch of constants BLUE = ( 0, 0, 255) WHITE = (255, 255, 255) DARKRED = (128, 0, 0) DARKBLUE = ( 0, 0, 128) RED = (255, 0, 0) GREEN = ( 0, 255, 0) DARKGREEN = ( 0, 128, 0) YELLOW = (255, 255, 0) DARKYELLOW = (128, 128, 0) BLACK = ( 0, 0, 0) BGCOLOR = WHITE WINDOWWIDTH = 640 # width of the program's window, in pixels WINDOWHEIGHT = 480 # height in pixels WIN_CENTERX = int(WINDOWWIDTH / 2) # the midpoint for the width of the window WIN_CENTERY = int(WINDOWHEIGHT / 2) # the midpoint for the height of the window FPS = 160 # frames per second to run at AMPLITUDE = 100 # how many pixels tall the waves with rise/fall. # standard pygame setup code pygame.init() FPSCLOCK = pygame.time.Clock() DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT)) pygame.display.set_caption('Trig Waves') fontObj = pygame.font.Font('freesansbold.ttf', 16) # variables that track visibility modes showSine = True showCosine = True showHighAmpSine = True showHighFreqSine = True pause = False xPos = 0 step = 0 # the current input f posRecord = {'sin': [], 'cos': [], 'hiampsin': [], 'hifreqsin': []} # keeps track of the ball positions for drawing the waves # making text Surface and Rect objects for various labels sinLabelSurf = fontObj.render('sine', True, RED, BGCOLOR) cosLabelSurf = fontObj.render('cosine', True, BLUE, BGCOLOR) highAmpSinLabelSurf = fontObj.render('hi amp sin', True, GREEN, BGCOLOR) highFreqSinLabelSurf = fontObj.render('hi freq sin', True, YELLOW, BGCOLOR) sinLabelRect = sinLabelSurf.get_rect() cosLabelRect = cosLabelSurf.get_rect() highAmpSinLabelRect = highAmpSinLabelSurf.get_rect() highFreqSinLabelRect = highFreqSinLabelSurf.get_rect() instructionsSurf = fontObj.render('Press Q, W, E, R to toggle waves. P to pause.', True, BLACK, BGCOLOR) instructionsRect = instructionsSurf.get_rect() instructionsRect.left = 10 instructionsRect.bottom = WINDOWHEIGHT - 10 # main application loop while True: # event handling loop for quit events for event in pygame.event.get(): if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE): pygame.quit() sys.exit() # check for key presses that toggle pausing and wave visibility if event.type == KEYUP: if event.key == K_q: showSine = not showSine elif event.key == K_w: showCosine = not showCosine elif event.key == K_e: showHighAmpSine = not showHighAmpSine elif event.key == K_r: showHighFreqSine = not showHighFreqSine elif event.key == K_p: pause = not pause # fill the screen to draw from a blank state DISPLAYSURF.fill(BGCOLOR) # draw instructions DISPLAYSURF.blit(instructionsSurf, instructionsRect) # draw the horizontal middle line and the amplitude lines pygame.draw.line(DISPLAYSURF, BLACK, (0, WIN_CENTERY), (WINDOWWIDTH, WIN_CENTERY)) pygame.draw.line(DISPLAYSURF, BLACK, (0, WIN_CENTERY + AMPLITUDE), (WINDOWWIDTH, WIN_CENTERY + AMPLITUDE)) pygame.draw.line(DISPLAYSURF, BLACK, (0, WIN_CENTERY - AMPLITUDE), (WINDOWWIDTH, WIN_CENTERY - AMPLITUDE)) # sine wave yPos = -1 * math.sin(step) * AMPLITUDE posRecord['sin'].append((int(xPos), int(yPos) + WIN_CENTERY)) if showSine: # draw the sine ball and label pygame.draw.circle(DISPLAYSURF, RED, (int(xPos), int(yPos) + WIN_CENTERY), 10) sinLabelRect.center = (int(xPos), int(yPos) + WIN_CENTERY + 20) DISPLAYSURF.blit(sinLabelSurf, sinLabelRect) # cosine wave yPos = -1 * math.cos(step) * AMPLITUDE posRecord['cos'].append((int(xPos), int(yPos) + WIN_CENTERY)) if showCosine: # draw the cosine ball and label pygame.draw.circle(DISPLAYSURF, BLUE, (int(xPos), int(yPos) + WIN_CENTERY), 10) cosLabelRect.center = (int(xPos), int(yPos) + WIN_CENTERY + 20) DISPLAYSURF.blit(cosLabelSurf, cosLabelRect) # high amplitude sine wave yPos = -1 * math.sin(step) * (AMPLITUDE + 100) # Note the "+ 100" to the amplitude! posRecord['hiampsin'].append((int(xPos), int(yPos) + WIN_CENTERY)) if showHighAmpSine: # draw the high amplitude sine ball and label pygame.draw.circle(DISPLAYSURF, GREEN, (int(xPos), int(yPos) + WIN_CENTERY), 10) highAmpSinLabelRect.center = (int(xPos), int(yPos) + WIN_CENTERY + 20) DISPLAYSURF.blit(highAmpSinLabelSurf, highAmpSinLabelRect) # high frequency sine wave yPos = -1 * math.sin(step * 4) * AMPLITUDE # Note the "* 4" to the frequency posRecord['hifreqsin'].append((int(xPos), int(yPos) + WIN_CENTERY)) if showHighFreqSine: # draw the high frequency sine ball and label pygame.draw.circle(DISPLAYSURF, YELLOW, (int(xPos), int(yPos) + WIN_CENTERY), 10) highFreqSinLabelRect.center = (int(xPos), int(yPos) + WIN_CENTERY + 20) DISPLAYSURF.blit(highFreqSinLabelSurf, highFreqSinLabelRect) # draw the waves from the previously recorded ball positions if showSine: for x, y in posRecord['sin']: pygame.draw.circle(DISPLAYSURF, DARKRED, (x, y), 4) if showCosine: for x, y in posRecord['cos']: pygame.draw.circle(DISPLAYSURF, DARKBLUE, (x, y), 4) if showHighAmpSine: for x, y in posRecord['hiampsin']: pygame.draw.circle(DISPLAYSURF, DARKGREEN, (x, y), 4) if showHighFreqSine: for x, y in posRecord['hifreqsin']: pygame.draw.circle(DISPLAYSURF, DARKYELLOW, (x, y), 4) # draw the border pygame.draw.rect(DISPLAYSURF, BLACK, (0, 0, WINDOWWIDTH, WINDOWHEIGHT), 1) pygame.display.update() FPSCLOCK.tick(FPS) if not pause: xPos += 0.5 if xPos > WINDOWWIDTH: xPos = 0 posRecord = {'sin': [], 'cos': [], 'hiampsin': [], 'hifreqsin': []} step = 0 else: step += 0.008 step %= 2 * math.pi