Posts: 75
Threads: 20
Joined: Dec 2020
Hello, i'm having trouble rotating a rectangle on screen. I'm not a computer science student and I need help because I have been at it for a couple of days.
The goal is to rotate a rectangle around it's top left corner like a sail around a post, but now the white rectangle rotates around it's center in a black square...
Here's my code:
import random, pygame, sys
from pygame.locals import *
Blue = (0,0,255)
Green = (0,255,0)
White = (255,255,255)
pygame.init()
DISPLAYSURF = pygame.display.set_mode((400, 300))
DISPLAYSURF.fill(Blue)
pygame.display.set_caption('Sailing!')
FPS = 30
fpsClock = pygame.time.Clock()
Sail = pygame.Surface([100,10])
Sail.fill(White)
degrees = 0
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
rotatedSail = pygame.transform.rotozoom(Sail, degrees, 1)
Sail_rect = Sail.get_rect(topleft =(200,150))
DISPLAYSURF.blit(rotatedSail, Sail_rect)
pygame.display.flip()
fpsClock.tick(FPS)
degrees += 1 I played around with the code using transform.rotate, draw.rect and I watched youtube videos, but i can't use rotate with draw.rect, i make the image fly off the screen or I just have different version of this problem and I don't want to load an image I want to create a rectangle.
Any help is welcomed
thank you
Posts: 6,222
Threads: 16
Joined: Feb 2020
I think the pygame rotations are rotations about the Z axis (axis pointing out of the screen). Sounds like you want to rotate about the Y axis instead. This would make appear like the rectangle is coming out of the screen. Like this?
import math
import tkinter as tk
class Point():
'''A point in space'''
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
def rotate_y(self, angle, center=(0, 0, 0)):
'''Rotate self around center.y. Angle is in radians'''
cx, cy, cz = center
x = self.x - cx
z = self.z - cz
d = math.hypot(x, z)
theta = math.atan2(x, z) + angle
self.z = cz + d * math.cos(theta)
self.x = cx + d * math.sin(theta)
def __str__(self):
return f'({self.x}, {self.y}, {self.z})'
class Rectangle():
def __init__(self, width, height):
self.points = [
Point(-width/2, height/2, 0),
Point(width/2, height/2, 0),
Point(width/2, -height/2, 0),
Point(-width/2, -height/2, 0),
]
def rotate_y(self, angle, center=(0, 0, 0)):
'''Rotate self around center.y. Angle is in radians'''
for point in self.points:
point.rotate_y(angle, center)
def draw(self, canvas, center=(0, 0)):
'''Draw self on canvas'''
cx, cy = center
for i in range(4):
a = rectangle.points[i]
b = rectangle.points[(i+1)%4]
canvas.create_line(a.x+cx, a.y+cy, b.x+cx, b.y+cy)
def __str__(self):
return f'{self.points[0]}, {self.points[1]}, {self.points[2]}, {self.points[3]}'
rectangle = []
def rotate_y(rectangle, angle, center=(0, 0, 0)):
canvas.delete("all")
rectangle.rotate_y(angle, center)
rectangle.draw(canvas, (140, 100))
canvas.after(10, lambda: rotate_y(rectangle, angle, center))
root = tk.Tk()
canvas = tk.Canvas(root, width=200, height=200)
canvas.pack()
rectangle = Rectangle(80, 80)
rotate_y(rectangle, 0.02, (-40, 0, 0))
root.mainloop() For a more realistic view the rectangle should be drawn in perspective instead of drawing the projection.
Posts: 75
Threads: 20
Joined: Dec 2020
Thank you for the answer, but I am trying to make it rotate on the z axis. A point in the center of the screen, like a ship seen from above. I think the white surface is rotating around it from it's top left corner, but the get_rect() function is all black and doesn't allow it to continue it's rotation around the screen so the white surface is stuck rotating in a black square in the lower portion of the screen.
All i want is to make a white rectangle acting like the radii of circle which has it's origin in the middle of a blue screen.
Posts: 6,222
Threads: 16
Joined: Feb 2020
Like this?
import math
import tkinter as tk
class Point():
'''A point in space'''
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
def move(self, x=0, y=0, z=0):
self.x += x
self.y += y
self.z += z
def rotate_z(self, angle, center=(0, 0, 0)):
cx, cy, cz = center
x = self.x - cx
y = self.y - cy
d = math.hypot(y, x)
theta = math.atan2(y, x) + angle
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 Rectangle():
def __init__(self, width, height):
self.points = [
Point(-width/2, height/2, 0),
Point(width/2, height/2, 0),
Point(width/2, -height/2, 0),
Point(-width/2, -height/2, 0),
]
def move(self, x=0, y=0, z=0):
for point in self.points:
point.move(x, y, z)
def rotate_z(self, angle, center=(0, 0, 0)):
for point in self.points:
point.rotate_z(angle, center)
def draw(self, canvas, center=(0, 0)):
'''Draw self on canvas'''
cx, cy = center
for i in range(4):
a = rectangle.points[i]
b = rectangle.points[(i+1)%4]
canvas.create_line(a.x+cx, a.y+cy, b.x+cx, b.y+cy)
def __str__(self):
return f'{self.points[0]}, {self.points[1]}, {self.points[2]}, {self.points[3]}'
rectangle = []
def rotate_z(rectangle, angle, center=(0, 0, 0)):
canvas.delete("all")
rectangle.rotate_z(angle, center)
rectangle.draw(canvas, (100, 100))
canvas.after(10, lambda: rotate_z(rectangle, angle, center))
root = tk.Tk()
canvas = tk.Canvas(root, width=200, height=200)
canvas.pack()
rectangle = Rectangle(80, 10)
rectangle.move(40, 0)
rotate_z(rectangle, 0.02)
root.mainloop() Using the pygame transorm functions you need to rotate and translate (move).
Posts: 75
Threads: 20
Joined: Dec 2020
yes exactly, but... there has to be a simpler way, like 4 lines of code using only pygame
Posts: 6,222
Threads: 16
Joined: Feb 2020
Aug-24-2021, 08:22 PM
(This post was last modified: Aug-24-2021, 08:47 PM by deanhystad.)
You need to do a rotate and a move. The rotate you probably already have (I am not going to install pygame to verify). The move is not that bad. You could steal the code from Point.rotate_z.
def rotate_z(self, angle, center=(0, 0, 0)):
cx, cy, cz = center
x = self.x - cx
y = self.y - cy
d = math.hypot(y, x)
theta = math.atan2(y, x) + angle
self.x = cx + d * math.cos(theta)
self.y = cy + d * math.sin(theta) The self.x and self.y would be the center of the rectangle/image/surface/sail. The cx and cy the point about which you want the sail to rotate. It shouldn't be difficult to strip this code out of the class and make it a function.
As for the "black square", that is the background of the image/surface/sail. You need to make the background transparent
Posts: 371
Threads: 2
Joined: Jan 2021
Is something like this going to work for you?
import random, pygame, 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
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
rotatedSail = pygame.transform.rotate(Sail, degrees)
Sail_rect = Sail.get_rect(topleft =(200,150))
DISPLAYSURF.fill(Blue)
DISPLAYSURF.blit(rotatedSail, Sail_rect)
pygame.display.flip()
fpsClock.tick(FPS)
degrees += 1
Posts: 75
Threads: 20
Joined: Dec 2020
Aug-24-2021, 11:30 PM
(This post was last modified: Aug-24-2021, 11:30 PM by CompleteNewb.)
okay, okay, okay,
first of all thank you for your time.
The blue fill is great and the post about making it move got me thinking and it kind of blew my mind so i tried writing it myself, but it didn't work...
So here's what i have now. In my head it should be working. You augment the degrees and the math function should do the trick???
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 = 100 * math.cos(degrees)
opp = 100 * math.sin(degrees)
dx = adj + 200
dy = opp + 150
sadj = math.pow(adj, 2)
sopp = math.pow(opp, 2)
shyp = math.pow(hyp,2)
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
Posts: 544
Threads: 15
Joined: Oct 2016
Example
import pygame
def main():
# Basic Pygame Setup
pygame.display.set_caption("Rotating Sailing Ship")
surface = pygame.display.set_mode((400, 300))
clock = pygame.time.Clock()
rect = surface.get_rect()
running = True
delta = 0
fps = 30
# Ship
ship = pygame.sprite.Sprite()
# Keep orignal image. For it doesn't get distorted.
# pygame.SRCALPHA for transparency
ship.orignal_image = pygame.Surface((100, 10), pygame.SRCALPHA)
ship.image = ship.orignal_image
ship.image.fill("white")
ship.rect = ship.image.get_rect(center=rect.center)
# Hold are floats
ship.position = pygame.Vector2(ship.rect.center)
# Hold are heading
ship.vector = pygame.Vector2(1, 0)
ship.rotate_velocity = 60
ship.velocity = 80
# Main Loop
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Move and Rotate
rotate = 0
key_state = pygame.key.get_pressed()
if key_state[pygame.K_UP] or key_state[pygame.K_w]:
ship.position += ship.vector * ship.velocity * delta
ship.rect.center = ship.position
if key_state[pygame.K_LEFT] or key_state[pygame.K_a]:
rotate = -ship.rotate_velocity * delta
if key_state[pygame.K_RIGHT] or key_state[pygame.K_d]:
rotate = ship.rotate_velocity * delta
if rotate != 0:
ship.vector.rotate_ip(rotate)
angle = ship.vector.as_polar()[1]
ship.image = pygame.transform.rotate(ship.orignal_image, -angle)
# Keep ship in center
ship.rect = ship.image.get_rect(center=ship.rect.center)
# Draw
surface.fill("blue")
surface.blit(ship.image, ship.rect)
# Render To Screen
pygame.display.flip()
# Delta for smooth movement
delta = clock.tick(fps) * 0.001
if __name__ == "__main__":
pygame.init()
main()
pygame.quit()
99 percent of computer problems exists between chair and keyboard.
Posts: 6,222
Threads: 16
Joined: Feb 2020
Aug-25-2021, 03:47 PM
(This post was last modified: Aug-25-2021, 03:47 PM by deanhystad.)
I spent too much effort solving the wrong problem. The problem is not rotating a rectangle, the problem is rotating an image of a sail. When you first tried this you had a white rectangle rotating about it's center inside a black rectangle.
To solve the problem of the white rectangle rotating about it's center, the easy solution is make the image 4 times larger and place the sail (rectangle) so the mast is at the center of the image. That way when you rotate the image the sail will rotate about the mast. There is no need to calculate how a rectangle should move as it rotates about a point if there is no rectangle.
The second problem is the black rectangle. The solution here is to make the background of the image transparent so the only thing blitted to the screen is the sail part. Here's a tutorial about that:
https://riptutorial.com/pygame/example/2...ansparency
Frustratingly pygame measures angles in degrees and python.math measures them in radians. That would be one reason why your results were off. 90 degrees is PI/2 radians (1.57)
|