'''
Created on 16-Apr-2021

@author: bthakur
'''
import traceback
import inspect
import random

import matplotlib.pyplot as plt
from xv.km.ui import SvgHelper
from xv.util import strip_source_code_tabs, get_function_source_code
from xv.util import UtilClass

from numpy import sin, cos
from sympy import latex
import numpy as np
import sympy as sp

class CircleHelper(UtilClass):
    '''
    
    '''
    
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        pass

    
    def plot_a_unit_circle_with_marked_integers(self, point_count):
        import matplotlib.pyplot as plt
        import matplotlib.colors as mcolors
        from itertools import cycle
        import random
        import numpy as np
        from numpy import linspace, pi, sin, cos
        
 
        one_cycle = 6
        offset_mul = 0.03
        ax_lim =  offset_mul * (point_count // one_cycle) + 1.5
        
        #colors = list(mcolors.CSS4_COLORS)
        colors = ['red', 'green', 'blue']
        random.shuffle(colors)
        
        fig = plt.figure(figsize=(20, 12))
        #fig, axs = plt.subplots(2, 3, figsize=(20, 12))
        
        gs = fig.add_gridspec(1,1)
        
        angles = linspace(0 * pi, 2 * pi, point_count )
        markersize = 2
        
        #plot circle
        #xs = cos(angles)
        #ys = sin(angles)
        #plt.plot(xs, ys, color = 'grey')
        
        # original circle
        ax00 = fig.add_subplot(gs[0, 0])
        self._plot_part_unit_circles_with_marked_integers(
                                                     ax00,
                                                     point_count,
                                                     colors,
                                                     markersize,
                                                     offset_mul,
                                                     one_cycle,
                                                     ax_lim,
                                                     int_mod = None
                                                     )
            
        plt.subplots_adjust(wspace=0.0001)    
        fig.tight_layout()
        pass
    

    
    def plot_four_unit_circles_with_marked_integers(self, point_count):
        import matplotlib.pyplot as plt
        import matplotlib.colors as mcolors
        from itertools import cycle
        import random
        import numpy as np
        from numpy import linspace, pi, sin, cos
 
        one_cycle = 6
        offset_mul = 0.02
        ax_lim =  offset_mul * (point_count // one_cycle) + 1
        
        #colors = list(mcolors.CSS4_COLORS)
        colors = ['red', 'green', 'blue']
        random.shuffle(colors)
        
        fig = plt.figure(figsize=(20, 12))
        #fig, axs = plt.subplots(2, 3, figsize=(20, 12))
        
        gs = fig.add_gridspec(3,2)
        
        markersize = 2
        
        #plot circle
        #angles = linspace(0 * pi, 2 * pi, point_count )
        #xs = cos(angles)
        #ys = sin(angles)
        #plt.plot(xs, ys, color = 'grey')
        
        # original circle
        ax00 = fig.add_subplot(gs[1, 0])
        self._plot_part_unit_circles_with_marked_integers(
                                                     ax00,
                                                     point_count,
                                                     colors,
                                                     markersize,
                                                     offset_mul,
                                                     one_cycle,
                                                     ax_lim,
                                                     int_mod = None
                                                     )


        # plot parts of circle
        for circle_idx in range(3):
            ax00 = fig.add_subplot(gs[circle_idx, 1])
            self._plot_part_unit_circles_with_marked_integers(
                                                         ax00,
                                                         point_count,
                                                         colors,
                                                         markersize,
                                                         offset_mul,
                                                         one_cycle,
                                                         ax_lim,
                                                         int_mod = circle_idx
                                                         )
            
        plt.subplots_adjust(wspace=0.0001)    
        fig.tight_layout()
        pass
    
    def _plot_part_unit_circles_with_marked_integers(self, 
                                                     ax,
                                                     point_count,
                                                     colors,
                                                     markersize,
                                                     offset_mul,
                                                     one_cycle,
                                                     ax_lim,
                                                     int_mod = None
                                                     ):
        for idx in range(point_count):
            if not int_mod is None:
                if idx % 3 != int_mod:
                    continue
            theta = idx
            color = colors[idx % 3]
            xy_offset = 1.05 + offset_mul * (idx // one_cycle)
            ax.plot(cos(theta), sin(theta), color = f'{color}', marker = 'o', markersize = markersize)
            ax.annotate(f'{theta}', xy=(cos(theta) * xy_offset, sin(theta) * xy_offset), xycoords='data', color = f'{color}', fontsize= 10)

            ax.set_aspect('equal')
            ax.axis('off')
            ax.set_xlim(-ax_lim, ax_lim)
            ax.set_ylim(-ax_lim, ax_lim)
            pass




    def get_plot_code_and_svg_str(self, plot_method, *args, **kwargs):
        try:
            
            svg_str =  plot_method(*args, **kwargs)
            
            lines = get_function_source_code(plot_method)
            
            source_code = strip_source_code_tabs(lines[1:])
            
            code_to_run =  '\n'.join(source_code)
            
            code_to_run = code_to_run.replace("\nplt.ioff()", "")
            
            
            start_idx = code_to_run.index("import matplotlib.pyplot as plt")
            
            end_idx = code_to_run.index("svg_str = ")
            
            
            code_to_run = code_to_run[start_idx :end_idx]
            plot_code = code_to_run + "\nplt.show()"
            
            
            result = {
                'plot_code': plot_code,
                'svg_str': svg_str,
                }
            
            return result
        except Exception as e:
            sError = f"""Error {self.__class__.__name__}.{inspect.stack()[0][3]}:{e}
            failed to plot 
            """
            self._log(sError)
            self._log(traceback.format_exc())
        return None
        pass


    def plot_unit_circle_center_origin(self, *args, **kwargs):
        
        import matplotlib.pyplot as plt
        import numpy as np
        from numpy import linspace, pi, sin, cos

        plt.ioff()

        fig = plt.figure(figsize=(6, 6))
            
        angles = linspace(0 * pi, 2 * pi, 100 )
            
        xs = cos(angles)
        ys = sin(angles)
        #plot center
        plt.plot(0, 0, marker = 'o', color = 'grey', markersize = 10)
        plt.gca().annotate('O (0, 0)', xy=(0-0.20, 0-0.12), xycoords='data', fontsize=10)
            
        #plot circle
        plt.plot(xs, ys, color = 'grey')
            
            
        #optional code
        plt.xlim(-1.2, 1.2)
        plt.ylim(-1.2, 1.2)
        plt.gca().set_aspect('equal')
        plt.axis('off')
        
        fig.tight_layout()
        
        svg_str = SvgHelper.GET_SVG_STR(close_plot =True)
        return svg_str
            
        pass
    

    def plot_concentric_circles_center_origin(self, *args, **kwargs):
        
        import matplotlib.pyplot as plt
        import numpy as np
        from numpy import linspace, pi, sin, cos
        import random

        plt.ioff()

        fig = plt.figure(figsize=(6, 6))
            
        angles = linspace(0 * pi, 2 * pi, 100 )
            
        #plot center
        plt.plot(0, 0, marker = 'o', color = 'b', markersize = 10)
        plt.gca().annotate('O (0, 0)', xy=(0-0.20, 0-0.12), xycoords='data', fontsize=10)
            
        radii = random.sample(list(range(1, 9)), random.randint(2, 5))
        
        for r in radii:
            #plot circle
            xs = r * cos(angles)
            ys = r * sin(angles)
            plt.plot(xs, ys, label = f"r = {r}")
            
        plt.legend(loc="upper right")
        
        #optional code
        plt.xlim(-max(radii) - 1, max(radii) + 1)
        plt.ylim(-max(radii) - 1, max(radii) + 1)
        plt.gca().set_aspect('equal')
        plt.axis('off')

        fig.tight_layout()
        
        svg_str = SvgHelper.GET_SVG_STR(close_plot =True)
        return svg_str
        pass
 
    
    def plot_cylinder(self, *args, **kwargs):
        import matplotlib.pyplot as plt
        import numpy as np
        from numpy import pi,cos,sin
        import random

        plt.ioff()
        
        fig = plt.figure(figsize=(6, 6))

        theta = np.linspace(0, 2 * pi, 100)
        
        #some random values. you can put specific values for each of them
        r = random.randint(1, 5)
        
        move_left_right = random.choice([0, 0.5, 0.9, 1, 2, 3]) * random.choice([1, -1])
        move_up_down = random.choice([0, 0.5, 0.9, 1, 2, 3]) * random.choice([1, -1])
        
        number_of_rings = random.randint(10, 50)
        length_of_cylinder = random.randint(2, 10)
        
        for i in np.linspace(0, length_of_cylinder, number_of_rings):
            x = r * cos(theta) +  i * move_left_right
            y = r * sin (theta) + i * move_up_down
            plt.plot(x,y)
            
        #optional code
        plt.gca().set_aspect('equal')
        plt.axis('off')

        fig.tight_layout()

        label_text = f"""
        r = {r}
        move_left_right = {move_left_right}
        move_up_down = {move_up_down}
        number_of_rings = {number_of_rings}
        length_of_cylinder = {length_of_cylinder}
        """

        plt.text(1, 1, label_text, 
                 ha='right', va='top', 
                 transform=plt.gca().transAxes)

        
        svg_str = SvgHelper.GET_SVG_STR(close_plot =True)
        return svg_str
        pass
 

    def plot_cone(self, *args, **kwargs):
        import matplotlib.pyplot as plt
        import numpy as np
        from numpy import pi,cos,sin
        import random

        plt.ioff()
        
        fig = plt.figure(figsize=(6, 6))

        theta = np.linspace(0, 2 * pi, 100)
            
        move_left_right = 3
        move_up_down = -1
        number_of_rings = 100
        length_of_cone = 120

        for i in np.linspace(0, length_of_cone, number_of_rings):
            r = i
            x = r * cos(theta) +  r * move_left_right
            y = r * sin (theta) + r * move_up_down
            plt.plot(x,y)
            
        #optional code
        plt.gca().set_aspect('equal')
        plt.axis('off')


        fig.tight_layout()
        
        label_text = f"""
        move_left_right = {move_left_right}
        move_up_down = {move_up_down}
        number_of_rings = {number_of_rings}
        length_of_cone = {length_of_cone}
        """
            
        plt.text(1, 1, label_text, 
                 ha='right', va='top', 
                 transform=plt.gca().transAxes)

        
        svg_str = SvgHelper.GET_SVG_STR(close_plot =True)
        return svg_str
        pass
 
    
    def plot_hemisphere(self, *args, **kwargs):
        import matplotlib.pyplot as plt
        import numpy as np
        from numpy import pi,cos,sin
        import random

        plt.ioff()
        
        fig = plt.figure(figsize=(6, 6))

        theta = np.linspace(0, 2 * pi, 100)

        
        #some random values. you can put specific values for each of them
        r0 = random.randint(1, 9)
        ellipses = random.randint(20, 50)
        ratio = random.randint(3, 7) /10  #major axis of ellipse with respect to its minor axis

        for alpha in np.linspace(0, pi / 2, ellipses):
            a = r0 * cos(alpha)  #major radius of the ellipse
            b = a * ratio    #minor radius of the ellipse
            
            x = a * cos(theta)
            y = b * sin (theta) + r0 * sin(alpha)
            plt.plot(x,y)
            
        #optional code
        plt.gca().set_aspect('equal')
        plt.axis('off')

        fig.tight_layout()
        
        label_text = f"""
        r0 = {r0}
        ellipses = {ellipses}
        ratio = {ratio}
        """
            
        plt.text(1, 1, label_text, 
                 ha='right', va='top', 
                 transform=plt.gca().transAxes)

        
        svg_str = SvgHelper.GET_SVG_STR(close_plot =True)
        return svg_str
        pass

    
    def plot_sphere(self, *args, **kwargs):
        import matplotlib.pyplot as plt
        import numpy as np
        from numpy import pi,cos,sin
        import random

        plt.ioff()
        
        fig = plt.figure(figsize=(6, 6))

        theta = np.linspace(0, 2 * pi, 100)
            
        #some random values. you can put specific values for each of them
        r0 = random.randint(1, 9)
        ellipses = random.randint(20, 50)
        ratio = random.randint(3, 7) /10  #major axis of ellipse with respect to its minor axis
            
        for alpha in np.linspace(0, pi / 2, ellipses):
            a = r0 * cos(alpha)  #major radius of the ellipse
            b = a * ratio    #minor radius of the ellipse
            
            x = a * cos(theta)
            y = b * sin (theta) + r0 * sin(alpha)
            plt.plot(x,y)
            
            y = b * sin (theta) - r0 * sin(alpha)
            plt.plot(x,y)
            
        #optional code
        plt.gca().set_aspect('equal')
        plt.axis('off')

        fig.tight_layout()
        
        label_text = f"""
        r0 = {r0}
        ellipses = {ellipses}
        ratio = {ratio}
        """
            
        plt.text(1, 1, label_text, 
                 ha='right', va='top', 
                 transform=plt.gca().transAxes)

        
        svg_str = SvgHelper.GET_SVG_STR(close_plot =True)
        return svg_str
        pass
 
    
    def plot_unit_circle(self, *args, **kwargs):
        import matplotlib.pyplot as plt
        import numpy as np
        from numpy import pi,cos,sin
        import random

        plt.ioff()
        
        fig = plt.figure(figsize=(6, 6))
    
        angles = np.linspace(0 * pi, 2 * pi, 100 )
        
        r = 1

        #specify a random center of circle
        center = (random.randint(1, 9) / 10, random.randint(1, 9) / 10)
        
        #point a right of center on the circle
        point_A = (center[0] + r, center[1])
            
        xs = center[0] + cos(angles)
        ys = center[1] + sin(angles)
        
        #plot circle
        plt.plot(xs, ys, color = 'grey')

        #plot center
        plt.plot(center[0], center[1], marker = 'o', color = 'grey', markersize = 10)
        
        plt.gca().annotate(f'O ({center[0]}, {center[1]})', 
                           xy=(center[0] - 0.20, center[1] - 0.12), 
                           xycoords='data', fontsize=10)
            
        #plot right point
        plt.plot(point_A[0], point_A[1], marker = 'o', color = 'grey', markersize = 10)
        
        plt.gca().annotate(f'A ({point_A[0]}, {point_A[1]})', 
                           xy=(point_A[0] - 0.20, point_A[1]  - 0.12), 
                           xycoords='data', fontsize=10)
            
        
        #plot radius
        plt.plot((center[0], point_A[0]) , (center[1], point_A[1]), color = 'blue')
        
        mid_x = (center[0]  + point_A[0]) / 2
        plt.gca().annotate(f'r = 1', 
                           xy=(mid_x - 0.20, center[1]  + 0.06), 
                           xycoords='data', fontsize=10,
                           color = 'blue',
                           )
            
        
        fig.tight_layout()
        
        label_text = f"""
        center = {center}
        r = {r}
        """
            
        plt.text(1, 1, label_text, 
                 ha='right', va='top', 
                 transform=plt.gca().transAxes)

        
        svg_str = SvgHelper.GET_SVG_STR(close_plot =True)
        return svg_str
        pass



    def plot_unit_circle_divided_into_n_sides(self, *args, **kwargs):
        
        import matplotlib.pyplot as plt
        import numpy as np
        from numpy import pi,cos,sin
        import random
        
        show_point_lables = kwargs.get('show_point_lables', True)
        point_count = kwargs.get('point_count')
        
        plt.ioff()
        
        fig = plt.figure(figsize=(6, 6))
        
        angles = np.linspace(0 * pi, 2 * pi, 100 )
            
        xs = cos(angles)
        ys = sin(angles)
        
        
        #plot center
        plt.plot(0, 0, marker = 'o', color = 'grey', markersize = 10)
        #plt.gca().annotate('O (0, 0)', xy=(0-0.20, 0-0.12), xycoords='data', fontsize=10)
            
        #plot circle
        plt.plot(xs, ys, color = 'grey')
            
        for idx in range(point_count):
            if show_point_lables:
                theta = 2 * pi * idx/point_count
                plt.plot(cos(theta), sin(theta), marker = 'o', markersize = 10)
                
                plt.plot( ( 0, cos(theta)  ),
                          ( 0, sin(theta)  ) 
                        )
                if idx != 0:
                    point_label = f'{idx}/{point_count}'
                else:
                    point_label = f'{idx}/{point_count} = {point_count}/{point_count} = 1'
                    
                
                xy = (cos(theta) * 1.12, sin(theta) -0.12)
                plt.gca().annotate(point_label, xy= xy, xycoords='data', fontsize=10) 
            pass

        #optional code
        plt.xlim(-1.2, 1.2)
        plt.ylim(-1.2, 1.2)
        plt.gca().set_aspect('equal')
        plt.axis('off')

        
        fig.tight_layout()
    
        if show_point_lables:
            label_text = f"""
            point_count = {point_count}
            """
            plt.text(1, 1, label_text, 
                     ha='right', va='top', 
                     transform=plt.gca().transAxes)
            pass
        
        svg_str = SvgHelper.GET_SVG_STR(close_plot =True)
        return svg_str
        pass

    

    def plot_unit_circle_mark_angles(self, *args, **kwargs):
        import matplotlib.pyplot as plt
        import numpy as np
        from numpy import pi,cos,sin
        import random
        
        show_point_lables = kwargs.get('show_point_lables', True)

        plt.ioff()
        
        fig = plt.figure(figsize=(6, 6))
        
        point_count = random.choice([3, 4, 6, 8])
        
        angles = np.linspace(0 * pi, 2 * pi, 100 )
            
        xs = cos(angles)
        ys = sin(angles)
        
        
        #plot center
        plt.plot(0, 0, marker = 'o', color = 'grey', markersize = 10)
        plt.gca().annotate('O (0, 0)', xy=(0-0.20, 0-0.12), xycoords='data', fontsize=10)
            
        #plot circle
        plt.plot(xs, ys, color = 'grey')
            
        for idx in range(point_count):
            theta = 2 * pi * idx/point_count
            plt.plot(cos(theta), sin(theta), marker = 'o', markersize = 10)
        
            if show_point_lables:
                xy = (cos(theta) * 1.12, sin(theta) -0.12)
                plt.gca().annotate(f'({idx}/{point_count})2π', xy= xy, xycoords='data', fontsize=10) 
            pass

        #optional code
        plt.xlim(-1.2, 1.2)
        plt.ylim(-1.2, 1.2)
        plt.gca().set_aspect('equal')
        plt.axis('off')

        
        fig.tight_layout()
    
        if show_point_lables:
            label_text = f"""
            point_count = {point_count}
            """
            plt.text(1, 1, label_text, 
                     ha='right', va='top', 
                     transform=plt.gca().transAxes)
            pass
        
        svg_str = SvgHelper.GET_SVG_STR(close_plot =True)
        return svg_str
        pass

    

    def plot_unit_circle_mark_points(self, *args, **kwargs):
        import matplotlib.pyplot as plt
        import numpy as np
        from numpy import pi,cos,sin
        import random
        
        show_point_lables = kwargs.get('show_point_lables', True)

        plt.ioff()
        
        fig = plt.figure(figsize=(6, 6))
        
        point_count = random.choice([3, 4, 6, 8])
        
        angles = np.linspace(0 * pi, 2 * pi, 100 )
            
        xs = cos(angles)
        ys = sin(angles)
        
        
        #plot center
        plt.plot(0, 0, marker = 'o', color = 'grey', markersize = 10)
        plt.gca().annotate('O (0, 0)', xy=(0-0.20, 0-0.12), xycoords='data', fontsize=10)
            
        #plot circle
        plt.plot(xs, ys, color = 'grey')
            
        for idx in range(point_count):
            theta = 2 * pi * idx/point_count
            plt.plot(cos(theta), sin(theta), marker = 'o', markersize = 10)
        
            if show_point_lables:
                point_label = f'(cos(({idx}/{point_count})2π),\n sin(({idx}/{point_count})2π))'
                xy = (cos(theta) + 0.06, sin(theta) - 0.10)
                plt.gca().annotate(point_label, xy= xy, xycoords='data', fontsize=10) 
            pass

        #optional code
        plt.xlim(-1.2, 1.2)
        plt.ylim(-1.2, 1.2)
        plt.gca().set_aspect('equal')
        plt.axis('off')

        
        fig.tight_layout()
    
        if show_point_lables:
            label_text = f"""
            point_count = {point_count}
            """
            plt.text(1, 1, label_text, 
                     ha='right', va='top', 
                     transform=plt.gca().transAxes)
            pass
        
        svg_str = SvgHelper.GET_SVG_STR(close_plot =True)
        return svg_str
        pass



    def plot_unit_circle_with_radius_at_theta(self, *args, **kwargs):
        import matplotlib.pyplot as plt
        import numpy as np
        from numpy import pi,cos,sin
        import random

        plt.ioff()
        
        fig = plt.figure(figsize=(6, 6))
    
        angles = np.linspace(0 * pi, 2 * pi, 100 )
        
        r = 1

        #specify a random center of circle
        center = (random.randint(1, 9) / 10, random.randint(1, 9) / 10)
        
        #point on the circle to draw radius
        theta_degrees = kwargs.get('theta_degrees', 0)
        theta = round(theta_degrees * np.pi / 180, 2)
        
        point_Ax = round(center[0] + r * cos(theta), 2)
        point_Ay = round(center[1] + sin(theta), 2)
        point_A = (point_Ax, point_Ay)
            
        xs = center[0] + cos(angles)
        ys = center[1] + sin(angles)
        
        #plot circle
        plt.plot(xs, ys, color = 'grey')

        #plot center
        plt.plot(center[0], center[1], marker = 'o', color = 'grey', markersize = 10)
        
        plt.gca().annotate(f'O ({center[0]}, {center[1]})', 
                           xy=(center[0] - 0.20, center[1] - 0.12), 
                           xycoords='data', fontsize=10)
            
        #plot right point
        plt.plot(point_A[0], point_A[1], marker = 'o', color = 'grey', markersize = 10)
        
        plt.gca().annotate(f'A ({point_A[0]}, {point_A[1]})', 
                           xy=(point_A[0] - 0.20, point_A[1]  - 0.12), 
                           xycoords='data', fontsize=10)
            
        
        #plot radius
        plt.plot((center[0], point_A[0]) , (center[1], point_A[1]), color = 'blue')
        
        mid_x = (center[0]  + point_A[0]) / 2
        mid_y = (center[1]  + point_A[1]) / 2
        plt.gca().annotate(f'r = 1', 
                           xy=(mid_x - 0.20, mid_y - 0.06), 
                           xycoords='data', fontsize=10,
                           color = 'blue',
                           )
            
        
        #optional code
        offset = 0.15
        plt.xlim(- r - offset + center[0], r + offset + center[0])
        plt.ylim(- r - offset + center[1], r + 2 * offset + center[1])
        plt.gca().set_aspect('equal')
        plt.axis('off')
        
        label_text = f"""
        center = {center}
        r = {r}
        theta_degrees = {theta_degrees}
        """
            
        plt.text(1, 1, label_text, 
                 ha='right', va='top', 
                 transform=plt.gca().transAxes)

        
        svg_str = SvgHelper.GET_SVG_STR(close_plot =True)
        return svg_str
        pass


    def plot_unit_circle_mark_sin_cos(self, *args, **kwargs):
        import matplotlib.pyplot as plt
        import numpy as np
        import sympy as sp
        from numpy import linspace, pi, sin, cos
        from sympy import latex
        import random

        plt.ioff()
        
        fig = plt.figure(figsize=(6, 6))

    
        angles = linspace(0 * pi, 2 * pi, 100 )
            
            
        xs = cos(angles)
        ys = sin(angles)
        #plot center
        plt.plot(0, 0, marker = 'o', color = 'grey', markersize = 10)
        plt.gca().annotate('O (0, 0)', xy=(0-0.20, 0-0.12), xycoords='data', fontsize=10)
            
        #plot circle
        plt.plot(xs, ys, color = 'grey')
            
            
        theta = np.pi / 3
        
        theta_symb = sp.Symbol('theta')
            
        #mark ${latex(theta_symb)}$ and arc BA
        plt.gca().annotate(f'${latex(theta_symb)}$', xy=( +.12, +0.07), xycoords='data', fontsize=10)
        plt.gca().annotate(f'arc AB = ${latex(theta_symb)}$', xy=( cos(theta/2)+.03, sin(theta/2)+0.03), xycoords='data', fontsize=10)
            
        #plot point A
        plt.plot(1, 0, marker = 'o', color = 'indigo', markersize = 10)
        plt.gca().annotate(f'A (1, 0)', xy=( 1.12, -0.12), xycoords='data', fontsize=10)
            
        #plot radius OA
        plt.plot((0, 1), (0, 0), color = 'indigo')
        plt.gca().annotate(f'OA = 1', xy=(0, 0.5), xycoords='data', fontsize=10, color = 'indigo')
            
        #plot point B
        plt.plot(cos(theta), sin(theta), marker = 'o', color = 'blue', markersize = 10)
        plt.gca().annotate(f'B ( ${latex(sp.cos(theta_symb))}$, ${latex(sp.sin(theta_symb))}$ )', xy=(cos(theta) * 1.12, sin(theta) +0.12), xycoords='data', fontsize=10)
            
            
        
        #plot radius OB
        plt.plot((0, cos(theta)), (0, sin(theta)), color = 'blue')
            
        #plot point C
        plt.plot(cos(theta), 0, marker = 'o', color = 'green', markersize = 10)
        plt.gca().annotate(f'C', xy=(cos(theta) * .88,  -0.12), xycoords='data', fontsize=10)
            
        #plot line OC
        label = f'''   OC
        = Left/Right
        = x
        = ${latex(sp.cos(theta_symb))}$'''
            
        plt.plot((0, cos(theta)), (0, 0), color = 'red')
        plt.gca().annotate(label, xy=(+0.12,  -0.39), xycoords='data', color = 'red', fontsize=10)
            
        #plot line CB
        label = f'''   CB
        = Up/Down
        = y
        = ${latex(sp.sin(theta_symb))}$'''
            
        plt.plot((cos(theta), cos(theta)), (0, sin(theta)), color = 'green')
        plt.gca().annotate(label, xy=(cos(theta)+0.03,  sin(theta)/4), xycoords='data', color = 'green', fontsize=10)
            
        #optional code
        plt.xlim(-1.2, 1.2)
        plt.ylim(-1.2, 1.2)
        plt.gca().set_aspect('equal')
        plt.axis('off')
        
        svg_str = SvgHelper.GET_SVG_STR(close_plot =True)
        return svg_str
        pass



    def plot_unit_circle_argand_diagram(self, *args, **kwargs):
        import matplotlib.pyplot as plt
        import numpy as np
        import sympy as sp
        from numpy import linspace, pi, sin, cos
        from sympy import latex
        import random

        plt.ioff()
        
        fig = plt.figure(figsize=(6, 6))

    
        angles = linspace(0 * pi, 2 * pi, 100 )
            
            
        xs = cos(angles)
        ys = sin(angles)
        #plot center
        plt.plot(0, 0, marker = 'o', color = 'grey', markersize = 10)
        plt.gca().annotate('O (0, 0)', xy=(0-0.20, 0-0.12), xycoords='data', fontsize=10)
            
        #plot circle
        plt.plot(xs, ys, color = 'grey')
            
            
        theta = np.pi / 3
        
        theta_symb = sp.Symbol('theta')
        
        #annotate theta
        plt.gca().annotate(f'${latex(theta_symb)}$', xy=( +.12, +0.07), xycoords='data', fontsize=10)
        
            
        #mark ${latex(theta_symb)}$ and arc BA
        plt.gca().annotate(f'${latex(theta_symb)}$', xy=( +.12, +0.07), xycoords='data', fontsize=10)
        plt.gca().annotate(f'arc AB = ${latex(theta_symb)}$', xy=( cos(theta/2)+.03, sin(theta/2)+0.03), xycoords='data', fontsize=10)
            
        #plot point A
        plt.plot(1, 0, marker = 'o', color = 'indigo', markersize = 10)
        plt.gca().annotate(f'$ A = 1 + i \\; 0$', xy=( 1.12, -0.12), xycoords='data', fontsize=10)
            
        #plot radius OA
        plt.plot((0, 1), (0, 0), color = 'indigo')
        plt.gca().annotate(f'OA = 1', xy=(0, 0.5), xycoords='data', fontsize=10, color = 'indigo')
            
        #plot point B
        plt.plot(cos(theta), sin(theta), marker = 'o', color = 'blue', markersize = 10)
        
        B_label = f"""$
        B =
        {latex(sp.cos(theta_symb))}
        + i \\; {latex(sp.sin(theta_symb))}
        = {latex(sp.exp(sp.I * theta_symb))} 
        $
        """.replace("\n", " ").strip()
        plt.gca().annotate(B_label, xy=(cos(theta) * 1.12, sin(theta) +0.12), 
                           xycoords='data', fontsize=10)
        
        #plot radius OB
        plt.plot((0, cos(theta)), (0, sin(theta)), color = 'blue')
            
        #plot point C
        plt.plot(cos(theta), 0, marker = 'o', color = 'green', markersize = 10)
        plt.gca().annotate(f'C', xy=(cos(theta) * .88,  -0.12), xycoords='data', fontsize=10)
            
        #plot line OC
        label = f'''   OC
        = Left/Right
        = x
        = ${latex(sp.cos(theta_symb))}$'''
            
        plt.plot((0, cos(theta)), (0, 0), color = 'red')
        plt.gca().annotate(label, xy=(+0.12,  -0.39), xycoords='data', color = 'red', fontsize=10)
            
        #plot line CB
        label = f'''   CB
        = Up/Down
        = y
        = ${latex(sp.sin(theta_symb))}$'''
            
        plt.plot((cos(theta), cos(theta)), (0, sin(theta)), color = 'green')
        plt.gca().annotate(label, xy=(cos(theta)+0.03,  sin(theta)/4), xycoords='data', color = 'green', fontsize=10)
            
        #optional code
        plt.xlim(-1.2, 1.2)
        plt.ylim(-1.2, 1.2)
        plt.gca().set_aspect('equal')
        plt.axis('off')
        
        svg_str = SvgHelper.GET_SVG_STR(close_plot =True)
        return svg_str
        pass


    def plot_unit_circle_mark_sin_cos_coordinates(self, *args, **kwargs):
        import matplotlib.pyplot as plt
        import numpy as np
        import sympy as sp
        from numpy import linspace, pi, sin, cos
        from sympy import latex
        import random

        plt.ioff()
        
        fig = plt.figure(figsize=(6, 6))

    
        angles = linspace(0 * pi, 2 * pi, 100 )
            
            
        xs = cos(angles)
        ys = sin(angles)
        #plot center
        plt.plot(0, 0, marker = 'o', color = 'grey', markersize = 10)
        plt.gca().annotate('O (0, 0)', xy=(0-0.20, 0-0.12), xycoords='data', fontsize=10)
            
        #plot circle
        plt.plot(xs, ys, color = 'grey')
            
            
        theta = np.pi / 3
        
        theta_symb = sp.Symbol('theta')

        #annotate theta
        plt.gca().annotate(f'${latex(theta_symb)}$', xy=( +.12, +0.07), xycoords='data', fontsize=10)
            
        #plot point A
        plt.plot(1, 0, marker = 'o', color = 'indigo', markersize = 10)
        
        zero_symb = sp.Symbol('0')
        
        A_label = [f"A",
                   f"= ( $cos(0), \\; sin(0)$ )",
                   f"= $cos(0) + i sin(0)$",
                   f"= ${latex(sp.exp(sp.I * zero_symb))}$"
                   ]
        A_label = "\n".join(A_label)
        
        plt.gca().annotate(A_label, xy=( 1.12, -0.12), xycoords='data', fontsize=10)
            
        #plot radius OA
        plt.plot((0, 1), (0, 0), color = 'indigo')
        plt.gca().annotate(f'OA = 1', xy=(0, 0.5), xycoords='data', fontsize=10, color = 'indigo')
            
        #plot point B
        plt.plot(cos(theta), sin(theta), marker = 'o', color = 'blue', markersize = 10)

        B_label = [f"B",
                   f"= ( $cos({latex(theta_symb)}), \\; sin({latex(theta_symb)})$ )",
                   f"= $cos({latex(theta_symb)}) + i \\; sin({latex(theta_symb)})$ ",
                   f"= ${latex(sp.exp(sp.I * theta_symb))}$"
                   ]
        B_label = "\n".join(B_label)
        
        plt.gca().annotate(B_label, xy=(cos(theta) * 1.12, sin(theta) +0.12), xycoords='data', fontsize=10)
            
            
        
        #plot radius OB
        plt.plot((0, cos(theta)), (0, sin(theta)), color = 'blue')
            
        #plot point C
        plt.plot(cos(theta), 0, marker = 'o', color = 'green', markersize = 10)

        C_label = [f"C",
                   f"= ( $cos({latex(theta_symb)}), \\; 0 $ )",
                   f"= $cos({latex(theta_symb)}) + i \\; 0 $ ",
                   ]
        C_label = "\n".join(C_label)
        
        plt.gca().annotate(C_label, xy=(cos(theta) * .88,  -0.3), xycoords='data', fontsize=10)
            
        plt.plot((0, cos(theta)), (0, 0), color = 'red')
            
        plt.plot((cos(theta), cos(theta)), (0, sin(theta)), color = 'green')
            
        #optional code
        plt.xlim(-1.2, 1.2)
        plt.ylim(-1.2, 1.2)
        plt.gca().set_aspect('equal')
        plt.axis('off')
        
        svg_str = SvgHelper.GET_SVG_STR(close_plot =True)
        return svg_str
        pass

 

    def plot_sin_cos_func_of_pi_theta(self, *args, **kwargs):
        import matplotlib.pyplot as plt
        import numpy as np
        import sympy as sp
        from numpy import linspace, pi, sin, cos
        from sympy import latex
        import random

        plt.ioff()
        
        fig = plt.figure(figsize=(6, 6))

    
        angles = linspace(0 * pi, 2 * pi, 100 )
            
            
        xs = cos(angles)
        ys = sin(angles)
        #plot center
        plt.plot(0, 0, marker = 'o', color = 'grey', markersize = 10)
        plt.gca().annotate('O (0, 0)', xy=(0-0.20, 0-0.12), xycoords='data', fontsize=10)
            
        #plot circle
        plt.plot(xs, ys, color = 'grey')

        theta = np.pi / 3
        theta_symb = sp.Symbol('theta')

        #annotate theta
        plt.gca().annotate(f'${latex(theta_symb)}$', xy=( +.12, +0.07), xycoords='data', fontsize=10)

        #plot diameter
        plt.plot( (cos(0), cos(np.pi)), (sin(0), sin(np.pi)), color = 'blue')
        
        #plot radius
        plt.plot((0, cos(theta)), (0, sin(theta)), color = 'blue')
            

            
        pt_pairs = [
            (theta, f"{latex(theta_symb)}"),
            (- theta, f"{latex(-theta_symb)}"),
            (np.pi - theta, f"{latex(sp.pi)} - {latex(theta_symb)}"),
            (np.pi + theta, f"{latex(sp.pi)} + {latex(theta_symb)}"),
            ]

        vertex_labels = list('ABCDEFGHIJKLM')[:len(pt_pairs)]
            
        for idx, (pt_angle, angle_ltx) in enumerate(pt_pairs):
            #plot point 
            plt.plot(cos(pt_angle), sin(pt_angle), marker = 'o', markersize = 10)
            
            pt_label = [f"{vertex_labels[idx]}",
                       f"( $cos({angle_ltx}), \\; sin({angle_ltx})$ )",
                       ]
            pt_label = " ".join(pt_label)

            plt.gca().annotate(pt_label, 
                               xy=(cos(pt_angle) * 1.12, sin(pt_angle) +0.12), 
                               xycoords='data', fontsize=10)
            
            pass
            
        #optional code
        plt.xlim(-1.2, 1.2)
        plt.ylim(-1.2, 1.2)
        plt.gca().set_aspect('equal')
        plt.axis('off')
        
        svg_str = SvgHelper.GET_SVG_STR(close_plot =True)
        return svg_str
        pass


    def get_conclusion_sin_cos_func_of_pi_theta(self, *args, **kwargs):
        theta = np.pi / 3
        theta_symb = sp.Symbol('theta')

        pt_pairs = [
            (theta, f"{latex(theta_symb)}"),
            (- theta, f"{latex(-theta_symb)}"),
            (np.pi - theta, f"{latex(sp.pi)} - {latex(theta_symb)}"),
            (np.pi + theta, f"{latex(sp.pi)} + {latex(theta_symb)}"),
            ]

        vertex_labels = list('ABCDEFGHIJKLM')[:len(pt_pairs)]

        soln_stmts = []
        idx0, idx1 = [0, 1] 
        soln_stmt = f"""
        <b>{vertex_labels[idx0]} and {vertex_labels[idx1]}</b>
        The x-values of both points are same but the y-values are opposite in sign. Hence,
        $cos({pt_pairs[idx1][1]}) = cos({pt_pairs[idx0][1]})$
        $sin({pt_pairs[idx1][1]}) = - sin({pt_pairs[idx0][1]})$
        """
        soln_stmts.append(soln_stmt.strip())


        idx0, idx1 = [0, 2] 
        soln_stmt = f"""
        <b>{vertex_labels[idx0]} and {vertex_labels[idx1]}</b>
        The y-values of both points are same but the x-values are opposite in sign. Hence,
        $cos({pt_pairs[idx1][1]}) = - cos({pt_pairs[idx0][1]})$
        $sin({pt_pairs[idx1][1]}) = sin({pt_pairs[idx0][1]})$
        """
        soln_stmts.append(soln_stmt.strip())


        idx0, idx1 = [0, 3] 
        soln_stmt = f"""
        <b>{vertex_labels[idx0]} and {vertex_labels[idx1]}</b>
        The x-values of both points opposite in sign and so are the y-values. Hence,
        $cos({pt_pairs[idx1][1]}) = - cos({pt_pairs[idx0][1]})$
        $sin({pt_pairs[idx1][1]}) = - sin({pt_pairs[idx0][1]})$
        """
        soln_stmts.append(soln_stmt.strip())



        soln_s = "\n\n".join(soln_stmts)
        
        result = {
            'soln_s': soln_s.strip(),
            }
            
        return result
        pass


    def plot_unit_circle_relation_sin_cos_pi_by_2(self, *args, **kwargs):
        import matplotlib.pyplot as plt
        import numpy as np
        import sympy as sp
        from numpy import linspace, pi, sin, cos
        from sympy import latex
        import random

        plt.ioff()
        
        fig = plt.figure(figsize=(6, 6))

    
        angles = linspace(0 * pi, 2 * pi, 100 )
            
            
        xs = cos(angles)
        ys = sin(angles)
        #plot center
        plt.plot(0, 0, marker = 'o', color = 'grey', markersize = 10)
        plt.gca().annotate('O (0, 0)', xy=(0-0.20, 0-0.12), xycoords='data', fontsize=10)
            
        #plot circle
        plt.plot(xs, ys, color = 'grey')
            
            
        theta = np.pi / 5
        
        theta_symb = sp.Symbol('theta')
        
        #annotate theta
        plt.gca().annotate(f'${latex(theta_symb)}$', xy=( +.16, +0.04), xycoords='data', fontsize=10)
            
        #annotate pi/2 - theta
        plt.gca().annotate(f'${latex(sp.pi / 2)}-{latex(theta_symb)}$', 
                           xy=( cos(theta) - 0.20, sin(theta) - 0.24), 
                           xycoords='data', fontsize=10)
            
        
        #plot point A
        plt.plot(1, 0, marker = 'o', color = 'indigo', markersize = 10)
        plt.gca().annotate(f'A (1, 0)', xy=( 1.12, -0.12), xycoords='data', fontsize=10)
            
        #plot radius OB
        plt.plot((0, 1), (0, 0), color = 'indigo')
        plt.gca().annotate(f'OB = 1', xy=(-0.05, 0.25), xycoords='data', fontsize=10, color = 'indigo')
            
        #plot point B
        plt.plot(cos(theta), sin(theta), marker = 'o', color = 'blue', markersize = 10)
        
        B_label = f'''   
        B = ( ${latex(sp.cos(theta_symb))}$, ${latex(sp.sin(theta_symb))}$ )
        = ( $ cos({latex(sp.pi / 2)} - {latex(theta_symb)})$, $ sin({latex(sp.pi / 2)} - {latex(theta_symb)})$)
        '''
            
        
        plt.gca().annotate(B_label, xy=(cos(theta) - 0.05 , sin(theta)-0.05), xycoords='data', fontsize=10)
            
            
        
        #plot radius OB
        plt.plot((0, cos(theta)), (0, sin(theta)), color = 'blue')
            
        #plot point C
        plt.plot(cos(theta), 0, marker = 'o', color = 'green', markersize = 10)
        plt.gca().annotate(f'C', xy=(cos(theta) * .88,  -0.12), xycoords='data', fontsize=10)
            
        #plot line OC
        label = f'''   
        OC = $ cos({latex(theta_symb)})$
        = $ sin({latex(sp.pi / 2)} - {latex(theta_symb)})$
        '''
            
        plt.plot((0, cos(theta)), (0, 0), color = 'red')
        plt.gca().annotate(label, xy=(-0.02,  -0.35), xycoords='data', color = 'red', fontsize=10)
            
        #plot line CB
        label = f'''   
        CB = $ sin({latex(theta_symb)})$
        = $ cos({latex(sp.pi / 2)} - {latex(theta_symb)})$
        '''
            
        plt.plot((cos(theta), cos(theta)), (0, sin(theta)), color = 'green')
        plt.gca().annotate(label, xy=(cos(theta)-0.12,  sin(theta)/6), xycoords='data', color = 'green', fontsize=10)
            
        #optional code
        plt.xlim(-1.2, 1.2)
        plt.ylim(-1.2, 1.2)
        plt.gca().set_aspect('equal')
        plt.axis('off')
        
        svg_str = SvgHelper.GET_SVG_STR(close_plot =True)
        return svg_str
        pass

    
    pass
#class CircleHelper(
