Beginner. 2d rotation gives unexpected results.


E

enmce

Hello!
This is my first post, nice to meet you all!
I`m biology student from Russia, trying to learn python to perform some

simple simulations.

Here`s my first problem.
I`m trying to perform some simple 2d vector rotations in pygame, in order

to learn the basics of linear algebra and 2d transformations. So far i

understand matrix multiplication pretty well, and probably all my math is

right. Eventually i`m planning to write Poly class, and use it to rotate

and translate some simple shapes. But when i try and write it in the

program, i get very weird results, like all points of rectangle with

coordinates [0,0],[0,100],[100,0],[100,100] start to go spiral and

eventually shrink to the center. Although even Excel calculations with

this formulas give me right result.
I use Python 3.3 on Windows Xp.
What is wrong with my code?

Code:
import pygame
import math as m

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

class Poly():
pos = [100,100] #x and y coordinates of a point
rot = m.radians(1) #rotation in degrees
def draw(self): #draw point
pygame.draw.circle(screen,white,self.pos,10,0)
def rotate(self): # rotation method
sin = m.sin(self.rot) #calculationg sin and cos
cos = m.cos(self.rot)
x_rot = int(self.pos[0]*cos-self.pos[1]*sin) #mulpitplicating

vector to rotation matrix
y_rot = int(self.pos[0]*sin+self.pos[1]*cos)

self.pos[0] = x_rot #set new coordinates to a point
self.pos[1] = y_rot

a = Poly() #Some simple sample points giving rectangle
b = Poly()
c = Poly()
d = Poly()

b.pos = [0,100]
c.pos = [100,0]
d.pos = [0,0]

pygame.init()
size = [700,500]
screen = pygame.display.set_mode(size)
done = False
clock = pygame.time.Clock()
while done == False:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True

a.rotate() #perform rotation
b.rotate()
c.rotate()
d.rotate()

screen.fill(black)

a.draw() #draw point
b.draw()
c.draw()
d.draw()
pygame.display.flip()
clock.tick(30)

pygame.quit()

P.S. Sorry for my english, bit rusty in that department.
 
Ad

Advertisements

P

Peter Otten

This is my first post, nice to meet you all!
Welcome!

I`m biology student from Russia, trying to learn python to perform some

simple simulations.

Here`s my first problem.
I`m trying to perform some simple 2d vector rotations in pygame, in order

to learn the basics of linear algebra and 2d transformations. So far i

understand matrix multiplication pretty well, and probably all my math is

right. Eventually i`m planning to write Poly class, and use it to rotate

and translate some simple shapes. But when i try and write it in the

program, i get very weird results, like all points of rectangle with

coordinates [0,0],[0,100],[100,0],[100,100] start to go spiral and

eventually shrink to the center. Although even Excel calculations with

this formulas give me right result.
I use Python 3.3 on Windows Xp.
What is wrong with my code?
def rotate(self): # rotation method
sin = m.sin(self.rot) #calculationg sin and cos
cos = m.cos(self.rot)
x_rot = int(self.pos[0]*cos-self.pos[1]*sin) #mulpitplicating

The conversion to int introduces a rounding error that accumulates over
time.
vector to rotation matrix
y_rot = int(self.pos[0]*sin+self.pos[1]*cos)

self.pos[0] = x_rot #set new coordinates to a point
self.pos[1] = y_rot

One way to keep the error low is to keep the float values in self.pos and do
the rounding on the fly when you display the point:

class Poly():
def __init__(self, color, pos, rot=m.radians(1)):
self.color = color
self.pos = pos
self.rot = rot
def draw(self):
x, y = self.pos
pygame.draw.circle(screen, self.color, [350+int(x), 250+int(y)], 10,
0)
def rotate(self):
sin = m.sin(self.rot)
cos = m.cos(self.rot)
x_rot = self.pos[0]*cos-self.pos[1]*sin
y_rot = self.pos[0]*sin+self.pos[1]*cos

self.pos = [x_rot, y_rot]

a = Poly(white, [100, 100])
b = Poly(green, [0, 100])
c = Poly(blue, [100, 0])
d = Poly(red, [0, 0])
 
N

Nobody

The conversion to int introduces a rounding error that accumulates over
time.

Most floating point calculations introduce a rounding error. If the
calculations are iterated, the error will accumulate.

In general, you want to avoid accumulating entire transformations. E.g. if
you want a spinning object, maintain the cumulative rotation angle and
rotate the original points each frame.

If you must accumulate transformations, you need to actively work to
maintain any desired invariants. E.g. if a transformation is supposed to
be orthonormal (all axes perpendicular and of unit length), you should
renormalise it periodically, otherwise the lengths and angles will change
over time.
 
J

Joshua Landau

Hello!
This is my first post, nice to meet you all!
I`m biology student from Russia, trying to learn python to perform some

simple simulations.

Here`s my first problem.
I`m trying to perform some simple 2d vector rotations in pygame, in order

to learn the basics of linear algebra and 2d transformations. So far i

understand matrix multiplication pretty well, and probably all my math is

right. Eventually i`m planning to write Poly class, and use it to rotate

and translate some simple shapes. But when i try and write it in the

program, i get very weird results, like all points of rectangle with

coordinates [0,0],[0,100],[100,0],[100,100] start to go spiral and

eventually shrink to the center. Although even Excel calculations with

this formulas give me right result.
I use Python 3.3 on Windows Xp.
What is wrong with my code?

Code:
import pygame
import math as m
[/QUOTE]

GAH!

Why on earth would you do such a thing?
Just "import math", there's no need to obfuscate your code.

[QUOTE]
black = ( 0, 0, 0)
white = ( 255, 255, 255)
green = ( 0, 255, 0)
red = ( 255, 0, 0)
[/QUOTE]

It's probably better to do:

black = pygame.Color("Black")
white = pygame.Color("white")
green = pygame.Color("green")
red   = pygame.Color("red")

[QUOTE]
class Poly():
pos = [100,100] #x and y coordinates of a point[/QUOTE]

rot = m.radians(1) #rotation in degrees
*Do not do this*

This is because classes have shared values -- these "pos" and "rot" values
are shared within the class.
....     n = []
....     def more_n(self):
....         self.n.append(len(self.n))
....
....
....[0, 1, 2]


Normally you want to set these at initialisation:

class Poly():
def __init__(self, pos=None, rot=math.radians(1)):
self.pos = [100, 100] if pos is None else pos
self.rot = rot
super().__init__(self)

[QUOTE]
def draw(self): #draw point
pygame.draw.circle(screen,white,self.pos,10,0)
[/QUOTE]

Add some spaces, dude.

I was going to say:
[QUOTE]
Also, use keyword arguments instead of throwing around "10" and "0" with no context:
def draw(self):
pygame.draw.circle(screen, white, self.pos, radius=10, width=0)
Pygame-ists will know that "width" means border_width, by now. Pygame[/QUOTE]
isn't known for it's clean design ;).

But pygame, being brilliant (not) decided that it won't let you.

def rotate(self): # rotation method[QUOTE]
sin = m.sin(self.rot) #calculationg sin and cos
cos = m.cos(self.rot)
x_rot = int(self.pos[0]*cos-self.pos[1]*sin) #mulpitplicating[/QUOTE]

vector to rotation matrix[QUOTE]
y_rot = int(self.pos[0]*sin+self.pos[1]*cos)

self.pos[0] = x_rot #set new coordinates to a point
self.pos[1] = y_rot
[/QUOTE]


A lot of your comments are ridiculous. This one is particularly so:
#mulpitplicating vector to rotation matrix. Don't add comments that talk
about lines. Here is a quick guide for when to use comments:

1) API usage, when docstrings aren't usable

2) When something funny or unexpected occurs in the code, such as:

# Goes down to 0 (does not include end-point)
for i in range(foo, -1, -1): ...

3) To explain large-scale methodologies and algorithms

Other than this, are you trying to obfuscate this line? HINT: ADD SPACES ;).


A big problem here (this solves your problem) is your int(...) conversions.
Do *not* store important information less accurately than it deserves. This
is one very important instance. Only convert to int when you need to.

Another thing: "sin = m.sin(self.rot)"??? Really? Write "sin_1deg = ..."
instead, at least.

[QUOTE]
a = Poly() #Some simple sample points giving rectangle
b = Poly()
c = Poly()
d = Poly()

b.pos = [0,100]
c.pos = [100,0]
d.pos = [0,0]
[/QUOTE]

Use:

a = Poly()
b = Poly([0, 100])
c = Poly([100, 0])
d = Poly([0, 0])

pygame.init()[QUOTE]
size = [700,500]
screen = pygame.display.set_mode(size)
done = False
clock = pygame.time.Clock()
while done == False:
[/QUOTE]

"while not done"

Also, just use "while True" and a "break". I know some C-people or what not
think this is "evil" or something, but they're wrong.

Personally, I actually like using:

while "rotating the squares":
...

instead of "while True". It's no slower and it's free documentation.

[QUOTE]
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True

a.rotate() #perform rotation
b.rotate()
c.rotate()
d.rotate()

screen.fill(black)

a.draw() #draw point
b.draw()
c.draw()
d.draw()
pygame.display.flip()
clock.tick(30)

pygame.quit()

You don't need pygame.quit(). You *only* want pygame.quit() if you're
quitting Pygame *before* the program is finished.
 
Ad

Advertisements

T

Terry Reedy

import math as m


GAH!

Why on earth would you do such a thing?

for the same reason people do 'import tkinter as tk': to minimize typing
and maximize clarity. In this case,
from math import sin, cos, radians
also works well
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top