Posts: 75
Threads: 20
Joined: Dec 2020
Aug-25-2021, 03:55 PM
(This post was last modified: Aug-25-2021, 03:57 PM by CompleteNewb.)
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
Posts: 6,794
Threads: 20
Joined: Feb 2020
Aug-25-2021, 04:34 PM
(This post was last modified: Aug-25-2021, 04:34 PM by deanhystad.)
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.
Posts: 75
Threads: 20
Joined: Dec 2020
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
Posts: 6,794
Threads: 20
Joined: Feb 2020
Aug-25-2021, 06:44 PM
(This post was last modified: Aug-25-2021, 06:44 PM by deanhystad.)
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.
Posts: 75
Threads: 20
Joined: Dec 2020
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
Posts: 6,794
Threads: 20
Joined: Feb 2020
Aug-25-2021, 07:32 PM
(This post was last modified: Aug-25-2021, 07:32 PM by deanhystad.)
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?
Posts: 6,794
Threads: 20
Joined: Feb 2020
Aug-27-2021, 09:39 AM
(This post was last modified: Aug-27-2021, 09:40 AM by deanhystad.)
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.
Posts: 6,794
Threads: 20
Joined: Feb 2020
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()
Posts: 544
Threads: 15
Joined: Oct 2016
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.
Posts: 75
Threads: 20
Joined: Dec 2020
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
|