#include #include #include #include #include #include "figure-draw.h" #ifndef M_PI #define M_PI 3.14159265358979323846 #endif /* Вспомогательная функция для установки пикселя */ static inline void set_pixel(uint8_t *data, int32_t width, int32_t height, int x, int y, uint32_t color) { if (x < 0 || x >= width || y < 0 || y >= height) return; uint32_t *pixel = (uint32_t *)(data + (y * width + x) * 4); *pixel = color; } /* Проверка, находится ли точка внутри треугольника (барицентрические координаты) */ static int point_in_triangle(float px, float py, float x1, float y1, float x2, float y2, float x3, float y3) { float d1 = (px - x2) * (y1 - y2) - (x1 - x2) * (py - y2); float d2 = (px - x3) * (y2 - y3) - (x2 - x3) * (py - y3); float d3 = (px - x1) * (y3 - y1) - (x3 - x1) * (py - y1); int has_neg = (d1 < 0) || (d2 < 0) || (d3 < 0); int has_pos = (d1 > 0) || (d2 > 0) || (d3 > 0); return !(has_neg && has_pos); } /* Расстояние от точки до отрезка */ static float point_to_segment_distance(float px, float py, float x1, float y1, float x2, float y2) { float dx = x2 - x1; float dy = y2 - y1; float len_sq = dx * dx + dy * dy; if (len_sq < 0.0001f) { dx = px - x1; dy = py - y1; return sqrtf(dx * dx + dy * dy); } float t = ((px - x1) * dx + (py - y1) * dy) / len_sq; t = fmaxf(0.0f, fminf(1.0f, t)); float proj_x = x1 + t * dx; float proj_y = y1 + t * dy; dx = px - proj_x; dy = py - proj_y; return sqrtf(dx * dx + dy * dy); } /* Рисование круга */ static void draw_circle(struct window_draw_info* draw_info, float cx, float cy, float radius, float border_thickness, uint32_t border_color, uint32_t fill_color) { int x_min = (int)fmaxf(0, cx - radius - border_thickness); int x_max = (int)fminf(draw_info->width - 1, cx + radius + border_thickness); int y_min = (int)fmaxf(0, cy - radius - border_thickness); int y_max = (int)fminf(draw_info->height - 1, cy + radius + border_thickness); for (int y = y_min; y <= y_max; y++) { for (int x = x_min; x <= x_max; x++) { float dx = x - cx; float dy = y - cy; float dist = sqrtf(dx * dx + dy * dy); if (dist <= radius) { set_pixel(draw_info->data, draw_info->width, draw_info->height, x, y, fill_color); } else if (dist <= radius + border_thickness) { set_pixel(draw_info->data, draw_info->width, draw_info->height, x, y, border_color); } } } } /* Рисование треугольника */ static void draw_triangle(struct window_draw_info* draw_info, float cx, float cy, float radius, float angle, float border_thickness, uint32_t border_color, uint32_t fill_color) { /* Вычисляем координаты вершин равностороннего треугольника */ /* Угол 0 означает, что одна вершина справа от центра */ float vertices[3][2]; for (int i = 0; i < 3; i++) { float vertex_angle = angle + i * (2.0f * M_PI / 3.0f); vertices[i][0] = cx + radius * cosf(vertex_angle); vertices[i][1] = cy - radius * sinf(vertex_angle); /* Инвертируем Y для экранных координат */ } /* Находим ограничивающий прямоугольник */ float min_x = fminf(vertices[0][0], fminf(vertices[1][0], vertices[2][0])) - border_thickness; float max_x = fmaxf(vertices[0][0], fmaxf(vertices[1][0], vertices[2][0])) + border_thickness; float min_y = fminf(vertices[0][1], fminf(vertices[1][1], vertices[2][1])) - border_thickness; float max_y = fmaxf(vertices[0][1], fmaxf(vertices[1][1], vertices[2][1])) + border_thickness; int x_min = (int)fmaxf(0, min_x); int x_max = (int)fminf(draw_info->width - 1, max_x); int y_min = (int)fmaxf(0, min_y); int y_max = (int)fminf(draw_info->height - 1, max_y); /* Рисуем треугольник */ for (int y = y_min; y <= y_max; y++) { for (int x = x_min; x <= x_max; x++) { int inside = point_in_triangle((float)x, (float)y, vertices[0][0], vertices[0][1], vertices[1][0], vertices[1][1], vertices[2][0], vertices[2][1]); if (inside) { set_pixel(draw_info->data, draw_info->width, draw_info->height, x, y, fill_color); } else { /* Проверяем расстояние до границ */ float dist1 = point_to_segment_distance((float)x, (float)y, vertices[0][0], vertices[0][1], vertices[1][0], vertices[1][1]); float dist2 = point_to_segment_distance((float)x, (float)y, vertices[1][0], vertices[1][1], vertices[2][0], vertices[2][1]); float dist3 = point_to_segment_distance((float)x, (float)y, vertices[2][0], vertices[2][1], vertices[0][0], vertices[0][1]); float min_dist = fminf(dist1, fminf(dist2, dist3)); if (min_dist <= border_thickness) { set_pixel(draw_info->data, draw_info->width, draw_info->height, x, y, border_color); } } } } } /* Рисование квадрата */ static void draw_square(struct window_draw_info* draw_info, float cx, float cy, float radius, float angle, float border_thickness, uint32_t border_color, uint32_t fill_color) { /* Вычисляем координаты вершин квадрата */ /* Угол 0 означает, что одна вершина справа от центра */ float vertices[4][2]; for (int i = 0; i < 4; i++) { float vertex_angle = angle + i * (M_PI / 2.0f); vertices[i][0] = cx + radius * cosf(vertex_angle); vertices[i][1] = cy - radius * sinf(vertex_angle); /* Инвертируем Y для экранных координат */ } /* Находим ограничивающий прямоугольник */ float min_x = vertices[0][0], max_x = vertices[0][0]; float min_y = vertices[0][1], max_y = vertices[0][1]; for (int i = 1; i < 4; i++) { min_x = fminf(min_x, vertices[i][0]); max_x = fmaxf(max_x, vertices[i][0]); min_y = fminf(min_y, vertices[i][1]); max_y = fmaxf(max_y, vertices[i][1]); } min_x -= border_thickness; max_x += border_thickness; min_y -= border_thickness; max_y += border_thickness; int x_min = (int)fmaxf(0, min_x); int x_max = (int)fminf(draw_info->width - 1, max_x); int y_min = (int)fmaxf(0, min_y); int y_max = (int)fminf(draw_info->height - 1, max_y); /* Рисуем квадрат */ for (int y = y_min; y <= y_max; y++) { for (int x = x_min; x <= x_max; x++) { float px = (float)x; float py = (float)y; /* Проверяем, находится ли точка внутри квадрата */ /* Используем два треугольника */ int inside = point_in_triangle(px, py, vertices[0][0], vertices[0][1], vertices[1][0], vertices[1][1], vertices[2][0], vertices[2][1]) || point_in_triangle(px, py, vertices[0][0], vertices[0][1], vertices[2][0], vertices[2][1], vertices[3][0], vertices[3][1]); if (inside) { set_pixel(draw_info->data, draw_info->width, draw_info->height, x, y, fill_color); } else { /* Проверяем расстояние до границ */ float min_dist = INFINITY; for (int i = 0; i < 4; i++) { int next = (i + 1) % 4; float dist = point_to_segment_distance(px, py, vertices[i][0], vertices[i][1], vertices[next][0], vertices[next][1]); min_dist = fminf(min_dist, dist); } if (min_dist <= border_thickness) { set_pixel(draw_info->data, draw_info->width, draw_info->height, x, y, border_color); } } } } } void figure_draw(struct window_draw_info* draw_info, float border_thickness, uint32_t border_color, uint32_t fill_color) { if (!draw_info || !draw_info->data) return; /* Координаты приходят в относительных единицах * Y: [0..1] -> умножаем на высоту * X: нормализован относительно высоты -> умножаем на высоту же */ float cx = draw_info->figure.position.x * draw_info->height; float cy = draw_info->figure.position.y * draw_info->height; float radius = draw_info->figure.radius; float angle = draw_info->figure.angle; enum figure_type type = draw_info->figure.type; switch (type) { case FIGURE_CIRCLE: draw_circle(draw_info, cx, cy, radius, border_thickness, border_color, fill_color); break; case FIGURE_TRIANGLE: draw_triangle(draw_info, cx, cy, radius, angle, border_thickness, border_color, fill_color); break; case FIGURE_SQUARE: draw_square(draw_info, cx, cy, radius, angle, border_thickness, border_color, fill_color); break; } }