Introducción
En este proyecto, aprenderás cómo crear una fascinante animación dinámica de un corazón utilizando el lenguaje de programación C. El proyecto utiliza el Sistema de Ventanas X para renderizar visuales animados. Siguiendo las instrucciones paso a paso, configurarás el proyecto, generarás datos y crearás una animación cautivadora que dará vida a un corazón dinámico en tu pantalla.
👀 Vista previa

🎯 Tareas
En este proyecto, aprenderás:
- Cómo configurar un proyecto de programación en C para crear una animación dinámica de un corazón
- Cómo utilizar las bibliotecas del Sistema de Ventanas X para crear y gestionar ventanas gráficas
- Cómo generar puntos aleatorios y animarlos para formar la forma de un corazón
- Cómo controlar la animación para que se expanda y contraiga, creando un efecto visual cautivador
🏆 Logros
Después de completar este proyecto, podrás:
- Utilizar las bibliotecas del Sistema de Ventanas X para la programación gráfica en C
- Generar y manipular puntos aleatorios en C
- Crear una animación dinámica combinando técnicas de generación de datos y renderizado
Crear los archivos del proyecto
Asegúrate de tener instaladas las bibliotecas necesarias. Necesitarás las bibliotecas de desarrollo de X11. Puedes instalarlas utilizando el siguiente comando:
sudo apt update
sudo apt-get install libx11-dev
Luego, crea un nuevo archivo llamado dynamic_heart.c y ábrelo en tu editor de código preferido.
cd ~/project
touch dynamic_heart.c
Definir variables necesarias
Primero, necesitamos escribir el código en C. El primer paso es incluir los archivos de encabezado:
#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>
Define la estructura para almacenar la información de un punto, incluyendo su coordenada x, coordenada y y color. Se utiliza para representar puntos en un gráfico.
struct Point {
double x, y;
unsigned long color;
};
Luego, define algunas variables globales:
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;
Utiliza variables relacionadas con el Sistema de Ventanas X para crear y gestionar ventanas gráficas y contextos gráficos.
Display *display;
Window win;
GC gc;
display representa la conexión al servidor X, win representa la ventana y gc representa el contexto gráfico, que se utiliza para dibujar los elementos gráficos.
Implementar funciones de coordenadas de pantalla
double screen_x(double x) {
x += xScreen / 2;
return x;
}
double screen_y(double y) {
y = -y + yScreen / 2;
return y;
}
El propósito de las funciones screen_x y screen_y es mapear las coordenadas reales a las coordenadas de la pantalla para garantizar que los elementos gráficos de la animación estén dentro del área visible de la pantalla y se muestren en la posición correcta. Para ser más específicos:
screen_x:
- Esta función toma un valor de coordenada x real como argumento y luego lo ajusta para que se dibuje correctamente en la pantalla. Suma el valor de la coordenada x de entrada a la mitad de
xScreen, de modo que estará en el centro horizontal de la pantalla, ya quexScreenrepresenta el ancho de la pantalla, dividido por2para encontrar el centro horizontal de la pantalla. - La función devuelve el valor de la coordenada x ajustado para dibujar puntos o formas en la pantalla.
screen_y:
- Esta función es similar a
screen_x, pero se ocupa de las coordenadas y. Toma el valor real de la coordenada y como parámetro y lo convierte al sistema de coordenadas de la pantalla. Primero, invierte la coordenada y para que el origen del sistema de coordenadas esté en la esquina superior izquierda de la pantalla, luego la suma a la mitad deyScreenpara que la coordenada esté en el centro vertical de la pantalla. - La función devuelve el valor de la coordenada y ajustado para que un punto o forma se dibuje correctamente en la pantalla.
Implementar la función de generador de números aleatorios
int create_random(int x1, int x2) {
if (x2 > x1) {
return rand() % (x2 - x1 + 1) + x1;
}
return 0;
}
Esta es una función utilizada para generar enteros aleatorios dentro de un rango especificado, y se utiliza para aleatorizar el color y la posición de los puntos generados para aumentar la variedad visual de la animación.
Donde x1 y x2 son los dos enteros pasados como argumentos. La función devuelve un entero aleatorio entre x1 y x2, incluyendo x1 y x2. Si x2 es mayor que x1, se genera un entero aleatorio utilizando la función rand(), que se restringe a un rango válido mediante aritmética modular. La función rand() generalmente devuelve un entero aleatorio entre 0 y RAND_MAX (generalmente 32767). Finalmente, se suma el resultado de la operación modular a x1 para garantizar que el entero aleatorio esté dentro del rango especificado.
Inicializar y generar conjuntos de puntos en forma de corazón
La función create_data genera los datos para la animación. Calcula los puntos de una forma de corazón y los anima según el algoritmo definido.
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;
}
}
Utiliza un bucle para iterar a través de una serie de valores de radianes (desde
0.1hasta2 * PI) y calcular las coordenadas x e y de cada punto.Se calcula la distancia entre dos puntos, y si la distancia es mayor que
average_distance, el punto se almacena en la matrizorigin_points.
Colorear puntos para generar animación
// 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);
}
}
}
Hay dos bucles anidados. El bucle externo aumenta gradualmente el valor de
size, y el bucle interno procesa cada punto dequantity.Con una probabilidad dada por
success_p, se decide si se debe generar un punto y se almacenan el color y las coordenadas del punto en la matrizpoints.
Generación y actualización de animación
// 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_sizese utiliza para obtener el número de puntos en el cuadro actual de la animación, calculado a partir de la sección de código anterior.indexes el número de puntos generados previamente.El bucle externo
for (int frame = 0; frame < frames; ++frame)se utiliza para iterar sobre cada cuadro de la animación, yframesespecifica cuántos cuadros hay en total.El bucle interno
for (index = 0; index < points_size; ++index)se utiliza para procesar cada punto en el cuadro actual. En cada cuadro, se realiza lo siguiente:
- Primero, se calcula la nueva posición de cada punto. Esto se hace mediante la siguiente fórmula:
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;
Estos cálculos se utilizan para actualizar las coordenadas x e y del punto para lograr el movimiento del punto en la animación. distance_increase controla la velocidad a la que se mueve el punto, que varía con la distancia desde la posición original del punto.
- Se dibujan puntos utilizando las funciones
XSetForegroundyXFillArc. Esto dibuja el punto en la pantalla,XSetForegroundpara establecer el color de pintura,XFillArcpara dibujar un punto relleno, y las coordenadas del centro del círculo se convierten mediante las funcionesscreen_xyscreen_y.
- La segunda parte del bucle interno
for (double size = 17; size < 23; size += 0.3)se utiliza para generar puntos adicionales en el cuadro actual. En este bucle, se genera, se colorea y se dibuja cada punto en la pantalla.
- Las coordenadas y los colores de los nuevos puntos se generan aleatoriamente según las siguientes condiciones:
Si
size >= 20y el número aleatorio es mayor que0.6, osize < 20y el número aleatorio es mayor que0.95, se genera un nuevo punto.
- Las coordenadas x e y de los puntos generados se calculan a partir de la posición del punto original y algunos desplazamientos aleatorios.
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);
}
- Finalmente, se utilizan las funciones
XSetForegroundyXFillArcpara dibujar el punto generado en la pantalla, al igual que el punto anterior.
Crear y inicializar una ventana X
El objetivo principal de esta función main es crear una ventana X, inicializar los datos y luego generar y dibujar cada cuadro de la animación en forma de corazón en un bucle infinito.
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: Abre la conexión con el servidor X.blackColor: Obtiene el valor del píxel negro mostrado.win: Crea una ventana y especifica sus propiedades, ubicación, tamaño y color del borde.XSelectInput: Especifica la máscara de eventos de entrada para la ventana.XMapWindow: Mapea la ventana en la pantalla.gc: Crea un Contexto Gráfico (Graphics Context).
Inicializa los datos y crea grupos de puntos
// 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)es un bucle infinito que espera a que se complete el mapeo de la ventana X, que es la fase de inicialización antes de que la ventana se muestre en la pantalla.
- Define una estructura
XEventpara recibir eventos X. XNextEvent: Espera y obtiene el siguiente evento X y lo almacena en la variablee.- Utiliza
ifpara verificar si el tipo de evento esMapNotify, lo que indica que la ventana se ha mapeado correctamente en la pantalla. Si el mapeo de la ventana se ha completado (es decir, el tipo de evento esMapNotify).
XFlush: Vacía el búfer de salida del servidor X para garantizar que los comandos de dibujo anteriores surtan efecto y no se retrasen hasta un dibujo de animación posterior.srand: Inicializa el generador de números aleatorios. Utiliza la hora actual como semilla para un generador de números aleatorios para generar efectos aleatorios en una animación.origin_points: Asigna memoria y crea una matriz dequantityestructurasPointque se utilizarán para almacenar las coordenadas de los puntos originales.points: Nuevamente, asigna memoria y crea una matriz más grande para almacenar los puntos en la animación.circlescontrola el número de puntos en la animación y es un valor constante.Finalmente, se llama a la función
create_data()para inicializar los datos, generar y establecer las coordenadas de los puntos originales, e inicializar el color y las coordenadas iniciales de los puntos de la animación.
Bucle principal de animación
El propósito de este bucle principal es controlar el estado del patrón del corazón cambiando el valor de frame para permitir que el efecto de animación se expanda y contraiga. La función usleep se utiliza para controlar la tasa de cuadros (frame rate) para que la animación se reproduzca a una velocidad determinada.
// 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;
}
Primero, se definen dos variables booleanas, extend y shrink, y se inicializan a true y false, que representan los estados extendido y contraído del patrón del corazón, respectivamente.
Luego, se inicia un bucle infinito, y la variable frame en el bucle se utiliza para realizar un seguimiento del recuento de los cuadros actuales de la animación.
usleepse utiliza para controlar la velocidad de la animación.Se borra todo en la ventana llamando a la función X11
XClearWindowpara que se pueda volver a dibujar en el siguiente cuadro.Se incrementa o decrementa
frameen función de los valores deextendyshrinkpara permitir que la animación se expanda y contraiga. Se utiliza el operador condicional? :para establecer un valor booleano.
Si
extendestrue, se verifica queframesea igual a19. Si es así, la animación está a punto de cambiar del estado extendido al estado contraído, se estableceshrinkentrueyextendenfalse. De lo contrario, se incrementaframe.
Si
extendesfalse, se verifica queframesea igual a0. Si es así, la animación está a punto de cambiar del estado contraído al estado extendido, se estableceshrinkenfalseyextendentrue. De lo contrario, se decrementaframe.
Finalmente, se liberan los recursos y se sale del programa.
// Inside the main() function
XCloseDisplay(display);
free(origin_points);
free(points);
return 0;
Compilar y ejecutar el proyecto
- Abra su terminal y navegue hasta el directorio del proyecto.
cd ~/project
- Compile el código utilizando el siguiente comando:
gcc -o dynamic_heart dynamic_heart.c -lX11 -lm
- Ejecute la aplicación:
./dynamic_heart

Resumen
En este proyecto, has aprendido cómo crear una cautivadora animación dinámica de un corazón utilizando el lenguaje de programación C. Has configurado el proyecto, definido variables, implementado funciones de coordenadas de pantalla, funciones de generador de números aleatorios y funciones de generación de datos. El programa utiliza el Sistema de Ventanas X (X Window System) para renderizar el corazón animado. Finalmente, has creado un bucle de animación y cerrado la pantalla, liberando la memoria asignada. Ahora puedes disfrutar de tu propia animación dinámica de un corazón creada en C.



