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


#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <unistd.h>

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

#define MAX_FLOWERS       50
#define PETAL_COUNT_MIN   6
#define PETAL_COUNT_MAX   10
#define BASE_PETAL_W      15.0
#define BASE_PETAL_H      45.0
#define BASE_CENTER_R     20
#define SCALE_MIN         0.8
#define SCALE_MAX         1.2
#define SPAWN_INTERVAL_US 500000 
#define MAX_ATTEMPTS      100 

typedef struct {
    int x, y;
    int petals;
    double scale;
    unsigned long color;
    struct timespec birth_time;
    int active;
} Daisy;

static int rand_range(int min, int max) {
    if (min == max) return min;
    return min + rand() % (max - min + 1);
}

// Вычисляет полный радиус цветка (от центра до кончика лепестка)
static double get_flower_radius(Daisy *d) {
    return (BASE_PETAL_H + BASE_CENTER_R) * d->scale;
}

// Проверка: можно ли поставить цветок new_d в текущий набор daisies
static int is_position_valid(Daisy *new_d, Daisy *daisies, int win_w, int win_h) {
    double r_new = get_flower_radius(new_d);

    // 1. Строгая проверка границ экрана
    // Цветок должен целиком помещаться внутри [0, win_w] и [0, win_h]
    if (new_d->x - r_new < 0 || new_d->x + r_new > win_w ||
        new_d->y - r_new < 0 || new_d->y + r_new > win_h) {
        return 0;
    }

    // 2. Проверка пересечений с другими активными цветами
    for (int i = 0; i < MAX_FLOWERS; i++) {
        if (!daisies[i].active) continue;
        
        // Если это тот же самый элемент (при замене), пропускаем
        if (new_d == &daisies[i]) continue;

        double r_other = get_flower_radius(&daisies[i]);
        double dx = (double)(new_d->x - daisies[i].x);
        double dy = (double)(new_d->y - daisies[i].y);
        double dist_sq = dx * dx + dy * dy;
        
        // Минимально допустимое расстояние = сумма радиусов
        double min_dist = r_new + r_other;
        
        // Если квадраты расстояний меньше квадрата мин. дистанции -> пересечение
        if (dist_sq < min_dist * min_dist) {
            return 0; 
        }
    }
    return 1;
}

void draw_scene(Display *dpy, Window win, GC gc, Daisy *daisies, unsigned long white, unsigned long yellow, int win_w, int win_h) {
    XSetForeground(dpy, gc, white);
    XFillRectangle(dpy, win, gc, 0, 0, win_w, win_h);

    const int PTS_COUNT = 32;
    XPoint *pts = malloc(PTS_COUNT * sizeof(XPoint));
    if (!pts) return;

    for (int f = 0; f < MAX_FLOWERS; f++) {
        if (!daisies[f].active) continue;
        Daisy *d = &daisies[f];
        double s = d->scale;

        double p_w = BASE_PETAL_W * s;
        double p_h = BASE_PETAL_H * s;
        // Расстояние от центра цветка до центра лепестка
        double dist = (BASE_PETAL_H * 0.5) * s; 
        int c_r = (int)(BASE_CENTER_R * s);

        XSetForeground(dpy, gc, d->color);
        for (int i = 0; i < d->petals; i++) {
            double angle = (2.0 * M_PI * i) / d->petals;
            double cos_a = cos(angle);
            double sin_a = sin(angle);

            for (int p = 0; p < PTS_COUNT; p++) {
                double t = (2.0 * M_PI * p) / PTS_COUNT;
                // Эллипс лепестка в локальных координатах
                double lx = p_h * cos(t);
                double ly = p_w * sin(t);
                
                // Поворот лепестка по направлению от центра
                double rx = lx * cos_a - ly * sin_a;
                double ry = lx * sin_a + ly * cos_a;
                
                // Перенос в позицию цветка
                pts[p].x = (short)(d->x + rx + dist * cos_a);
                pts[p].y = (short)(d->y + ry + dist * sin_a);
            }
            XFillPolygon(dpy, win, gc, pts, PTS_COUNT, Convex, CoordModeOrigin);
        }

        // Рисуем центр поверх лепестков
        XSetForeground(dpy, gc, yellow);
        XFillArc(dpy, win, gc, d->x - c_r, d->y - c_r,
                 c_r * 2, c_r * 2, 0, 360 * 64);
    }
    free(pts);
}

int main() {
    srand((unsigned)time(NULL));

    Display *dpy = XOpenDisplay(NULL);
    if (!dpy) { fprintf(stderr, "Error\n"); return 1; }

    int screen = DefaultScreen(dpy);
    Window root = RootWindow(dpy, screen);
    int win_w = 800, win_h = 600;

    Window win = XCreateSimpleWindow(dpy, root, 50, 50, win_w, win_h, 1,
                                     BlackPixel(dpy, screen), WhitePixel(dpy, screen));
    
    XSelectInput(dpy, win, ExposureMask | KeyPressMask | StructureNotifyMask);
    XMapWindow(dpy, win);

    GC gc = XCreateGC(dpy, win, 0, NULL);
    XSetFillStyle(dpy, gc, FillSolid);

    Colormap cmap = DefaultColormap(dpy, screen);
    
    const char *colors[] = {
        "skyblue", "cyan", "lightblue", "powderblue", 
        "cadetblue", "steelblue", "dodgerblue", "deepskyblue",
        "cornflowerblue", "mediumturquoise"
    };
    int num_colors = sizeof(colors) / sizeof(colors[0]);
    unsigned long pixel_colors[20];
    
    for(int i=0; i<num_colors; i++) {
        XColor xc;
        XAllocNamedColor(dpy, cmap, colors[i], &xc, &xc);
        pixel_colors[i] = xc.pixel;
    }
    
    XColor xc_white, xc_yellow;
    XAllocNamedColor(dpy, cmap, "white", &xc_white, &xc_white);
    XAllocNamedColor(dpy, cmap, "yellow", &xc_yellow, &xc_yellow);
    unsigned long white = xc_white.pixel;
    unsigned long yellow = xc_yellow.pixel;

    Atom wmDelete = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
    XSetWMProtocols(dpy, win, &wmDelete, 1);

    Daisy daisies[MAX_FLOWERS];
    memset(daisies, 0, sizeof(daisies));
    
    struct timespec last_spawn;
    clock_gettime(CLOCK_MONOTONIC, &last_spawn);

    XEvent ev;
    int running = 1;
    
    draw_scene(dpy, win, gc, daisies, white, yellow, win_w, win_h);

    while (running) {
        if (XCheckWindowEvent(dpy, win, ExposureMask | KeyPressMask | StructureNotifyMask, &ev)) {
            switch (ev.type) {
                case Expose:
                    draw_scene(dpy, win, gc, daisies, white, yellow, win_w, win_h);
                    break;
                case KeyPress:
                    running = 0;
                    break;
                case ClientMessage:
                    if (ev.xclient.data.l[0] == wmDelete) running = 0;
                    break;
            }
        } else {
            struct timespec now;
            clock_gettime(CLOCK_MONOTONIC, &now);
            
            if (time_diff_us(&last_spawn, &now) >= SPAWN_INTERVAL_US) {
                Daisy candidate;
                int success = 0;

                // Попытка найти свободное место в случайных координатах
                for (int k = 0; k < MAX_ATTEMPTS; k++) {
                    candidate.x = rand_range(50, win_w - 50);
                    candidate.y = rand_range(50, win_h - 50);
                    candidate.petals = rand_range(PETAL_COUNT_MIN, PETAL_COUNT_MAX);
                    candidate.scale = SCALE_MIN + ((double)rand() / RAND_MAX) * (SCALE_MAX - SCALE_MIN);
                    candidate.color = pixel_colors[rand() % num_colors];
                    candidate.birth_time = now;
                    
                    if (is_position_valid(&candidate, daisies, win_w, win_h)) {
                        success = 1;
                        break;
                    }
                }

                if (success) {
                    // Ищем любой свободный слот в массиве
                    int slot = -1;
                    for(int i=0; i<MAX_FLOWERS; i++) {
                        if(!daisies[i].active) {
                            slot = i;
                            break;
                        }
                    }
                    
                    // Если свободных слотов нет, но место на экране есть (редкость, но бывает)
                    // Удаляем самый старый, чтобы освободить слот
                    if (slot == -1) {
                         struct timespec oldest = daisies[0].birth_time;
                         slot = 0;
                         for(int i=1; i<MAX_FLOWERS; i++) {
                             if (time_diff_us(&daisies[i].birth_time, &oldest) > 0) {
                                 oldest = daisies[i].birth_time;
                                 slot = i;
                             }
                         }
                    }
                    
                    daisies[slot] = candidate;
                    daisies[slot].active = 1;
                } else {
                    // НЕ УДАЛОСЬ НАЙТИ МЕСТО. Экран переполнен.
                    // Стратегия: Удаляем самый старый цветок и ставим новый.
                    
                    int oldest_idx = -1;
                    struct timespec oldest_time = {0, 0};
                    int first = 1;

                    for(int i=0; i<MAX_FLOWERS; i++) {
                        if(daisies[i].active) {
                            if (first) {
                                oldest_time = daisies[i].birth_time;
                                oldest_idx = i;
                                first = 0;
                            } else {
                                // Ищем самый старый (наименьшее время)
                                // time_diff_us(A, B) > 0 значит B новее A. 
                                // Нам нужно найти минимальное birth_time.
                                if (time_diff_us(&daisies[i].birth_time, &oldest_time) < 0) {
                                    oldest_time = daisies[i].birth_time;
                                    oldest_idx = i;
                                }
                            }
                        }
                    }

                    if (oldest_idx != -1) {
                        // Генерируем нового кандидата
                        // Пытаемся поставить его примерно туда же, где был старый, 
                        // или случайно. Так как старый удален, место свободно от него.
                        
                        // Для надежности генерируем заново и проверяем относительно ОСТАЛЬНЫХ
                        int replace_success = 0;
                        for(int k=0; k<MAX_ATTEMPTS; k++) {
                             candidate.x = rand_range(50, win_w - 50);
                             candidate.y = rand_range(50, win_h - 50);
                             candidate.petals = rand_range(PETAL_COUNT_MIN, PETAL_COUNT_MAX);
                             candidate.scale = SCALE_MIN + ((double)rand() / RAND_MAX) * (SCALE_MAX - SCALE_MIN);
                             candidate.color = pixel_colors[rand() % num_colors];
                             candidate.birth_time = now;
                             
                             // Временно помечаем старый как неактивный для проверки
                             daisies[oldest_idx].active = 0;
                             int valid = is_position_valid(&candidate, daisies, win_w, win_h);
                             daisies[oldest_idx].active = 1; // возвращаем пока
                             
                             if (valid) {
                                 replace_success = 1;
                                 break;
                             }
                        }
                        
                        if (replace_success) {
                            daisies[oldest_idx] = candidate;
                            daisies[oldest_idx].active = 1;
                        } else {
                            // Если совсем никак (очень плотная упаковка), 
                            // просто удаляем старый, чтобы хоть немного освободить место
                            daisies[oldest_idx].active = 0;
                        }
                    }
                }
                
                draw_scene(dpy, win, gc, daisies, white, yellow, win_w, win_h);
                XFlush(dpy);
                last_spawn = now;
            }
            usleep(10000); 
        }
    }

    XFreeGC(dpy, gc);
    XDestroyWindow(dpy, win);
    XCloseDisplay(dpy);
    return 0;
}