0

I am creating an application which rotates a few points around a center point. The aim is to connect each point using eg. lines/arcs and have the points/(subsequent drawing) rotate around the center point.

I am trying to accomplish this by a method which rotates each point by a given amount each time the method is called, then distributing each point n times around the center point using a for loop.

(For future use I will also need some tkinter widgets running along side the code eg. entries to get user-input.)

My current code simply draws a circle for each point, instead of connecting them. There are a couple of things I dont currently understand:

  1. My code runs fine for a short while, then closes with Error: maximum recursion depth exceeded. - Is it bad to clear canvas by .delete ?

  2. The value of the .after function doesn't seem to have any effect at all hence using time.sleep.

(I have also used a while True: loop to run the code in an earlier version, but I read that it is bad practice to run an infinite loop inside the GUI event loop. And I edited it because of flickering)

Would it be better to structure my code differently? Sorry for any mis-terminology and the messy and long post/code, I am a new non-english python student.

class Create_gear:

    def __init__(self, location, ox, oy, rpm, n):
        self.location = location
        self.ox = ox
        self.oy = oy
        self.rpm = rpm
        self.n = n
        self.rotation_from_normal = 0

    #Rotates point px1, py1 by value of "rpm" each time method is called.
    def draw_rotating_gear(self, px1, py1, px2, py2, r):
        self.rotation_from_normal = self.rotation_from_normal +self.rpm
        self.location.delete("all")

        #rotates point px1, py1 n times around to form a circle. 
        for i in range (0, self.n):
            angle = (math.radians(self.rotation_from_normal + 360/self.n *i) )

            qx = ( self.ox + math.cos(angle) * (px1 - self.ox) - math.sin(angle) * (py1 - self.oy) )
            qy = ( self.oy + math.sin(angle) * (px1 - self.ox) + math.cos(angle) * (py1 - self.oy) )

            x0 = qx - r
            y0 = qy - r
            x1 = qx + r
            y1 = qy + r
            self.location.create_oval(x0, y0, x1, y1, fill = "black")
        self.location.update()
        time.sleep(0.01)
        self.location.after(1000000000, self.draw_rotating_gear(480, 200, 500, 300, 5))
cdlane
  • 40,441
  • 5
  • 32
  • 81
Floxxy
  • 7
  • 3
  • In general, you probably shouldn't use recursive functions in Python because of the exact situation you've run into. See https://stackoverflow.com/questions/4278327/danger-of-recursive-functions – Triggernometry May 08 '19 at 21:31
  • 1
    I wouldn't say that at all--there's nothing wrong with recursion when you need it, for example, to traverse a recursive data structure. But the code above will fail in every language--Python has nothing to do with it. – Lee Daniel Crocker May 08 '19 at 21:34
  • The cause of the problem is this statement: `self.location.after(1000000000, self.draw_rotating_gear(480, 200, 500, 300, 5))`. Change it to `self.location.after(1000000000, self.draw_rotating_gear, 480, 200, 500, 300, 5)` will fix it. However, calling the function with fixed parameters every time seems a design issue and how come the delay time `1000000000` is so large. – acw1668 May 09 '19 at 00:54

2 Answers2

1

Nothing in the description of your problem indicates the need for recursion at all, and the way you implement it in your code will always fail. You have a call to draw_rotating_gear() at the end of the function draw_rotating_gear() with no conditional for stopping the recursion, so it will go infinitely deep on the first call. Reorganize it to use a simple loop.

Lee Daniel Crocker
  • 12,927
  • 3
  • 29
  • 55
1

You didn't provide enough code for a working example solution but I believe the problem is you're invoking the .after() method with incorrect arguments. The first argument needs to be an integer, the number of milliseconds before the call, not a floating point value. The second argument needs to be a function to call after that delay, not a call to a function which is what you did. Fixing these, and simplifying your example slightly, I'd expect something like:

def draw_rotating_gear(self, px1, py1, r):
    self.rotation_from_normal = self.rotation_from_normal + self.rpm
    self.location.delete("all")

    # rotates point px1, py1 n times around to form a circle.
    for i in range(0, self.n):
        angle = (math.radians(self.rotation_from_normal + 360/self.n * i))

        qx = (self.ox + math.cos(angle) * (px1 - self.ox) - math.sin(angle) * (py1 - self.oy))
        qy = (self.oy + math.sin(angle) * (px1 - self.ox) + math.cos(angle) * (py1 - self.oy))

        x0 = qx - r
        y0 = qy - r
        x1 = qx + r
        y1 = qy + r
        self.location.create_oval(x0, y0, x1, y1, fill="black")

    self.location.update()
    self.location.after(100, lambda px1=qx, py1=qy, r=r: self.draw_rotating_gear(px1, py1, r))

(I may be passing the wrong variables to the lambda call as I don't have enough code context to work with.) The recursion error you got was due to your incorrect second argument to .after(), i.e. a false recursion due to a programming error.

cdlane
  • 40,441
  • 5
  • 32
  • 81