1

I'm trying to make a visual representation of the first million digits of pi inside a circle. Like in the coding train channel, but with Python. (https://www.youtube.com/watch?v=WEd_UIKG-uc&index=137&list=PLRqwX-V7Uu6ZiZxtDDRCi6uhfTH4FilpH)

It should be looking like this:

but it looks like this:

First off all I imported math and tkinter class, I read the document and make the canvas. I think there's no problem in here:

from  math import *
from tkinter import *
openpi = open (r'PI_numbers.txt', 'r')
pi = openpi.read()
win= Tk()
win = Canvas(win, width=500, height=500)
win.configure(background=('black'))
win.create_oval(0,0,500,500, fill='white')

Next, as I didn't know how to make a switch case, I searched and found something.

  def zero(sx,sy):
     angle=36*0
     x= 250* (1 + cos(angle))
     y= 250* (1 + sin(angle))
     win.create_line(sx,sy, x, y)
     sx=x
     sy=y
  def one(sx,sy):
     angle=36*1
     x = 250 * (1 + cos(angle))
     y = 250 * (1 + sin(angle))
     win.create_line(sx, sy, x, y)
     sx = x
     sy = y
  def two(sx,sy):
     angle=36*2
     x = 250 * (1 + cos(angle))
     y = 250 * (1 + sin(angle))
     win.create_line(sx, sy, x, y)
     sx = x
     sy = y 

And like this to nine. And then:

y=250
x=250
options = {
            '0': zero(sx=x, sy= y),
            '1': one(sx=x, sy= y),
            '2': two(sx=x, sy= y),
            '3': three(sx=x, sy= y),
            '4': four(sx=x, sy= y),
            '5': five(sx=x, sy= y),
            '6': six(sx=x, sy= y),
            '7': seven(sx=x, sy= y),
            '8': eight(sx=x, sy= y),
            '9': nine(sx=x, sy= y),
            ".": point()
 }
i=0
while len(pi)> i:
    n = pi[i]
    options [n]
    i += 1

win.pack()
win.mainloop()

So, in here I'm trying to make a line from the last number to the new one. I start at 250 250, the center of the circumference. The problem I detect is not just the x and y are wrong but the fact that it's starting always from the center and i don't know why.

And in line of options [n] I get this : statement seems to have no effect.

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
  • 1
    Hmm :) At a glance, your solution to Python lacking a `switch` is causing the problem. `sx` and `sy` are function parameters and so they are not changed. – Jongware Jul 14 '18 at 17:53
  • 2
    That isn't the correct substitution for a `switch` block. You could define the function with the angle as an argument, but defining a new function for each case is not a good way to do it. Also, the values of `sx` and `sy` are not being updated. – sam-pyt Jul 14 '18 at 17:56
  • @jonrsharpe: isn't the fact that Martin *came from* C and Java somewhat relevant here? I know the switchlessness of Python has been a major headache for me, until I learned to avoid it. – Jongware Jul 14 '18 at 17:56
  • 1
    BTW, there's absolutely no need for 10 separate functions. They're all identical except for the angle multiplier, which could be passed in as a parameter. – PM 2Ring Jul 14 '18 at 18:02
  • It would be helpful if you explain what you want your program to do. Stack Overflow questions need to be self-contained. Links can be used for supporting information, but the question must make sense without the links. And you can't expect people to watch a 16 minute YouTube video just so they can understand your question. – PM 2Ring Jul 14 '18 at 18:42

2 Answers2

1

I propose working with module turtle because it's a lot easier for drawing things

I strongly agree with this sentiment by @Superior but I personally would avoid the math library and let the turtle (which runs atop tkinter) do all the work instead:

from turtle import Turtle, Screen

LARGE_RADIUS = 200
SMALL_RADIUS = 15

def make_pi():

    """ from https://stackoverflow.com/a/9005163/5771269 """

    q, r, t, k, m, x = 1, 0, 1, 1, 3, 3

    while True:
        if 4 * q + r - t < m * t:
            yield m
            q, r, m = 10 * q, 10 * (r - m * t), (10 * (3 * q + r)) // t - 10 * m
        else:
            q, r, m, t, k, x = k * q, x * (r + 2 * q), (q * (7 * k + 2) + r * x) // (t * x), t * x, k + 1, x + 2

screen = Screen()
screen.mode('logo')  # put zero straight up

turtle = Turtle('turtle', visible=False)
turtle.speed('fastest')
turtle.penup()

turtle.sety(LARGE_RADIUS)
turtle.right(90)

positions = []

for index in range(10):  # pre calculate all positions on circle
    positions.append(turtle.position())
    turtle.circle(-LARGE_RADIUS, extent=360 / 10)

turtle.home()
turtle.pendown()
turtle.showturtle()
turtle.speed('slow')

previous_digit = -1  # track digit repetitions

for digit in make_pi():

    if digit == previous_digit:  # same digit again, circle around
        turtle.setheading(turtle.towards(0, 0))
        turtle.left(90)
        turtle.circle(-SMALL_RADIUS)
    else:
        position = positions[digit]
        turtle.setheading(turtle.towards(position))
        turtle.goto(position)

        previous_digit = digit

screen.mainloop()  # never reached

Instead of using a fixed size PI_numbers.txt file, I've set this up to run forever using a digits of pi algorithm posted on StackOverflow by @batbaatar. Replace as you see fit.

I've modified the drawing slightly, showing when a digit follows itself by circling back around to the same digit:

enter image description here

Note how long it takes before we encounter our first double 7. I've put turtle into Logo mode to naturally put zero at the top of the screen. You can adjust the speed of the animation at the second turtle.speed() call.

This is not a proper turtle program as it is built on a while True: and should instead use screen.ontimer() to extract each digit so other events, like screen.exitonclick(), can execute.

cdlane
  • 40,441
  • 5
  • 32
  • 81
  • Sometimes, I don't get SO. "You forgot to close the bracket" - 26 upvotes. Marvelous idea plus complete code - tumbleweed. I for one enjoyed watching the turtle dance. – Mr. T Jul 15 '18 at 08:17
0

well here are problems

1)
when creating dict options, by putting parentheses and parameters you already call functions (that should be done later) and in dict in position '0' (and every other) is saved the result of that function (in this case None, because every function without a return value, by default returns None)
by options[n] you just get None from options and do nothing

solution:

options = {'0': zero,
           '1': one,...}
for n in pi: ## much more elegant solution than while loop
    options[n](x, y) ## notice only here I want him to go to that place 
    ## also there is no need for explicitly saying `sx = x` because `sx` and `sy` are positional arguments

2)
method Canvas.create_line creates a line from point to point.
since your arguments sx and sy are always 250, it draws line starting at (250, 250) (in this case, middle of a circle)

3)
sin and cos functions of math take an angle in radians, not degrees

final solution

PI = openpi.read() # caps important because we need pi from module math

...

def go(n, ix, iy): # instead of functions zero, one ... and "switch" case
    angle = n / 5 * pi # cos and sin functions calclate with radians not degrees
    x, y = 250 * (1 + cos(angle)), 250 * (1 + sin(angle))

    win.create_line(ix, iy, x, y) # from initial x & y to our new x & y
    return x, y # this will become new initial x & y in next call

x = y = 250
for n in PI: # much more elegant solution than while loop
    x, y = go(int(n), x, y) 
    # function returns new position and now this will be initial position

and I propose working with module turtle because it's a lot easier for drawing things

Superior
  • 787
  • 3
  • 17