I've got close, but there are two problems. One is the final position of the rotated square (no longer in centre). The second is my original vertices list mutates, in spite of my making a copy with list(..).
Any help much appreciated as usual.
from Tkinter import *
import math
WIDTH = 400
HEIGHT = 400
CANVAS_MID_X = WIDTH/2
CANVAS_MID_Y = HEIGHT/2
SIDE = WIDTH/4
root = Tk()
canvas = Canvas(root, bg="black", height=HEIGHT, width=WIDTH)
canvas.pack()
vertices = [
[CANVAS_MID_X - SIDE/2, CANVAS_MID_Y - SIDE/2],
[CANVAS_MID_X + SIDE/2, CANVAS_MID_Y - SIDE/2],
[CANVAS_MID_X + SIDE/2, CANVAS_MID_Y + SIDE/2],
[CANVAS_MID_X - SIDE/2, CANVAS_MID_Y + SIDE/2]]
def rotate(points, angle):
new_points = list(points)
rad = angle * (math.pi/180)
cos_val = math.cos(rad)
sin_val = math.sin(rad)
for coords in new_points:
x_val = coords[0]
y_val = coords[1]
coords[0] = x_val * cos_val - y_val * sin_val
coords[1] = x_val * sin_val + y_val * cos_val
return new_points
def draw_square(points):
canvas.create_polygon(points, fill="red")
def test():
print "vertices: ", vertices, "should be: ", "[[150, 150], [250, 150], [250, 250], [150, 250]]"
new_square = rotate(vertices, 30)
draw_square(new_square)
test()
mainloop()
Your first problem is that the formula you're using rotates around the origin. I assume you want to rotate the square around its center. To do that, you simply translate the square so its center is at the origin, rotate it, then translate it back.
The second problem is that doing list(points)
does create a new outer list, but it doesn't create new lists for the lists inside points
. There are ways to make a deep copy that creates new lists for those internal lists, but you don't really need to do that here. Just build a fresh list from the rotated vertices.
My version of your code draws the original square in blue so that we can see that the rotated square ends up in the correct location.
from Tkinter import *
import math
WIDTH = 400
HEIGHT = 400
CANVAS_MID_X = WIDTH/2
CANVAS_MID_Y = HEIGHT/2
SIDE = WIDTH/4
root = Tk()
canvas = Canvas(root, bg="black", height=HEIGHT, width=WIDTH)
canvas.pack()
vertices = [
[CANVAS_MID_X - SIDE/2, CANVAS_MID_Y - SIDE/2],
[CANVAS_MID_X + SIDE/2, CANVAS_MID_Y - SIDE/2],
[CANVAS_MID_X + SIDE/2, CANVAS_MID_Y + SIDE/2],
[CANVAS_MID_X - SIDE/2, CANVAS_MID_Y + SIDE/2],
]
def rotate(points, angle, center):
angle = math.radians(angle)
cos_val = math.cos(angle)
sin_val = math.sin(angle)
cx, cy = center
new_points = []
for x_old, y_old in points:
x_old -= cx
y_old -= cy
x_new = x_old * cos_val - y_old * sin_val
y_new = x_old * sin_val + y_old * cos_val
new_points.append([x_new + cx, y_new + cy])
return new_points
def draw_square(points, color="red"):
canvas.create_polygon(points, fill=color)
def test():
old_vertices = [[150, 150], [250, 150], [250, 250], [150, 250]]
print "vertices: ", vertices, "should be: ", old_vertices
print vertices == old_vertices
draw_square(vertices, "blue")
center = (CANVAS_MID_X, CANVAS_MID_Y)
new_square = rotate(vertices, 30, center)
test()
draw_square(new_square)
mainloop()
I've also made a few other minor changes to your code.
BTW, you probably should do
from __future__ import division
before your other imports. That tells Python to use true division. Without it, SIDE / 2
does integer division. That works ok here, but it won't be correct if SIDE
is an odd number.
You should try to get out of the habit of doing
from Tkinter import *
Instead, do
import Tkinter as tk
and then call the Tkinter functions like this:
canvas = tk.Canvas(root, bg="black", height=HEIGHT, width=WIDTH)
This is slightly more typing, but it makes the code easier to read and maintain, and prevents the errors that can occur when your code accidentally uses one of the names that Tkinter has defined. FWIW, doing from Tkinter import *
imports over 170 names into your namespace. You don't need that clutter!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With