Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Rotating a rectangle
#11
Well, that's the point. I don't want to rotate the image of a sail. I want to rotate a rectangle. I want to do what you did in tkinter, but in pygame and I do want to be able to manipulate it with control keys in the future, but right now I just want to make that white rectangle rotate like the hands of a clock. And i'm getting real close with my code, but they're not rotating the way i want them to. So if you can help me understand my code, it would be greatly appreciated

import random, pygame, math, sys
from pygame.locals import *
 
Blue = (0,0,255)
Black = (0, 0, 0) 
Green = (0,255,0)
White = (255,255,255)
  
pygame.init()
DISPLAYSURF = pygame.display.set_mode((400, 300))
pygame.display.set_caption('Sailing!')
  
FPS = 30
fpsClock = pygame.time.Clock()
  
Sail = pygame.Surface([100,10])
Sail.set_colorkey (Black)
Sail.fill(White)
  
degrees = 0
hyp = 100
x = 200
y = 150

while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
   
    adj = 10 * math.cos(degrees)
    opp = 10 * math.sin(degrees)
    dx = adj + 200
    dy = opp + 150
    
    rotatedSail = pygame.transform.rotate(Sail, degrees)
    Sail_rect = Sail.get_rect(topleft = (dx, dy))
    DISPLAYSURF.fill(Blue)
    DISPLAYSURF.blit(rotatedSail, Sail_rect)
    pygame.display.flip()
  
    fpsClock.tick(FPS)
  
    degrees += 1
 
Reply
#12
I disagree. You have no interest at all in rectangles. I would go so far as to say there is no place for rectangles in your program. The only purpose of the rectangle was to represent a sail and there are probably better ways to do that.

I would make an image file of a mast and realistic looking sail. The image has to be square and the mast in the center. I would make another image file of the topview of the boat, It too would be square and drawn so that the mast location is in the center of the image. I would also stop using topleft and start using center when positioning my surfaces. Now I can move my boat surface around and rotate it (is rotating about the mast ok?) and I can rotate my sail surface independently, and it is easy to keep the boat and sail synchronized by setting the center of their bounding rectangles to the same coordinates.

I might even make two sail images with the wind billowing the sail in different directions. I would choose which image to use based on the wind angle relative to the sail angle.
Reply
#13
Yeah, you're right. It would be so much simpler. There's even a video on youtube that explains exactly how to do it. I could have done that in the very beginning, but that's not what I want to learn
Reply
#14
I am confused. I write a response that shows how to do the math and you ask for 4 lines of pygame code. I provide a solution that easily accomplishes what you want using 4 lines of pygame code and you want to do math. Make up your mind please.

Here is one last tkinter example showing how a rotating image can do what you originally requested. I already wrote it so I'm not throwing it away.
from PIL import Image, ImageDraw, ImageTk
import tkinter as tk

angle = 0
image = None
tkimage = None
sail = None

def rotate_sail(delta):
    global angle, tkimage, sail
    angle += delta
    # Create rotated image from original image
    tkimage = ImageTk.PhotoImage(image.rotate(angle))
    if sail:
        canvas.delete(sail)
    sail = canvas.create_image(75, 75, anchor=tk.CENTER, image=tkimage)
    canvas.after(10, rotate_sail, delta)

# Make an image of sail and mast
image = Image.new(mode='RGBA', size=(100, 100), color=0)
draw = ImageDraw.Draw(image)
draw.ellipse((45, 45, 55, 55), fill='black')
draw.line((50, 50, 100, 50), width=5, fill='white')

root = tk.Tk()
canvas = tk.Canvas(root, width=150, height=150, bg='blue')
canvas.pack()
canvas.create_rectangle(55, 50, 95, 100, fill='brown')
rotate_sail(-1)

root.mainloop()
PIL likes angles in degrees. Surprised it wasn't something else like revolutions. Your last example is still using degrees with the math library. You need to use radians. You should also use center when you position the image. It is much easier than trying to position the corner. Remember that rotating the image/surface changes the size, and this changes the location of the corner relative to the center of the image.
Reply
#15
Man, look at my name. the examples you're giving me are to complex for me.
And I try to define my question as clearly as possible, but you don't even take what I say into account and you don't even have pygame install so you don't even know what my code is doing.
I mean thanks for the effort, but it's not helping
Reply
#16
At least use this part:

Quote:Your last example is still using degrees with the math library. You need to use radians. You should also use center when you position the image. It is much easier than trying to position the corner. Remember that rotating the image/surface changes the size, and this changes the location of the corner relative to the center of the image.

When you rotate your image you have to use degrees for the angle. When figuring out where to draw the image you need to use radians for the angle of math.cos(angle) and math.sin(angle). 1 radian = 180 / PI or approximately 57.29 degrees

Your last example still shows you using the top left corner of the surface.
Sail_rect = Sail.get_rect(topleft = (dx, dy))
This makes it really complicated to compute dx and dy. Instead you should use the center of the image.
Sail_rect = Sail.get_rect(center= (dx, dy))
And compute dx and dy to be the where the center of the sail/rectangle should be.

Does noob mean you don't know trigonometry? No insult, just asking. How about linear algebra? Matrix math?
Reply
#17
This works. I finally installed pygame and found an error with pygame rotating surface the opposite direction that the surface was rotated about the origin.
import pygame, math
from pygame.locals import *
    
BLUE = (0, 0, 255)
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
degrees = 0
FPS = 30
 
def move_surface(surface, center=(0, 0)):
    '''Move surface to be centered at center(x, y)'''
    bounds = surface.get_rect(center = center)
    DISPLAYSURF.blit(surface, bounds)
 
def rotate_surface(surface, degrees, origin=(0, 0), offset=0):
    '''Rotate surface about an origin.
    surface - The thing to rotate
    degrees - How much to rotate surface in degrees
    origin - Center of rotation
    offset - Distance from center of rotation to center of surface
    '''
    x, y = origin  # Where are we rotating around
 
    # Calculate center of rotated surface
    if offset != 0:
        radians = degrees * math.pi / 180  # Need angle in radians for sin() and cos()
        x += int(offset * math.cos(radians))
        y -= int(offset * math.sin(radians))   # This was +=
 
    # Rotate the surface
    if degrees != 0:
        surface = pygame.transform.rotate(surface, degrees)  # Pygame uses degrees
 
    move_surface(surface, (x, y))
 
pygame.init()
DISPLAYSURF = pygame.display.set_mode((400, 300))
pygame.display.set_caption('Sailing!')
fpsClock = pygame.time.Clock()
Sail = pygame.Surface([100,10])
Sail.set_colorkey(BLACK)
Sail.fill(WHITE)
 
running = True
while running:
    for event in pygame.event.get():
        if event.type == QUIT:
            running = False
            break
      
    DISPLAYSURF.fill(BLUE)
    rotate_surface(Sail, degrees, (200, 150), 50)
    pygame.display.flip()
    fpsClock.tick(FPS)
    degrees += 1
 
pygame.quit()
Another way to fix this is:
# Change surface rotation so + is clockwise
x += int(offset * math.cos(radians))
y += int(offset * math.sin(radians))
surface = pygame.transform.rotate(surface, -degrees)
Now the rectangle rotates clockwise for positive degrees.
Reply
#18
A pygame program that tumbles a cube. Rotations about x, y and z.
import pygame, math
from pygame.locals import *
     
class Point():
    '''A point in space'''
    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

    def projection(self):
        '''Return projection on xy plane.  Not doing perspective'''
        return self.x, self.y

    def move(self, x=0, y=0, z=0):
        '''Move self in x, y and z'''
        self.x += x
        self.y += y
        self.z += z

    def rotate_x(self, degrees, center=(0, 0, 0)):
        '''Rotate self about x axis
        degrees is angle of rotation measured in degrees
        center is the center of rotation
        '''
        cx, cy, cz = center
        y = self.y - cy
        z = self.z - cz
        d = math.hypot(y, z)
        theta  = math.atan2(y, z) + degrees * math.pi/180
        self.z = cz + d * math.cos(theta)
        self.y = cy + d * math.sin(theta)

    def rotate_y(self, degrees, center=(0, 0, 0)):
        '''Rotate self about y axis
        degrees is angle of rotation measured in degrees
        center is the center of rotation
        '''
        cx, cy, cz = center
        x = self.x - cx
        z = self.z - cz
        d = math.hypot(x, z)
        theta  = math.atan2(x, z) + degrees * math.pi/180
        self.z = cz + d * math.cos(theta)
        self.x = cx + d * math.sin(theta)

    def rotate_z(self, degrees, center=(0, 0, 0)):
        '''Rotate self about z axis
        degrees is angle of rotation measured in degrees
        center is the center of rotation
        '''
        cx, cy, cz = center
        x = self.x - cx
        y = self.y - cy
        d = math.hypot(y, x)
        theta  = math.atan2(y, x) + degrees * math.pi/180
        self.x = cx + d * math.cos(theta)
        self.y = cy + d * math.sin(theta)

    def __str__(self):
        return f'({self.x}, {self.y}, {self.z})'
 
class Cube():
    '''A 3D cube'''
    def __init__(self, size, center=(0, 0, 0)):
        x, y, z = center
        a = size/2
        self.points = [
            Point(x-a, y-a, z+a),
            Point(x+a, y-a, z+a),
            Point(x+a, y+a, z+a),
            Point(x-a, y+a, z+a),
            Point(x-a, y-a, z-a),
            Point(x+a, y-a, z-a),
            Point(x+a, y+a, z-a),
            Point(x-a, y+a, z-a),
        ]

    def center(self):
        '''Calculate center of cube'''
        x = y = z = 0
        for point in self.points:
            x += point.x
            y += point.y
            z += point.z
        return x/8, y/8, z/8

    def move(self, x=0, y=0, z=0):
        '''Move cube in x, y and z'''
        for point in self.points:
            point.move(x, y, z)

    def rotate_x(self, degrees, center=None):
        '''Rotate self about x axis
        degrees is angle of rotation measured in degrees
        center is the center of rotation.  If center not
        provided rotate about self.center.
        '''
        if center is None:
            center = self.center()
        for point in self.points:
            point.rotate_x(degrees, center)

    def rotate_y(self, degrees, center=None):
        '''Rotate self about y axis
        degrees is angle of rotation measured in degrees
        center is the center of rotation.  If center not
        provided rotate about self.center.
        '''
        if center is None:
            center = self.center()
        for point in self.points:
            point.rotate_y(degrees, center)

    def rotate_z(self, degrees, center=None):
        '''Rotate self about z axis
        degrees is angle of rotation measured in degrees
        center is the center of rotation.  If center not
        provided rotate about self.center.
        '''
        if center is None:
            center = self.center()
        for point in self.points:
            point.rotate_z(degrees, center)

    def draw(self, surface, color=(255, 255, 255)):
        '''Draw self on surface'''
        for a, b in zip([0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3], [1, 2, 3, 0, 5, 6, 7, 4, 4, 5, 6, 7]):
            pygame.draw.line(surface, color, self.points[a].projection(), self.points[b].projection())

    def __str__(self):
        return f"{{{', '.join([str(point) for point in self.points])}}}"

FPS = 30
pygame.init()
DISPLAYSURF = pygame.display.set_mode((200, 200))
pygame.display.set_caption('Cube')
fpsClock = pygame.time.Clock()
cube = Cube(60, (160, 100, 0))
running = True
while running:
    for event in pygame.event.get():
        if event.type == QUIT:
            running = False
            break
       
    DISPLAYSURF.fill((0, 0, 0))
    cube.rotate_x(1)
    cube.rotate_y(1)
    cube.rotate_z(1, (100, 100, 0))
    cube.draw(DISPLAYSURF)
    pygame.display.flip()
    fpsClock.tick(FPS)
  
pygame.quit()
Reply
#19
Learn pygame Vector2 and Vector3 formulas. They will handle the math.
Example vector3 cube.
import pygame

class Cube:
    def __init__(self, center, color):
        self.color = color
        self.center = pygame.Vector3(center)
        self.points = [
            pygame.Vector3(-1, -1, 1),
            pygame.Vector3(1, -1, 1),
            pygame.Vector3(1, 1, 1),
            pygame.Vector3(-1, 1, 1),
            pygame.Vector3(-1, -1, -1),
            pygame.Vector3(1, -1, -1),
            pygame.Vector3(1, 1, -1),
            pygame.Vector3(-1, 1, -1),
        ]

        self.lines = [(0, 1), (1, 2), (2, 3), (3, 0), # Front
                      (4, 5), (5, 6), (6, 7), (7, 4), # Back
                      (0, 4), (3, 7), # Left
                      (1, 5), (2, 6)  # Right
        ]

        self.scale = 100

    def draw(self, surface):
        for i, j in self.lines:
            a = self.points[i] * self.scale + self.center
            a = pygame.Vector2(a.x, a.y)
            b = self.points[j] * self.scale + self.center
            b = pygame.Vector2(b.x, b.y)
            pygame.draw.line(surface, self.color, a, b)

    def move(self, x, y, z):
        self.center += (x, y, z)

    def rotate(self, angle, x, y, z):
        vector = pygame.Vector3(x, y, z)
        for point in self.points:
            point.rotate_ip(angle, vector)

def main():
    pygame.display.set_caption("Rotating Cube")
    surface = pygame.display.set_mode((800, 600))
    clock = pygame.time.Clock()
    rect = surface.get_rect()
    running = True
    delta = 0
    fps = 60

    center = rect.centerx, rect.centery, 0
    cube = Cube(center, pygame.Color("dodgerblue"))
    velocity = 50

    while running:
        for event in pygame.event.get():
            if event.type == pygame.MOUSEMOTION:
                x, y = event.pos
                cube.center = pygame.Vector3(x, y, 0)
            elif event.type == pygame.QUIT:
                running = False

        surface.fill("black")
        cube.draw(surface)
        cube.rotate(velocity * delta, 1, 1, 1)
        pygame.display.flip()
        delta = clock.tick(fps) * 0.001

if __name__ == "__main__":
    pygame.init()
    main()
    pygame.quit()
99 percent of computer problems exists between chair and keyboard.
Reply
#20
That's it. that's perfect. The code is simple enough that i can understand what's going and it does exactly what i want. My rectangles were flying all over the place. But that cube is not really my level. Impressive though. Maybe someday if keep at it
Thanks you very much for the effort
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [PyGame] Surface and rectangle in pygame Fabrizio_fg 6 2,305 May-27-2023, 09:15 AM
Last Post: Fabrizio_fg
  Make rectangle snap to other rectangle corners Pedroski55 2 4,364 Aug-13-2021, 09:59 PM
Last Post: Pedroski55
  [PyGame] Rotating image issue Evoluxman 8 6,151 Oct-12-2019, 12:54 PM
Last Post: Evoluxman
  [PyGame] Rectangle keeps teleporting? keyfive 1 3,202 Jun-27-2018, 11:49 PM
Last Post: sonnekrieger7

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020