Search code examples
pythonpython-3.xpygamepygame-surfacepygame-tick

How to rotate Element around pivot point in pygame?


I am trying to rotate an element (in my case font) in pygame around a pivot point my code is as follows:

    import pygame, time

    pygame.init()

    display_width = 800
    display_height = 800

    window = pygame.display.set_mode((display_width, display_height))
    pygame.display.set_caption("Dials")

    black = (0, 0, 0)
    white = (255, 255, 255)
    red = (255, 0, 0)
    green = (0, 255, 0)
    blue = (0, 0, 255)


clock = pygame.time.Clock()

font = pygame.font.SysFont('Arial', 50, True)

#All Images of Dials

tutorial = [pygame.image.load('Dial_Images/tutorial_base.png')]

tutorialInputs = [[1,4,3,4], [1,4,3,2], [1,4,3,2], [1,4,3,4]]

class Dial:
    def __init__(self, x, y, radius, inputs):
        self.x = x
        self.y = y
        self.radius = radius
        self.inputs = inputs
        self.columns = len(self.inputs)
        self.rows = len(self.inputs[0])

    def drawDial(self):
        for i in range(0, self.columns):
            pygame.draw.circle(window, red, (self.x, self.y), self.radius)
            pygame.draw.circle(window, black, (self.x, self.y), self.radius, 1)

        if self.rows == 4:
            input_1 = font.render(str(self.inputs[0][0]), 1, black)
            input_2 = font.render(str(self.inputs[0][1]), 1, black)
            input_3 = font.render(str(self.inputs[0][2]), 1, black)
            input_4 = font.render(str(self.inputs[0][3]), 1, black)

            window.blit(input_1, (self.x - 4, self.y - self.radius + 10))
            window.blit(input_2, (self.x + self.radius - 35, self.y - 15))
            window.blit(input_3, (self.x - 4, self.y + self.radius - 40))
            window.blit(input_4, (self.x - self.radius + 20, self.y - 15))




def level_1():
   window.fill(white)
   dial1.drawDial()


#all Dials Instantiated

dial1 = Dial(int(display_width/2), int(display_height/2), int((display_width/2) - 100), tutorialInputs)

score = 0

run = True
level1 = True

while run:
    keys = pygame.key.get_pressed()

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
    pygame.display.update()
    clock.tick(100)

    if level1:
        level_1()



pygame.quit()   

The following will create a window and a circle with a black border and 4 numbers at 12 o'clock, 3 o'clock, 6 o'clock and 9 o'clock.

I want to rotate the inputs around the center of the circle. Any pygame function that will allow me to rotate input_1 - input _4 90 degrees around the center of the circle? I saw on pygame some functions like pygame.math.vector and some other .rotate function, but I wanted the best approach.

Also, if there is a way to clean up the way I code the location of the inputs so that they align at 12 o'clock, 3 o'clock, 6 o'clock and 9 o'clock that would be helpful.


Solution

  • Rencered text in only image/surface so you can use pygame's functions to rotate image in place (around of center of image)

    image = pygame.transform.rotate(image, -angle)
    

    and later move it to border of circle using radius and angle starting at center of circle

    angle = 0 # 90, 180, 270
    x = dial_center_x
    y = dial_center_y
    x += radius * sin(radians(angle))
    y += radius * cos(radians(angle))
    

    It is good to use pygame.Rect() to keep position and size - it has attributes like .x, .y but also .center, .centerx, .centery so it is easy to center number on dial circle

    image_rect = image.get_rect()
    
    image_rect.center = dial_rect.center
    

    and then you can use radius and angle to move it to position of 12 o'clock, 3 o'clock, 6 o'clock and 9 o'clock or even 1 o'clock, 2 o'clock, etc

    angle = 0 # 90, 180, 270
    image_rect.centerx += radius * sin(radians(angle))
    image_rect.centery += radius * cos(radians(angle))
    

    enter image description here


    import pygame
    import time
    import math
    
    # --- constants --- (UPPER_CASE_NAMES)
    
    BLACK = (0, 0, 0)
    WHITE = (255, 255, 255)
    RED   = (255, 0, 0)
    GREEN = (0, 255, 0)
    BLUE  = (0, 0, 255)
    
    DISPLAY_WIDTH = 800
    DISPLAY_HEIGHT = 800
    
    # --- classes ---
    
    class Dial:
    
        def __init__(self, x, y, radius, inputs, angle_step=45):
            self.rect = pygame.Rect(x-radius, y-radius, 2*radius, 2*radius)
            self.radius = radius
            self.radius2 = radius - 30 # position for digits
            self.inputs = inputs
            self.angle_step = angle_step
    
        def draw(self): # no need to add prefix/postfix 'Dial' to name `draw`
            #for i in range(0, self.columns):
            # `Dial` means single object so it should draw only one circle 
            pygame.draw.circle(window, RED, self.rect.center, self.radius)
            pygame.draw.circle(window, BLACK, self.rect.center, self.radius, 1)
    
            angle = 0
            for number in self.inputs:
                text = font.render(str(number), 1, BLACK)
    
                # rotate image
                text = pygame.transform.rotate(text, -angle)
    
                # center image on circle
                text_rect = text.get_rect(center=self.rect.center)
    
                # move to border using `angle` and `radius`
                angle_rad = math.radians(180-angle)
                text_rect.centerx += self.radius2 * math.sin(angle_rad)
                text_rect.centery += self.radius2 * math.cos(angle_rad)
    
                window.blit(text, text_rect)
    
                angle += self.angle_step
    
    
    # --- functions ---
    
    def level_1():
       window.fill(WHITE)
       dial1.draw()
       dial2.draw()
       dial3.draw()
    
    # --- main ---
    
    pygame.init()
    
    window = pygame.display.set_mode((DISPLAY_WIDTH, DISPLAY_HEIGHT))
    window_rect = window.get_rect()
    
    pygame.display.set_caption("Dials")
    
    clock = pygame.time.Clock()
    
    font = pygame.font.SysFont('Arial', 50, True)
    
    #tutorial = [pygame.image.load('Dial_Images/tutorial_base.png')]
    
    tutorial_inputs = [[1,4,3,4], [1,4,3,2], [1,4,3,2], [1,4,3,4]]
    
    dial1 = Dial(window_rect.centerx, window_rect.centery, window_rect.centerx-250, tutorial_inputs[0], 45)
    dial2 = Dial(window_rect.centerx-250, window_rect.centery-250, window_rect.centerx-250, tutorial_inputs[1], 90)
    dial3 = Dial(window_rect.centerx+250, window_rect.centery-250, window_rect.centerx-250, tutorial_inputs[2], 30)
    
    score = 0
    
    run = True
    level1 = True
    
    while run:
        #keys = pygame.key.get_pressed()
    
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False
    
        pygame.display.update()
        clock.tick(100)
    
        if level1:
            level_1()
    
    pygame.quit()