solution_285.py

#!/usr/bin/python3
# ==============================================================
# draw a Theodorus spiral (a square root spiral) 
# ==============================================================

import graphics as gr
import coordinate_conversion as cc
import math
import sys

DEBUG = False

ORIGIN_LINES = False

# --------------------------------------------------------------
# ---- convert polar to Cartesian coordinate systems 
# ----
# ----  ## Cartesian to polar
# ----  def cart2polar(x,y):
# ----      rho = np.sqrt(x**2 + y**2)
# ----      phi = np.arctan2(y,x)
# ----      return (rho,phi)
# --------------------------------------------------------------

def polar2cart(rho:[int|float],phi:[int|float]) -> tuple:
    '''
    polar to cartesian
    '''
    x = rho * math.cos(phi)
    y = rho * math.sin(phi)
    return (x,y)

# --------------------------------------------------------------
# ---- create a graphics window
# --------------------------------------------------------------

def create_a_graphics_window(width:[int|float],
                             height:[int|float],
                             axes:bool=False,
                             title:str='Graph') -> gr.GraphWin:
    '''
    create graphics window and maybe Cartesian axes
    '''

    # ---- create window
    
    win = gr.GraphWin(title,width,height)
    win.setBackground("white")

    # ---- draw axes?

    if axes:
             
        # ---- X,Y axes origin in window coordinates

        xaxis = round(width/2.0)
        yaxis = round(height/2.0)

        # ---- draw X axis (line)

        xl = gr.Line(gr.Point(0,yaxis),gr.Point(width-1,yaxis))
        xl.setWidth(1)
        xl.setFill("black")
        xl.draw(win)

        # ---- draw Y axis (line)

        yl = gr.Line(gr.Point(xaxis,0),gr.Point(xaxis,height-1))
        yl.setWidth(1)
        yl.setFill("black")
        yl.draw(win)

    return win

# --------------------------------------------------------------
# ---- create a list of points on a square root spiral
# ---- return polar coordinates
# ----
# ---- format (tuple):
# ----   [0] radial distance
# ----   [1] triangle polar angle (degrees)
# ----   [2] triangle polar angle (radians)
# ----   [3] triangle hypotenuse using a and b
# ----       (next triangle's b value)
# --------------------------------------------------------------
# ----          angle x
# ----
# ----              +          sin(x) = a/h
# ----             /|          cos(x) = b/h
# ----            / |
# ----         h /  | a        h = math.sqrt(a**2 + b**2)
# ----          /   |
# ----         /    |          x = atan(a/b)  x = atan2(x,y) 
# ----        / x   |          x = asin(a/h)
# ----       +------+          x = acos(b/h)
# ----           b
# ----
# --------------------------------------------------------------

def create_square_root_spiral_points(loops:int=17) -> list:
    '''
    create a list of points on a square root spiral
    '''

    pts = []    # list of spiral point

    # ---- calculate each of the sprial points

    for loop in range(0,loops):

        # ---- loop == 0 is the initial spiral point
        # ----           on the x axes

        if loop == 0:

            a   = 0.0
            b   = 1.0            
            hyp = 1.0            
            rad = 0.0
            deg = 0.0
            dst = 1.0

        else:
            
            a   = 1.0
            b   = math.sqrt(loop)

            hyp = math.sqrt(a**2 + b**2)

            rad = math.asin(a/hyp)
            deg = math.degrees(rad)
            dst = math.sqrt(loop + 1)

        if DEBUG:
            print()
            print(f'loop {loop}')
            print(f'a  ={a:.6f}  b={b:.6f}  hyp={hyp:.6f}')
            print(f'dst={dst:.6f} deg={deg:.6f} rad={rad:.6f}')

        pts.append((dst,rad,deg,hyp))

    return pts

# --------------------------------------------------------------
# ---- draw a spiral (this code is incomplete. finish it.)
# --------------------------------------------------------------

def draw_spiral(win:gr.GraphWin,pts:list) -> None:

    accmulated_angle = 0.0

    previous_point = None

    for pt in pts:
        
        accmulated_angle += pt[1]

        x,y = polar2cart(pt[0],accmulated_angle)

        x = x*75               # scale the coordinate
        y = y*75               # scale the coordinate

        # ---- convert point cartesian coords to window coords

        wx1,wy1 = cc.center_to_win_coords(x,y,
                                    win.width,win.height)

        # ---- draw a line (from point to point)

        if previous_point is not None:
            
            l = gr.Line(gr.Point(wx1,wy1),previous_point)
            l.setWidth(1)
            l.setFill('black')
            l.draw(win)
    
        # ---- draw a line (from point to origin)

        if ORIGIN_LINES:

            wx2,wy2 = cc.center_to_win_coords(0.0,0.0,
                      win.width,win.height)
            
            l = gr.Line(gr.Point(wx1,wy1),gr.Point(wx2,wy2))
            l.setWidth(1)
            l.setFill('black')
            l.draw(win)

        # ---- draw a point (circle)
        
        c = gr.Circle(gr.Point(wx1,wy1),4)
        c.setFill('red')
        c.draw(win)

        # ---- save the current point

        previous_point = gr.Point(wx1,wy1)

    return

# --------------------------------------------------------------
# ---- main
# --------------------------------------------------------------

if __name__ == '__main__':

    # ---- square root spirial plot/drawing points

    pts = create_square_root_spiral_points()

    # ---- graphics window

    win = create_a_graphics_window(801,801,axes=False,
            title="Theodorus (Square Root) Spiral")

    # ---- draw a Theodorus (square root) spiral

    draw_spiral(win,pts)

    # ---- wait for a mouse click in window, then exit

    click = win.getMouse()
    win.close()
    sys.exit()