'Animate algorithm

I want to visualize an algorithm (Graham scan) in python with tkinter. I want to animate the algorithm and I am stuck.

I basically want to draw and delete lines but I don't understand canvas.after() well enough to make it work.

draw_line() returns the line object but when I call it in canvas.after(..., draw_line, ...) I don't see a way to get the return value or how to call another canvas.after() to change the color/delete that line if the function draw_line() hasn't been called yet because of the delay.

Thanks in advance.

from tkinter import *
import math
import random


class Point:
    def __init__(self, _x, _y, _a=0):
        self.x = _x
        self.y = _y
        self.angle = _a

    def get_co(self):
        return self.x, self.y


def draw_hull(hull):
    for i in range(len(hull) - 1):
        canvas.create_line(hull[i][0], hull[i][1], hull[i + 1][0], hull[i + 1][1], fill="red", width=2)


def draw_line(p1, p2, color="yellow"):
    return canvas.create_line(p1.x, p1.y, p2.x, p2.y, fill=color, width=2)


def convex_hull(list_points):

    # find bottom point
    bottom_point = Point(math.inf, math.inf)
    for point in list_points:
        if point[1] < bottom_point.y:
            bottom_point = Point(point[0], point[1])

    # calculate angles between the bottom point and the other points
    points = []
    for point in list_points:
        if point != bottom_point.get_co():
            new_point = Point(point[0], point[1])
            angle = calculate_angle(bottom_point, new_point)
            new_point.angle = angle
            points.append(new_point)

    # sort the points by angle
    swaps = None
    while swaps != 0:
        swaps = 0
        for i in range(len(points) - 1):
            point1 = points[i]
            point2 = points[i + 1]
            if point1.angle > point2.angle:
                points[i], points[i + 1] = points[i + 1], points[i]
                swaps += 1

    # go through the points and add them to the convex hull
    # if the angle between 3 points ever exeeds 180 degrees, discard the middle point
    hull = [bottom_point, points[0]]

    i = 1
    while i < len(points):
        ####### DRAW LINE #######
        canvas.after(i*500, draw_line, hull[-2], hull[-1])
        ##############

        # check angle
        angle = calculate_angle(hull[-2], hull[-1], points[i])

        if angle == -1:
            ########## DELETE LINE ##########
            # change color of line to red and delete it a bit later
            # canvas.itemconfig(line, fill="red")
            # canvas.after(i*500+250, canvas.delete, line)
            ####################
            # pop the point of the stack
            hull.pop()
        else:
            ########## CHANGE COLOR OF LINE ##########
            # change color of line to green
            # canvas.itemconfig(line, fill="green")
            ####################
            # move to the next point
            hull.append(points[i])
            i += 1

    # add bottom point again for loop
    hull.append(bottom_point)

    # give easy return list (with coordinate-tupels not class objects)
    output = []
    for point in hull:
        output.append(point.get_co())
    return output


def calculate_angle(point1, point2, point3=None):
    if point3 is None:
        if point2.x - point1.x == 0:
            return 90
        elif point2.x - point1.x > 0:
            return math.degrees(math.atan((point2.y - point1.y)/(point2.x - point1.x)))
        else:
            return 180 - math.degrees(math.atan((point2.y - point1.y)/(point1.x - point2.x)))
    else:
        v1 = Point(point1.x - point2.x, point1.y - point2.y)
        v2 = Point(point3.x - point2.x, point3.y - point2.y)
        det = (v1.x * v2.y) - (v2.x * v1.y)

        if det < 0:
            return 1
        else:
            return -1


window = Tk()

window.geometry("1000x600")
canvas = Canvas(window, width=1000, height=600)
canvas.pack()


POINTSIZE = 2
points = []
for i in range(100):
    x = random.randint(50, 950)
    y = random.randint(50, 550)

    points.append((x, y))
    canvas.create_oval(x - POINTSIZE, y - POINTSIZE, x + POINTSIZE, y + POINTSIZE, fill="black")


hull = convex_hull(points)

# draw_hull(hull)


window.mainloop()



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source