# SimpleGraphics.py
""" A module that supports the simple drawing of colored
rectangles, disks, stars, line segments, and text. """

import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
import numpy as np
from time import sleep as pause

# Built-in SimpleGraphics colors
YELLOW    = [1.0,1.0,0.0]
CYAN      = [0.0,1.0,1.0]
MAGENTA   = [1.0,0.0,1.0]
RED       = [1.0,0.0,0.0]
GREEN     = [0.0,1.0,0.0]
BLUE      = [0.0,0.0,1.0]
WHITE     = [1.0,1.0,1.0]
BLACK     = [0.0,0.0,0.0]
PURPLE    = [.57,.17,.93]
DARKGRAY  = [.33,.33,.33]
LIGHTGRAY = [.67,.67,.67]
ORANGE    = [1.0,.50,0.0]
PINK      = [1.0,.71,.80]


def MakeWindow(M,labels=True,bgcolor=WHITE):
    """
    Creates a window with x range -M<=x<=M and y range -M<=y<=M

    If labels is set to False, it will turn off the labeled axes.
    Labeling will not look good if M is too large, e.g., M>10.
    
    The window will have a background color specified by bgcolor.
    The default is white.
    
    Preconditions: M is positive number, labels is boolean,
    and bgcolor is an rgb array.
    
    Usage:
       MakeWindow(5)
       MakeWindow(5,labels=False)
       MakeWindow(5,bgcolor=PURPLE)
       MakeWindow(5,labels=False,bgcolor=[0.87 1.00 0.00])
    """

    plt.figure(figsize=(8,8), dpi=80)
    # Where to put the axis ticks.
    plt.xticks(np.linspace(-M, M, 2*M+1, endpoint=True))
    plt.yticks(np.linspace(-M, M, 2*M+1, endpoint=True))
    # The x and y ranges along the axes.
    plt.xlim(-M,M)
    plt.ylim(-M,M)
    # Background color
    axes = plt.gca() #get current axes
    axes.set_axis_bgcolor(bgcolor) 
    if not labels:
        # Suppress the ticks
        axes.set_xticks([]) # remove number labels and ticks
        axes.set_yticks([])
       
def ShowWindow(time=None):
    """
    Display the current window for a specified time before resuming
    execution. The default time is infinite. That means execution
    halts until the window is closed "manually".
    
    Precondition: t is a nonegative number.
    Usage:
        ShowWindow()  
        ShowWindow(5) 
    """
    if time==None:
        plt.show()
    else:
        plt.show(block=False)
        pause(time)
    
def CloseWindow():
    """ Close all windows. """
    plt.close()


def DrawRect(a,b,L,W,theta=0.0,FillColor=None,EdgeColor=BLACK,EdgeWidth=1):
    """
    Displays a rectangle that is rotated theta degrees counter
    clockwise about its center (a,b). The unrotated version of the
    rectangle has horizontal dimension L and vertical dimension W.
    It has fill color FillColor, edge color EdgeColor, edge width EdgeWidth.
    If no fill color is specified, then the rectangle is transparant
    and only its perimeter is displayed. The default edge color is black
    and the default edge width is one.
    
    Preconditions: a, b, L, and W are numbers, FillColor and EdgeColor are
    rgb lists, EdgeWidth is a positive number, and theta is a number.
    
    Usage:
        DrawRect(0,0,1,2)
        DrawRect(0,0,1,2,theta=30)
        DrawRect(0,0,1,2,FillColor=CYAN)
        DrawRect(0,0,1,2,EdgeColor=RED)
        DrawRect(0,0,1,2,theta=-30,FillColor=CYAN,EdgeColor=RED,EdgeWidth=5)
    """

    # These arrays specify the (x,y) coordinates of the rectangle corners.
    L = float(L)
    W = float(W)
    theta = theta*np.pi/180
    x1 = np.array([-L/2,L/2,L/2,-L/2,-L/2])
    y1 = np.array([-W/2,-W/2,W/2,W/2,-W/2])
    x = a + np.cos(theta)*x1 - np.sin(theta)*y1
    y = b + np.sin(theta)*x1 + np.cos(theta)*y1
    if FillColor is None:
        # No fill, just draw the perimeter
        plt.plot(x,y,color=EdgeColor,linewidth=EdgeWidth)
    else:
        # Fill and accent the perimeter according to the values of eColor and eWidth.
        plt.fill(x,y,facecolor=FillColor,edgecolor=EdgeColor,linewidth=EdgeWidth)


def DrawDisk(a,b,r,FillColor=None,EdgeColor=BLACK,EdgeWidth=1):
    """
    Draws a disk with center at (a,b), and radius r.
    It has fill color FillColor, edge color EdgeColor, edge width EdgeWidth.
    If no fill color is specified, then the disk is transparant
    and only its perimeter is displayed. The default edge color is black
    and the default edge width is one.
    
    Preconditions: a, b, and r are numbers, FillColor and EdgeColor are
    rgb lists, and EdgeWidth is a positive number.
    
    Usage:
        DrawDisk(0,0,2)
        DrawDisk(0,0,2,FillColor=CYAN)
        DrawDisk(0,0,2,EdgeColor=RED)
        DrawDisk(0,0,2,FillColor=CYAN,EdgeColor=RED,EdgeWidth=5)
    """
    
    theta= np.linspace(0,2*np.pi,256,endpoint=True)
    x = a+r*np.cos(theta)
    y = b+r*np.sin(theta)
    if FillColor is None:
        # No fill, just the perimeter
        plt.plot(x,y,color=EdgeColor,linewidth=EdgeWidth)
    else:
        # Fill and accent the perimeter according to the values of EdgeColor and EdgeWidth.
        plt.fill(x,y,facecolor=FillColor,edgecolor=EdgeColor,linewidth=EdgeWidth)


def DrawStar(a,b,r,theta=0.0,FillColor=None,EdgeColor=BLACK,EdgeWidth=1):
    """
    Displays a star that is rotated theta degrees counter
    clockwise about its center (a,b). The star has radius r.
    It has fill color FillColor, edge color EdgeColor, edge width EdgeWidth.
    If no fill color is specified, then the star is transparant
    and only its perimeter is displayed. The default edge color is black
    and the default edge width is one.
    
    Preconditions: a, b, r and theta are numbers, FillColor and EdgeColor are
    rgb lists, and EdgeWidth is a positive number.
    
    Usage:
        DrawStar(0,0,2)
        DrawStar(0,0,2,theta=30)
        DrawStar(0,0,2,FillColor=CYAN)
        DrawStar(0,0,2,EdgeColor=RED)
        DrawStar(0,0,2,theta=-30,FillColor=CYAN,EdgeColor=RED,EdgeWidth=5)
    """
    
    # The radius of the inner 5 vertices..
    r2 = r/(2*(1+np.sin(np.pi/10)))
    # Compute the 10 vertices
    tau = np.linspace(0,2*np.pi,11,endpoint=True) + np.pi/10 + theta*np.pi/180
    x = np.cos(tau); x[0:11:2]= r*x[0:11:2]; x[1:11:2]=r2*x[1:11:2]; x = x+a
    y = np.sin(tau); y[0:11:2]= r*y[0:11:2]; y[1:11:2]=r2*y[1:11:2]; y = y+b
    
    # Display...  
    if FillColor is None:
        # No fill, just the perimeter
        plt.plot(x,y,color=EdgeColor,linewidth=EdgeWidth)
    else:
        # Fill and accent the perimeter according to the values of eColor and eWidth.
        plt.fill(x,y,facecolor=FillColor,edgecolor=EdgeColor,linewidth=EdgeWidth)
        
    
def DrawLineSeg(x0,y0,x1,y1,LineColor=BLACK,LineWidth=1):
    """
    Displays a line segment that connects (x0,y0) and (x1,y1). The
    line segment has color LineColor and width LineWidth.
    
    Preconditions: x0,y0,x1,y1 are numbers, c is an rgb list, and LW is a positive
    number.
    
    Usage:
        DrawLineSeg(1,2,3,4)
        DrawLineSeg(1,2,3,4,LineWidth=4)
        DrawLineSeg(1,2,3,4,LineColor=MAGENTA)
        DrawLineSeg(1,2,3,4,LineColor=BLUE,LineWidth=2)
        
    """
    plt.plot([x0,x1],[y0,y1],linewidth=LineWidth,color=LineColor)
    
def DrawText(x,y,s,FontColor=BLACK,FontSize=10):
    """
    Displays string s at (x,y) with color FontColor and size FontSize. The default color
    is BLACK and the default size is 10.
    
    PreConditions: x and y are numbers, FontColor is an rgb list, and FontSize is a positive int.
    
    Usage:
        DrawText(1,2,'Message')
        DrawText(1,2,'Message',FontColor=RED)
        DrawText(1,2,'Message',FontSize=18)
        DrawText(1,2,'Message',FontSize=18,FontColor=RED)
    """
    plt.text(x,y,s,color=FontColor,fontsize=FontSize)
    
def Title(s,FontColor=BLACK,FontSize=18):
    """
    Displays string s as a title above the figure window with color FontColor and size FontSize.
    The default color is BLACK and the default size is 18.
    
    PreConditions: s is a string, FontColor is an rgb list, and FontSize is a positive int.

    Usage:
        Title('Message')
        Title('Message',FontColor=RED)
        Title('Message',FontSize=24)
        Title('Message',FontSize=24,FontColor=RED)
    """
    plt.title(s,fontsize=FontSize,color=FontColor)
    
def DrawPoly(x,y, FillColor=None, EdgeWidth=1, EdgeColor=BLACK):
    """ Draws a polygon whose vertices are specified by th
    lists x and y. 
    
    The fill color can be one of the 13 built-in colors YELLOW, CYAN, MAGENTA,
    RED, GREEN, BLUE, WHITE, BLACK, PURPLE, LIGHTFGRAY, DARKGRAY, ORANGE, or PINK
    or an rgb array. The default value for color is None and in this case
    the rectangle is transparent.
    
    The perimeter display width is specified through the argument stroke.
    The default value is 1. Larger values create a wider black outline of
    the displayed rectangle. For no perimeter highlighting, set stroke=0.
    
    Preconditions: a,b,L, and W are float or int and positive. color
    is an rgb array, stroke is a positive float or int, and rotate is
    a float or int that specifies the clockwise rotation angle in degrees.
    
    Sample calls:
                  DrawPoly([-2,2,2,-2],[-2,-2,2,2],color=PINK)
             
    """

    # These arrays specify the (x,y) coordinates of the rectangle corners.
    u = list(x);u.append(u[0])
    v = list(y);v.append(v[0])
    if FillColor is None:
        # No fill, just draw the perimeter
        plt.plot(u, v,linewidth=EdgeWidth,color=EdgeColor)
    else:
        # Fill and accent the perimeter according to the value of stroke.
        plt.fill(u, v, facecolor=FillColor, edgecolor=EdgeColor, linewidth=EdgeWidth)