Using self.canvas.coords(polygon, position) in tkinter causes the polygon to dissapear

26 Views Asked by At

Whenever I start the game by running the game file, if I remove the lines of code that cause the ship to actually move, the ship disappears. Currently I am just trying to make a ship move on a canvas.

Here is the Game class


import tkinter as tk 
import math as math 
from ship import Ship 

class Game:
    def __init__(self, gamewidth, gameheight):
        self.root = tk.Tk()
        self.gamewidth = gamewidth
        self.gameheight = gameheight
        self.canvas = tk.Canvas(height = self.gameheight, width = self.gamewidth, bg = "black")
        #make canvas
        self.objects()
        self.binds()
        self.canvas.pack()
        self.root.mainloop()
    def objects(self):
        self.player1 = Ship(self.canvas)
    def binds(self): 
        self.root.bind('<a>', self.player1.rotate(1))
        

gun=Game(700, 500)

And here is the Ship class

import math as math
import tkinter as tk 

class Ship: 
    def __init__(self, canvas): 
        self.pos = [(5, 5), (5, 25), (30, 15)]
        self.canvas = canvas
        self.ship = self.canvas.create_polygon(self.pos, outline ="white", fill="white")
    
    def rotate(self, theta): #rotate about origin
        theta = (theta*math.pi)/180
        for i in range(len(self.pos)):
            self.pos[i] = (self.centroid()[0] - self.pos[i][0], self.pos[i][1] - self.centroid()[1])
        for i in range(len(self.pos)):
            self.pos[i]=(math.sin(theta)*self.pos[i][1] - math.cos(theta)*self.pos[i][0], 
                        math.cos(theta)*self.pos[i][0] - math.sin(theta)*self.pos[i][1])
        for i in range(len(self.pos)):
            self.pos[i] = (self.centroid()[0] + self.pos[i][0], self.pos[i][1] + self.centroid()[1])
        self.canvas.coords(self.ship, self.pos[0][0], self.pos[0][1], self.pos[1][0], self.pos[1][1], self.pos[2][0],self.pos[2][1])

        
    def centroid(self): #function to find center of polygon
        temp1 = 0
        temp2 = 0 
        inc = 0
        for i in range(len(self.pos)):      
            temp2 += self.pos[i][1]
            inc += 1 
        return((temp1/inc, temp2/inc))

I have tested just removing the line of code that contains "canvas.coords" which does allow the ship to be rendered, however I need that line of code to allow the ship to move. I have tried making a function inside of the Ship class to refresh the coordinates but that does not seem to work. Any help is welcome, including tips for how to make my questions more clear in the future.

1

There are 1 best solutions below

0
acw1668 On

It looks like the methods to calculate the centroid and rotating points are wrong.

Using the following answers:

You can rotate the ship correctly.

import math
import tkinter as tk

def find_centroid(vertices):
    """
    https://stackoverflow.com/a/75699257/5317403
    """
    x, y = 0, 0
    n = len(vertices)
    signed_area = 0
    for i in range(len(vertices)):
        x0, y0 = vertices[i]
        x1, y1 = vertices[(i + 1) % n]
        # shoelace formula
        area = (x0 * y1) - (x1 * y0)
        signed_area += area
        x += (x0 + x1) * area
        y += (y0 + y1) * area
    signed_area *= 0.5
    x /= 6 * signed_area
    y /= 6 * signed_area
    return x, y

def rotate_point(origin, point, angle):
    """
    https://stackoverflow.com/a/34374437/5317403

    Rotate a point counterclockwise by a given angle around a given origin.

    The angle should be given in radians.
    """
    ox, oy = origin
    px, py = point

    qx = ox + math.cos(angle) * (px - ox) - math.sin(angle) * (py - oy)
    qy = oy + math.sin(angle) * (px - ox) + math.cos(angle) * (py - oy)
    return qx, qy


class Ship:
    def __init__(self, canvas):
        self.pos = [(5, 5), (5, 25), (30, 15)]
        self.canvas = canvas
        self.ship = self.canvas.create_polygon(self.pos, outline ="white", fill="white")

    def rotate(self, theta): #rotate about origin
        theta = math.radians(theta)
        centroid = find_centroid(self.pos)
        for i, pt in enumerate(self.pos):
            self.pos[i] = rotate_point(centroid, pt, -theta) # since canvas y-axis is reversed
        self.canvas.coords(self.ship, self.pos)


class Game:
    def __init__(self, gamewidth, gameheight):
        self.root = tk.Tk()
        self.gamewidth = gamewidth
        self.gameheight = gameheight
        self.canvas = tk.Canvas(height = self.gameheight, width = self.gamewidth, bg = "black")
        #make canvas
        self.objects()
        self.binds()
        self.canvas.pack()
        self.root.mainloop()
    def objects(self):
        self.player1 = Ship(self.canvas)
    def binds(self):
        self.root.bind('<a>', lambda e: self.player1.rotate(5)) # used lambda here


gun = Game(700, 500)