Введение
В этом проекте вы научитесь создавать завораживающую динамическую анимацию сердца с использованием языка программирования C. Проект использует X Window System для рендеринга анимированных визуалов. Следуя пошаговым инструкциям, вы настроите проект, сгенерируете данные и создадите увлекательную анимацию, которая оживит динамическое сердце на вашем экране.
👀 Предпросмотр

🎯 Задачи
В этом проекте вы научитесь:
- Настраивать проект на языке программирования C для создания динамической анимации сердца
- Использовать библиотеки X Window System для создания и управления графическими окнами
- Генерировать случайные точки и анимировать их для формирования фигуры сердца
- Управлять анимацией, чтобы она расширялась и сжималась, создавая завораживающий визуальный эффект
🏆 Достижения
После завершения этого проекта вы сможете:
- Использовать библиотеки X Window System для графического программирования на языке C
- Генерировать и манипулировать случайными точками на языке C
- Создавать динамическую анимацию, используя сочетание методов генерации данных и рендеринга
Создание файлов проекта
Убедитесь, что у вас установлены необходимые библиотеки. Вам понадобятся разработческие библиотеки X11. Вы можете установить их с помощью следующей команды:
sudo apt update
sudo apt-get install libx11-dev
Затем создайте новый файл с именем dynamic_heart.c и откройте его в вашем предпочитаемом текстовом редакторе кода.
cd ~/project
touch dynamic_heart.c
Определение необходимых переменных
Сначала нам нужно написать код на языке C. Первым шагом является включение заголовочных файлов:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <stdbool.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <unistd.h>
Определите структуру для хранения информации о точке, включая ее координаты x, y и цвет. Используется для представления точек на графике.
struct Point {
double x, y;
unsigned long color;
};
Затем определите некоторые глобальные переменные:
unsigned long colors[7] = {0xff1f53, 0xfcdefa, 0xff0000, 0xff0000, 0xff0202, 0xff0008, 0xff0505};
const int xScreen = 1200;
const int yScreen = 800;
const double PI = 3.1426535159;
const double e = 2.71828;
const double average_distance = 0.162;
const int quantity = 506;
const int circles = 210;
const int frames = 20;
struct Point* origin_points;
struct Point* points;
Используйте переменные, связанные с X Window System, для создания и управления графическими окнами и графическими контекстами.
Display *display;
Window win;
GC gc;
display представляет соединение с X-сервером, win представляет окно, а gc представляет графический контекст, который используется для рисования графических элементов.
Реализация функций для экранных координат
double screen_x(double x) {
x += xScreen / 2;
return x;
}
double screen_y(double y) {
y = -y + yScreen / 2;
return y;
}
Цель функций screen_x и screen_y состоит в том, чтобы отобразить фактические координаты на координаты экрана, чтобы обеспечить, чтобы графические элементы в анимации находились в видимой области экрана и отображались в правильном положении. Конкретнее:
screen_x:
- Эта функция принимает фактическое значение координаты x в качестве аргумента и затем корректирует его, чтобы оно было правильно нарисовано на экране. Она прибавляет входное значение координаты x к половине
xScreen, так что оно будет находиться в горизонтальном центре экрана, так какxScreenпредставляет ширину экрана, деленная на2для нахождения горизонтального центра экрана. - Функция возвращает скорректированное значение координаты x для рисования точек или фигур на экране.
screen_y:
- Эта функция аналогична
screen_x, но обрабатывает координаты y. Она принимает фактическое значение координаты y в качестве параметра и преобразует его в систему координат экрана. Сначала она инвертирует координату y, чтобы начало координатной системы находилось в верхнем левом углу экрана, затем прибавляет ее к половинеyScreen, чтобы координата находилась в вертикальном центре экрана. - Функция возвращает скорректированное значение координаты y, чтобы точка или фигура была правильно нарисована на экране.
Реализация функции генератора случайных чисел
int create_random(int x1, int x2) {
if (x2 > x1) {
return rand() % (x2 - x1 + 1) + x1;
}
return 0;
}
Это функция, используемая для генерации случайных целых чисел в заданном диапазоне. Она применяется для случайного определения цвета и положения сгенерированных точек, чтобы повысить визуальную разнообразие анимации.
Здесь x1 и x2 - это два целых числа, передаваемые в качестве аргументов. Функция возвращает случайное целое число между x1 и x2, включая x1 и x2. Если x2 больше x1, то с помощью функции rand() генерируется случайное целое число, которое с использованием операций по модулю ограничивается допустимым диапазоном. Функция rand() обычно возвращает случайное целое число между 0 и RAND_MAX (обычно 32767). Наконец, к результату операции по модулю прибавляется x1, чтобы гарантировать, что случайное целое число попадает в заданный диапазон.
Инициализация и генерация наборов точек в форме сердца
Функция create_data генерирует данные для анимации. Она вычисляет точки для формы сердца и анимирует их на основе определенного алгоритма.
void create_data() {
int index = 0;
double x1 = 0, y1 = 0, x2 = 0, y2 = 0;
for (double radian = 0.1; radian <= 2 * PI; radian += 0.005) {
// Calculate the x and y coordinates of the heart
x2 = 16 * pow(sin(radian), 3);
y2 = 13 * cos(radian) - 5 * cos(2 * radian) - 2 * cos(3 * radian) - cos(4 * radian);
double distance = sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2));
if (distance > average_distance) {
// Qualifying heart points are stored
x1 = x2;
y1 = y2;
origin_points[index].x = x2;
origin_points[index++].y = y2;
}
}
Используется цикл для перебора серии значений радиан (от
0.1до2 * PI) и вычисления координат x и y каждой точки.Вычисляется расстояние между двумя точками, и если расстояние больше
average_distance, точка сохраняется в массивеorigin_points.
Цветовое кодирование точек для создания анимации
// Inside the create_data() function
index = 0;
for (double size = 0.1, lightness = 1.5; size <= 20; size += 0.1) {
double success_p = 1 / (1 + pow(e, 8 - size / 2));
if (lightness > 1) {
lightness -= 0.0025;
}
for (int i = 0; i < quantity; ++i) {
if (success_p > (double)create_random(0, 100) / 100.0) {
// Randomly generate the color of the points, coordinates, and store them in the Points array
points[index].color = colors[create_random(0, 6)];
points[index].x = size * origin_points[i].x + create_random(-4, 4);
points[index++].y = size * origin_points[i].y + create_random(-4, 4);
}
}
}
Используются два вложенных цикла. Внешний цикл постепенно увеличивает значение
size, а внутренний цикл обрабатывает каждую изquantityточек.С вероятностью
success_pрешается, нужно ли сгенерировать точку и сохранить цвет и координаты точки в массивеpoints.
Генерация и обновление анимации
// Inside the create_data() function
int points_size = index;
for (int frame = 0; frame < frames; ++frame) {
for (index = 0; index < points_size; ++index) {
// The position of the calculated point is increased and the coordinates are updated
double x = points[index].x, y = points[index].y;
double distance = sqrt(pow(x, 2) + pow(y, 2));
double distance_increase = -0.0009 * distance * distance + 0.35714 * distance + 5;
double x_increase = distance_increase * x / distance / frames;
double y_increase = distance_increase * y / distance / frames;
points[index].x += x_increase;
points[index].y += y_increase;
// Draw points using XSetForeground and XFillArc
XSetForeground(display, gc, points[index].color);
XFillArc(display, win, gc, screen_x(points[index].x), screen_y(points[index].y), 2, 2, 0, 360 * 64);
}
for (double size = 17; size < 23; size += 0.3) {
for (index = 0; index < quantity; ++index) {
// Randomly generate the coordinates and color of the point according to the condition, and draw the point using XSetForeground and XFillArc
if ((create_random(0, 100) / 100.0 > 0.6 && size >= 20) || (size < 20 && (double)create_random(0, 100) / 100.0 > 0.95)) {
double x, y;
if (size >= 20) {
x = origin_points[index].x * size + create_random(-frame * frame / 5 - 15, frame * frame / 5 + 15);
y = origin_points[index].y * size + create_random(-frame * frame / 5 - 15, frame * frame / 5 + 15);
} else {
x = origin_points[index].x * size + create_random(-5, 5);
y = origin_points[index].y * size + create_random(-5, 5);
}
XSetForeground(display, gc, colors[create_random(0, 6)]);
XFillArc(display, win, gc, screen_x(x), screen_y(y), 2, 2, 0, 360 * 64);
}
}
}
}
points_sizeиспользуется для получения количества точек в текущем кадре анимации, которое вычисляется на основе предыдущего раздела кода.index- это количество ранее сгенерированных точек.Внешний цикл
for (int frame = 0; frame < frames; ++frame)используется для перебора каждого кадра анимации, аframesопределяет общее количество кадров.Внутренний цикл
for (index = 0; index < points_size; ++index)используется для обработки каждой точки в текущем кадре. В каждом кадре выполняются следующие действия:
- Сначала вычисляется новая позиция каждой точки. Это делается по следующей формуле:
double x = points[index].x, y = points[index].y;
double distance = sqrt(pow(x, 2) + pow(y, 2));
double distance_increase = -0.0009 * distance * distance + 0.35714 * distance + 5;
double x_increase = distance_increase * x / distance / frames;
double y_increase = distance_increase * y / distance / frames;
points[index].x += x_increase;
points[index].y += y_increase;
Эти вычисления используются для обновления координат x и y точки, чтобы достичь движения точки в анимации. distance_increase контролирует скорость движения точки, которая изменяется в зависимости от расстояния от исходной позиции точки.
- Точки рисуются с использованием функций
XSetForegroundиXFillArc. Это рисует точку на экране,XSetForegroundиспользуется для установки цвета рисования,XFillArc- для рисования заполненной точки, а координаты центра круга преобразуются функциямиscreen_xиscreen_y.
- Второй раздел внутреннего цикла
for (double size = 17; size < 23; size += 0.3)используется для генерации дополнительных точек в текущем кадре. В этом цикле каждая точка генерируется, окрашивается и рисуется на экране.
- Координаты и цвета новых точек генерируются случайным образом в соответствии с следующими условиями:
Если
size >= 20и случайное число больше0.6, илиsize < 20и случайное число больше0.95, то генерируется новая точка.
- Координаты x и y сгенерированных точек вычисляются на основе положения исходной точки и некоторых случайных смещений.
double x, y;
if (size >= 20) {
x = origin_points[index].x * size + create_random(-frame * frame / 5 - 15, frame * frame / 5 + 15);
y = origin_points[index].y * size + create_random(-frame * frame / 5 - 15, frame * frame / 5 + 15);
} else {
x = origin_points[index].x * size + create_random(-5, 5);
y = origin_points[index].y * size + create_random(-5, 5);
}
- Наконец, используются функции
XSetForegroundиXFillArcдля рисования сгенерированной точки на экране, как и в случае с предыдущей точкой.
Создание и инициализация X-окна
Основная цель функции main заключается в создании окна X, инициализации данных и последующей генерации и отрисовке каждого кадра анимации в форме сердца в бесконечном цикле.
int main() {
display = XOpenDisplay(NULL);
int blackColor = BlackPixel(display, DefaultScreen(display));
win = XCreateSimpleWindow(display, RootWindow(display, DefaultScreen(display)), 0, 0, xScreen, yScreen, 0, blackColor, blackColor);
XSelectInput(display, win, StructureNotifyMask);
XMapWindow(display, win);
gc = XCreateGC(display, win, 0, NULL);
}
display: Открывает соединение с сервером X.blackColor: Получает значение черного пикселя для отображения.win: Создает окно и задает его свойства, местоположение, размер и цвет границы.XSelectInput: Задает маску входных событий для окна.XMapWindow: Отображает окно на экране.gc: Создает графический контекст (Graphics Context).
Инициализация данных и создание групп точек
// Inside the main() function
while (1) {
XEvent e;
XNextEvent(display, &e);
if (e.type == MapNotify)
break;
}
XFlush(display);
srand(time(NULL));
origin_points = (struct Point*)malloc(quantity * sizeof(struct Point));
points = (struct Point*)malloc(circles * quantity * sizeof(struct Point));
create_data();
while (1)представляет собой бесконечный цикл, который ожидает завершения отображения (mapping) окна X. Это фаза инициализации перед отображением окна на экране.
- Определяется структура
XEventдля приема событий X. XNextEvent: Ожидает и получает следующее событие X, а затем сохраняет его в переменнойe.- Используется оператор
ifдля проверки, является ли тип событияMapNotify, что означает, что окно было успешно отображено на экране. Если отображение окна завершено (то есть тип события -MapNotify).
XFlush: Очищает выходной буфер сервера X, чтобы гарантировать, что предыдущие команды рисования вступят в силу и не будут отложены до последующего рисования анимации.srand: Инициализирует генератор случайных чисел. Используется текущее время в качестве семени для генератора случайных чисел, чтобы создать случайные эффекты в анимации.origin_points: Выделяет память и создает массив изquantityструктурPoint, которые будут использоваться для хранения исходных координат точек.points: Опять выделяет память и создает более крупный массив для хранения точек в анимации.circlesконтролирует количество точек в анимации и является константным значением.Наконец, вызывается функция
create_data()для инициализации данных, генерации и установки координат исходных точек, а также инициализации цвета и начальных координат анимированных точек.
Основной цикл анимации
Цель этого главного цикла - контролировать состояние сердцеобразного паттерна путем изменения значения frame, чтобы обеспечить эффект расширения и сжатия анимации. Функция usleep используется для контроля частоты кадров, чтобы анимация воспроизводилась с определенной скоростью.
// Inside the main() function
bool extend = true, shrink = false;
for (int frame = 0;;) {
usleep(20000);
XClearWindow(display, win);
if (extend)
frame == 19? (shrink = true, extend = false) : ++frame;
else
frame == 0? (shrink = false, extend = true) : --frame;
}
Сначала определяются два булевых переменных, extend и shrink, и инициализируются значениями true и false соответственно, которые представляют состояния расширения и сжатия сердцеобразного паттерна.
Затем запускается бесконечный цикл, и переменная frame в цикле используется для отслеживания номера текущего кадра анимации.
usleepиспользуется для контроля скорости анимации.Весь контент окна очищается с помощью вызова функции X11
XClearWindow, чтобы можно было перерисовать его на следующем кадре.Значение
frameувеличивается или уменьшается в зависимости от значенийextendиshrink, чтобы обеспечить эффект расширения и сжатия анимации. Для установки булевых значений используется условный оператор? :.
Если
extendравноtrue, проверяется, равно лиframeзначению19. Если это так, анимация должна перейти из состояния расширения в состояние сжатия, поэтомуshrinkустанавливается вtrue, аextend- вfalse. В противном случаеframeувеличивается.
Если
extendравноfalse, проверяется, равно лиframeзначению0. Если это так, анимация должна перейти из состояния сжатия в состояние расширения, поэтомуshrinkустанавливается вfalse, аextend- вtrue. В противном случаеframeуменьшается.
В конце освобождаются ресурсы и программа завершается.
// Inside the main() function
XCloseDisplay(display);
free(origin_points);
free(points);
return 0;
Компиляция и запуск проекта
- Откройте терминал и перейдите в директорию проекта.
cd ~/project
- Скомпилируйте код с помощью следующей команды:
gcc -o dynamic_heart dynamic_heart.c -lX11 -lm
- Запустите приложение:
./dynamic_heart

Резюме
В этом проекте вы научились создавать увлекательную динамическую анимацию сердца с использованием языка программирования C. Вы настроили проект, определили переменные, реализовали функции для работы с экранными координатами, функцию генератора случайных чисел и функции генерации данных. Программа использует систему окон X (X Window System) для рендеринга анимированного сердца. Наконец, вы создали цикл анимации, закрыли дисплей и освободили выделенную память. Теперь вы можете наслаждаться своей собственной динамической анимацией сердца, созданной на языке C.



