Загрузка данных


// ============================================================================
// БЛОК 1: ПОДКЛЮЧЕНИЕ БИБЛИОТЕК, КОНСТАНТЫ И ГЛОБАЛЬНЫЕ НАСТРОЙКИ
// ============================================================================
#include <windows.h>
#include <iostream>
#include <cmath>

using namespace std;

// Константа Пи для тригонометрических расчетов
const double PI = 3.14159265358979323846;

// Глобальные переменные для передачи настроек из консоли в графическое окно
int g_choice = 1;
int g_iterations = 0;


// ============================================================================
// БЛОК 2: МЕТОДЫ ПОСТРОЕНИЯ СНЕЖИНКИ КОХА
// ============================================================================

// Рекурсивная функция для построения одной линии (стороны) Коха
void drawKochLine(HDC hdc, double x1, double y1, double x2, double y2, int order) {
    if (order == 0) {
        // Базовый случай: итерации закончились, просто рисуем прямую линию
        MoveToEx(hdc, round(x1), round(y1), NULL);
        LineTo(hdc, round(x2), round(y2));
    } else {
        // Вычисляем координаты точек деления отрезка на 3 равные части
        double xA = x1 + (x2 - x1) / 3.0;
        double yA = y1 + (y2 - y1) / 3.0;
        double xB = x1 + 2.0 * (x2 - x1) / 3.0;
        double yB = y1 + 2.0 * (y2 - y1) / 3.0;

        // Вычисляем координаты вершины треугольного выступа (поворот на 60 градусов)
        double angle = 60.0 * PI / 180.0;
        double xC = xA + (xB - xA) * cos(angle) + (yB - yA) * sin(angle);
        double yC = yA - (xB - xA) * sin(angle) + (yB - yA) * cos(angle);

        // Рекурсивный вызов для 4-х новых получившихся мини-отрезков
        drawKochLine(hdc, x1, y1, xA, yA, order - 1);
        drawKochLine(hdc, xA, yA, xC, yC, order - 1);
        drawKochLine(hdc, xC, yC, xB, yB, order - 1);
        drawKochLine(hdc, xB, yB, x2, y2, order - 1);
    }
}

// Функция сборки полноценной снежинки из 3-х базовых сторон
void drawSnowflake(HDC hdc, int order) {
    // Начальные координаты вершин правильного треугольника
    double x1 = 200, y1 = 150;
    double x2 = 600, y2 = 150;
    double x3 = 400, y3 = 150 + 400 * sin(60.0 * PI / 180.0);

    // Запуск процесса для каждой из трех сторон
    drawKochLine(hdc, x1, y1, x2, y2, order);
    drawKochLine(hdc, x2, y2, x3, y3, order);
    drawKochLine(hdc, x3, y3, x1, y1, order);
}


// ============================================================================
// БЛОК 3: МЕТОДЫ ПОСТРОЕНИЯ КРИВОЙ СЕРПИНСКОГО
// ============================================================================

// Вспомогательная функция движения "черепашки" вперед с рисованием линии
void moveForward(HDC hdc, double &x, double &y, double angle, double distance) {
    double x_new = x + distance * cos(angle * PI / 180.0);
    double y_new = y - distance * sin(angle * PI / 180.0); // Минус, так как в Win32 ось Y идет вниз
    
    MoveToEx(hdc, round(x), round(y), NULL);
    LineTo(hdc, round(x_new), round(y_new));
    
    // Обновляем текущее положение
    x = x_new;
    y = y_new;
}

// Рекурсивное построение извилистой кривой Серпинского
void drawSierpinskiCurve(HDC hdc, double &x, double &y, double &angle, int order, double distance, int sign) {
    if (order == 0) {
        moveForward(hdc, x, y, angle, distance);
    } else {
        // Поворот и рекурсивный шаг с инверсией направления (sign), чтобы фрактал не замыкался в спираль
        angle += 60 * sign;
        drawSierpinskiCurve(hdc, x, y, angle, order - 1, distance / 2, -sign);
        
        angle -= 60 * sign;
        drawSierpinskiCurve(hdc, x, y, angle, order - 1, distance / 2, sign);
        
        angle -= 60 * sign;
        drawSierpinskiCurve(hdc, x, y, angle, order - 1, distance / 2, -sign);
        
        angle += 60 * sign;
    }
}


// ============================================================================
// БЛОК 4: ГРАФИЧЕСКИЙ ДВИЖОК WINDOWS (ОБРАБОТКА И ОТРИСОВКА ОКНА)
// ============================================================================

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
        case WM_PAINT: {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);

            // Создаем черное перо для рисования линий толщиной в 1 пиксель
            HPEN hPen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
            SelectObject(hdc, hPen);

            // Отрисовка фрактала в зависимости от выбора пользователя
            if (g_choice == 1) {
                drawSnowflake(hdc, g_iterations);
            } else if (g_choice == 2) {
                // Стартовая позиция, угол и базовый размер для кривой Серпинского
                double x = 200;
                double y = 500;
                double angle = 0;
                double size = 400;

                // Корректировка угла для сохранения правильной ориентации при нечетных итерациях
                if (g_iterations % 2 != 0) {
                    angle += 60;
                    drawSierpinskiCurve(hdc, x, y, angle, g_iterations, size, -1);
                } else {
                    drawSierpinskiCurve(hdc, x, y, angle, g_iterations, size, 1);
                }
            }

            DeleteObject(hPen);
            EndPaint(hwnd, &ps);
            return 0;
        }
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}


// ============================================================================
// БЛОК 5: ГЛАВНЫЙ СИНХРОНИЗИРУЮЩИЙ МОДУЛЬ (ИНТЕРФЕЙС И ЗАПУСК)
// ============================================================================
int main() {
    setlocale(LC_ALL, "Russian");

    // 1. Консольное меню для ввода параметров пользователем
    cout << "=== Программа построения фракталов (Win32) ===" << endl;
    cout << "1. Снежинка Коха" << endl;
    cout << "2. Кривая Серпинского" << endl;
    cout << "Выберите фрактал (1 или 2): ";
    cin >> g_choice;

    if (g_choice != 1 && g_choice != 2) {
        cout << "Ошибка: неверный выбор фрактала!" << endl;
        return 1;
    }

    cout << "Введите количество итераций (рекомендуется от 0 до 5): ";
    cin >> g_iterations;

    if (g_iterations < 0) {
        cout << "Ошибка: число итераций не может быть отрицательным!" << endl;
        return 1;
    }

    cout << "\n[Успешно] Графическое окно запускается. Консоль можно сдвинуть." << endl;

    // 2. Стандартная процедура создания окна Windows приложения
    HINSTANCE hInstance = GetModuleHandle(NULL);
    const wchar_t CLASS_NAME[] = L"FractalWindowClass";

    WNDCLASSW wc = {};
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // Установка белого фона

    RegisterClassW(&wc);

    // Создаем окно размером 800x600 пикселей
    HWND hwnd = CreateWindowExW(
        0, CLASS_NAME, L"Визуализация фракталов (Снежинка Коха / Кривая Серпинского)",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
        NULL, NULL, hInstance, NULL
    );

    if (hwnd == NULL) {
        return 0;
    }

    ShowWindow(hwnd, SW_SHOWNORMAL);

    // 3. Бесконечный цикл обработки системных сообщений окна
    MSG msg = {};
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}