solution_045b.py

#!/usr/bin/python3
# ===================================================================
# How High is the Mountain - Draw Diagram From User Input
# ===================================================================

import sys
import draw_axes as ax
import coordinate_conversion as cc
import user_interface as ui
import graphics as gr
from math import radians, sin, cos

# ---- input ranges

flight_altitude_min  = 10000
flight_altitude_max  = 80000
measure_distance_min = 1000
measure_distance_max = 20000 
angle_alpha_min      = 10
angle_alpha_max      = 80
angle_beta_min       = 10
angle_beta_max       = 80

# ---- graphics window 

wwidth  = 801                  # (pixels) window width
wheight = 801                  # (pixels) window height
maxxy   = 300                  # (pixels) diagram max x,y pixels 
                               # max distance from  the origin 0,0
# ---- explanation

explanation = f'''
This program will calculate and then draw a diagram that show the
height of a mountain based on the user's input. Ridiculous input
data will result in ridiculous output.
      {flight_altitude_min:>6} < flight altitude  < {flight_altitude_max:<6}
      {measure_distance_min:>6} < measure distance < {measure_distance_max:<6}
      {angle_alpha_min:>6} <   angle alpha    < {angle_alpha_max:<6}
      {angle_beta_min:>6} <   angle beta     < {angle_beta_max:<6}
'''

# -------------------------------------------------------------------
# ---- prompt for user input
# -------------------------------------------------------------------

def user_input(min,max,prompt):

    while(True):

        print()
        s = ui.get_user_input(prompt)

        if not s:
            return(False,0)

        x,n = ui.is_float(s)

        if not x:
            print()
            print('unknown value enter - try again')
            continue

        if n < min:
            print()
            print(f'value must be > {min} - try again')
            continue

        if n > max:
            print()  
            print('value must < {max} - try again')
            continue

        return(True,n)

# -------------------------------------------------------------------
# ---- get all user input
# -------------------------------------------------------------------

def get_all_user_input(explanation = None):

    ui.clear_screen()

    if explanation:
        print(explanation)

    # ---- flight altitude

    x,n = user_input(flight_altitude_min,
                     flight_altitude_max,
                     'Enter flight altitude (ft): ')

    if not x:
        return(False,0,0,0,0)

    flight_altitude = n

    # ---- measure distance
    
    flight_altitude = n

    # ---- measure distance

    x,n = user_input(measure_distance_min,
                     measure_distance_max,
                     'Enter measure distance (ft): ')

    if not x:
        return(False,0,0,0,0)

    measure_distance = n

    # ---- angle alpha

    x,n = user_input(angle_alpha_min,
                     angle_alpha_max,
                     'Enter angle alpha (deg): ')

    if not x:
        return(False,0,0,0,0)

    angle_alpha = n

    # ---- angle beta

    x,n = user_input(angle_beta_min,
                     angle_beta_max,
                     'Enter angle beta (deg): ')

    if not x:
        return(False,0,0,0,0)

    angle_beta = n

    # ---- return user input

    return(True,flight_altitude,measure_distance,
           angle_alpha,angle_beta)

    
# -------------------------------------------------------------------
# ---- calculation beta angle x and y
# -------------------------------------------------------------------

def calculation(flight_altitude,
                measure_distance,
                angle_alpha,
                angle_beta):

    angle_gamma = 180 - angle_alpha - angle_beta

    alpha_rad = radians(angle_alpha)
    beta_rad  = radians(angle_beta)
    gamma_rad = radians(angle_gamma)

    gamma_hypotenuse = (sin(alpha_rad) * measure_distance) / sin(gamma_rad)

    beta_x = sin(beta_rad) * gamma_hypotenuse
    beta_y = cos(beta_rad) * gamma_hypotenuse

    return(beta_x, beta_y)

# -------------------------------------------------------------------
# ---- draw diagram
# -------------------------------------------------------------------

def draw_diagram(flight_altitude, measure_distance, beta_x, beta_y):

    # ---------------------------------------------------------------
    # ---- draw a point
    # ---------------------------------------------------------------

    def draw_point(win,x,y,raidus=5,color='black'):
        wx,wy = cc.center_to_win_coords(x,y,wwidth,wheight)
        c = gr.Circle(gr.Point(wx,wy),raidus)
        c.setFill(color)
        c.draw(win)

    # ---------------------------------------------------------------
    # ---- draw a line
    # ---------------------------------------------------------------

    def draw_line(win,x0,y0,x1,y1,width=3,color='black'):
        wx0,wy0 = cc.center_to_win_coords(x0,y0,wwidth,wheight)
        wx1,wy1 = cc.center_to_win_coords(x1,y1,wwidth,wheight)
        c = gr.Line(gr.Point(wx0,wy0),gr.Point(wx1,wy1))
        c.setWidth(width)
        c.setFill(color)
        c.draw(win)

    # ---------------------------------------------------------------
    # ---- add text to window
    # ---------------------------------------------------------------

    def add_text(win,x,y,txt):
         wx,wy = cc.center_to_win_coords(x,y,wwidth,wheight)
         t = gr.Text(gr.Point(wx,wy),txt)
         t.setSize(20)
         t.draw(win)

    # ---------------------------------------------------------------
    # ---- scale coordinates to fit the diagram in the window
    # ---- center the diagram so that the beta angle
    # ---- x coordinate is 0 and the y coordinate is 1/2 of beta_y
    # ---------------------------------------------------------------

    def scale_point_coords(maxxy,measure_distance,beta_x,beta_y):

        # ---- calculate coordinates

        x0 = -(measure_distance - beta_x)
        y0 = beta_y / 2.0
        x1 = beta_x
        y1 = beta_y / 2.0
        x2 = 0.0
        y2 = -beta_y / 2.0

        # ---- calculate the scale factor to fit diagram
        # ---- in the graphics window

        max_coord = 0.0
        if abs(x0) > max_coord:
            max_coord = abs(x0)
        if abs(y0) > max_coord:
            max_coord = abs(y0)
        if abs(x1) > max_coord:
            msx_coord = abs(x1)
        if abs(y1) > max_coord:
            max_coord = abs(y1)
        if abs(x2) > max_coord:
            max_coord = abs(x2)
        if abs(y2) > max_coord:
            max_coord = abs(y2)

        scale = maxxy / max_coord

        print(f'scale factor    : {round(scale,6)}')

        # ---- round and scale coordinates

        x0 = round(x0 * scale)
        y0 = round(y0 * scale)
        x1 = round(x1 * scale)
        y1 = round(y1 * scale)
        x2 = round(x2 * scale)
        y2 = round(y2 * scale)

        return (x0,y0,x1,y1,x2,y2)

    # ---- create graphics window

    win = gr.GraphWin("How high is the mountain?",wwidth,wheight)
    win.setBackground("white")

    # ---- draw x,y axes

    ax.draw_xy_axes(win,True)

    # ---- calculate the triangle point coordinates
    # ---- round coordinates and scale to fit in the window

    x0,y0,x1,y1,x2,y2 = scale_point_coords(maxxy,
                                           measure_distance,
                                           beta_x,
                                           beta_y)

    print(f'point x0        : {x0:<8}  (pix)')
    print(f'point y0        : {y0:<8}  (pix)')
    print(f'point x1        : {x1:<8}  (pix)')
    print(f'point y1        : {y1:<8}  (pix)')
    print(f'point x2        : {x2:<8}  (pix)')
    print(f'point y2        : {y2:<8}  (pix)')

    draw_point(win,x0,y0)
    draw_point(win,x1,y1)
    draw_point(win,x2,y2)

    draw_line(win,x0,y0,x1,y1)
    draw_line(win,x1,y1,x2,y2)
    draw_line(win,x2,y2,x0,y0)

    # ---- add text to window

    add_text(win,0,y0+20,str(flight_altitude)+' ft')
    add_text(win,x0,y0+20,'alpha')
    add_text(win,x1,y1+20,'beta')
    add_text(win,x2,y2-40,'mountain top\n' +
             str(flight_altitude - beta_y) + ' ft')

    # ---- pause then proceed

    ui.pause()

    win.close()


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

if __name__ == '__main__':

    (x,flight_altitude,measure_distance,
    angle_alpha,angle_beta) = get_all_user_input(explanation)

    if not x:
       print()
       sys.exit()

    print()
    print(f'flight altitude : {flight_altitude:<8}  (ft)')
    print(f'measure distance: {measure_distance:<8}  (ft)')
    print(f'angle alpha     : {angle_alpha:<8}  (deg)')
    print(f'angle beta      : {angle_beta:<8}  (deg)')

    beta_x,beta_y = calculation(flight_altitude,
                                measure_distance,
                                angle_alpha,
                                angle_beta)

    beta_x = round(beta_x,1)
    beta_y = round(beta_y,1)

    print(f'angle beta x    : {beta_x:<8}  (ft)')
    print(f'angle beta y    : {beta_y:<8}  (ft)')

    draw_diagram(flight_altitude,measure_distance,beta_x,beta_y)

    print()