import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle

def generate_figure_eight(left, right, top, bottom, num_points=1000):
    """
    Генерирует эллиптическую восьмерку в заданном прямоугольнике.
    Использует sin^3 для сглаживания прямых участков.
    
    Параметры:
    - left, right, top, bottom: границы в градусах
    - num_points: количество точек для генерации
    
    Возвращает список кортежей (x, y) в градусах
    """
    # Центр прямоугольника (это наш 0,0)
    center_x = (left + right) / 2
    center_y = (top + bottom) / 2
    
    # Размеры
    width = right - left
    height = top - bottom
    
    # Генерируем параметр t от 0 до 2π
    t = np.linspace(0, 2 * np.pi, num_points)
    
    # Эллиптическая восьмерка с sin^3 для сглаживания
    # Используем степенные функции чтобы избежать долгих прямых участков
    sin_t = np.sin(t)
    sin_2t = np.sin(2 * t)
    
    # Применяем степенную функцию для сглаживания
    x = center_x + (width / 2) * np.sign(sin_t) * np.abs(sin_t) ** 1.5
    y = center_y + (height / 2) * np.sign(sin_2t) * np.abs(sin_2t) ** 1.5
    
    # Округляем до целых градусов (минимальный шаг крутилки)
    x_rounded = np.round(x).astype(int)
    y_rounded = np.round(y).astype(int)
    
    # Формируем список точек
    points = list(zip(x_rounded, y_rounded))
    
    # Убираем последовательные дубликаты (чтобы не стоять на месте)
    unique_points = [points[0]]
    for point in points[1:]:
        if point != unique_points[-1]:
            unique_points.append(point)
    
    # Добавляем возврат в начальную точку (0, 0 относительно центра)
    start_point = (int(round(center_x)), int(round(center_y)))
    if unique_points[-1] != start_point:
        unique_points.append(start_point)
    
    return unique_points, center_x, center_y

def draw_figure_eight(left, right, top, bottom, pixels_per_degree=10):
    """
    Рисует эллиптическую восьмерку на картинке.
    
    Параметры:
    - left, right, top, bottom: границы в градусах
    - pixels_per_degree: масштаб (градусы -> пиксели)
    """
    # Генерируем точки
    points, center_x, center_y = generate_figure_eight(left, right, top, bottom)
    
    # Размеры картинки в пикселях
    width_pixels = (right - left) * pixels_per_degree
    height_pixels = (top - bottom) * pixels_per_degree
    
    # Создаем фигуру
    fig, ax = plt.subplots(figsize=(10, 8))
    
    # Преобразуем координаты из градусов в пиксели для отображения
    x_pixels = [(x - left) * pixels_per_degree for x, y in points]
    y_pixels = [(top - y) * pixels_per_degree for x, y in points]  # Инвертируем Y для правильного отображения
    
    # Рисуем прямоугольник границ
    rect = Rectangle((0, 0), width_pixels, height_pixels, 
                     linewidth=2, edgecolor='blue', facecolor='none', label='Границы')
    ax.add_patch(rect)
    
    # Рисуем траекторию (линии между точками)
    ax.plot(x_pixels, y_pixels, 'r-', linewidth=1.5, label='Траектория', alpha=0.7)
    
    # Рисуем точки
    ax.plot(x_pixels, y_pixels, 'go', markersize=2, label='Точки (шаг 1°)', alpha=0.5)
    
    # Отмечаем начальную точку (центр = 0)
    center_x_pixel = (center_x - left) * pixels_per_degree
    center_y_pixel = (top - center_y) * pixels_per_degree
    ax.plot(center_x_pixel, center_y_pixel, 'b*', markersize=15, label='Старт/Финиш (0,0)')
    
    # Настройки осей
    ax.set_xlim(-10, width_pixels + 10)
    ax.set_ylim(-10, height_pixels + 10)
    ax.set_aspect('equal')
    ax.grid(True, alpha=0.3)
    ax.legend()
    ax.set_title(f'Эллиптическая восьмерка: [{left}°, {right}°] x [{bottom}°, {top}°]\nВсего точек: {len(points)}')
    ax.set_xlabel('Пиксели (→)')
    ax.set_ylabel('Пиксели (↓)')
    
    plt.tight_layout()
    plt.savefig('figure_eight.png', dpi=150)
    plt.show()
    
    return points

def test_consistency(left, right, top, bottom, num_iterations=10):
    """
    Проверяет, что траектория идентична при повторных запусках.
    """
    print(f"\n{'='*60}")
    print(f"Тест консистентности для прямоугольника:")
    print(f"Горизонталь: [{left}°, {right}°] (ширина: {right-left}°)")
    print(f"Вертикаль: [{bottom}°, {top}°] (высота: {top-bottom}°)")
    print(f"{'='*60}\n")
    
    all_trajectories = []
    
    for i in range(num_iterations):
        points, _, _ = generate_figure_eight(left, right, top, bottom)
        all_trajectories.append(points)
        
        if i == 0:
            print(f"Итерация 1 - Первые 10 точек (x, y в градусах):")
            for j, (x, y) in enumerate(points[:10]):
                print(f"  Точка {j+1}: ({x:3d}, {y:3d})")
            print(f"  ... всего {len(points)} точек\n")
            print(f"Последние 5 точек:")
            for j, (x, y) in enumerate(points[-5:], start=len(points)-4):
                print(f"  Точка {j}: ({x:3d}, {y:3d})")
    
    # Проверяем идентичность
    all_identical = all(trajectory == all_trajectories[0] for trajectory in all_trajectories)
    
    print(f"\n{'='*60}")
    if all_identical:
        print(f"✓ УСПЕХ: Все {num_iterations} итераций дали ИДЕНТИЧНЫЕ траектории!")
        print(f"  Количество точек в траектории: {len(all_trajectories[0])}")
    else:
        print(f"✗ ОШИБКА: Траектории различаются!")
        for i, traj in enumerate(all_trajectories):
            print(f"  Итерация {i+1}: {len(traj)} точек")
    print(f"{'='*60}\n")
    
    return all_identical

# Пример использования
if __name__ == "__main__":
    # Задаем прямоугольник (в градусах)
    left = -40
    right = 40
    top = 70
    bottom = -70
    
    # Тест консистентности (10 итераций)
    is_consistent = test_consistency(left, right, top, bottom, num_iterations=10)
    
    # Рисуем картинку
    print("\nГенерирую картинку...")
    points = draw_figure_eight(left, right, top, bottom, pixels_per_degree=10)
    
    # Выводим полную траекторию
    print("\nПолная траектория (все точки):")
    for i, (x, y) in enumerate(points, start=1):
        print(f"{i:3d}. ({x:3d}, {y:3d})")
    
    print(f"\nКартинка сохранена как 'figure_eight.png'")