From 7de923d0b62860e6e4f427aa23e6ece44cf4b652 Mon Sep 17 00:00:00 2001 From: Roman Pytkov Date: Mon, 17 Nov 2025 23:02:16 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=BE=D1=87=D1=82=D0=B8=20=D0=BA=D1=80?= =?UTF-8?q?=D0=B0=D1=81=D0=BE=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- wayland/src/figure-draw.c | 323 +++++++++++++++++++++++----------- wayland/src/wayland-runtime.c | 2 +- wayland/src/window.c | 6 +- 3 files changed, 227 insertions(+), 104 deletions(-) diff --git a/wayland/src/figure-draw.c b/wayland/src/figure-draw.c index 3b50cad..3fca942 100644 --- a/wayland/src/figure-draw.c +++ b/wayland/src/figure-draw.c @@ -1,116 +1,239 @@ #include #include #include +#include +#include #include "figure-draw.h" -/* C implementation of the drawing routine. The .asm file simply forwards to this - * function so we keep the symbol present in an assembly file. */ -void figure_draw(struct window_draw_info* draw_info, float border_thickness, uint32_t border_color, uint32_t fill_color) +#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 (!draw_info || !draw_info->data) + if (x < 0 || x >= width || y < 0 || y >= height) return; - - int w = draw_info->width; - int h = draw_info->height; - uint32_t *pixels = (uint32_t *)draw_info->data; - - struct figure_animation_info fig = draw_info->figure; - - /* Calculate aspect ratio: width is proportional, height is always 1.0 */ - float aspect_ratio = (float)w / (float)h; - /* center in pixels: - * x coordinates are in range [0, aspect_ratio] - * y coordinates are in range [0, 1.0] - */ - float cx = fig.position.x * (float)h; /* scale x by height to maintain aspect */ - float cy = fig.position.y * (float)h; /* scale y by height */ - /* `fig.radius` is in pixels now; use it directly. */ - float r = fig.radius; - float r2 = r * r; - float border = border_thickness; - if (border < 0.0f) border = 0.0f; + uint32_t *pixel = (uint32_t *)(data + (y * width + x) * 4); + *pixel = color; +} - /* bounding box */ - int minx = (int)floorf(cx - r); - int maxx = (int)ceilf(cx + r); - int miny = (int)floorf(cy - r); - int maxy = (int)ceilf(cy + r); - /* make sure we don't go out of bounds */ - if (miny < 0) miny = 0; - if (minx < 0) minx = 0; - if (maxy >= h) maxy = h - 1; - if (maxx >= w) maxx = w - 1; +/* Проверка, находится ли точка внутри треугольника (барицентрические координаты) */ +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); +} - /* We'll compute pixel centers at (x + 0.5f, y + 0.5f) */ - for (int y = miny; y <= maxy; ++y) - { - float py = (float)y + 0.5f; - for (int x = minx; x <= maxx; ++x) - { - float px = (float)x + 0.5f; - int draw = 0; - int border_pixel = 0; - switch (fig.type) - { - case FIGURE_CIRCLE: - { - float dx = px - cx; - float dy = py - cy; - float d2 = dx*dx + dy*dy; - if (d2 <= r2) - { - draw = 1; - if (d2 >= (r - border)*(r - border)) - border_pixel = 1; - } - break; - } - case FIGURE_SQUARE: - { - float dx = fabsf(px - cx); - float dy = fabsf(py - cy); - if (dx <= r && dy <= r) - { - draw = 1; - if (fmaxf(dx, dy) >= r - border) - border_pixel = 1; - } - break; - } - case FIGURE_TRIANGLE: - { - /* Equilateral triangle centered at cx,cy. Apex up. * - * Vertices: A=(cx, cy - r), B=(cx + r*0.866, cy + r*0.5), C=(cx - r*0.866, cy + r*0.5) */ - float v1x = r * 0.8660254037844386f; - float v1y = r * 0.5f; - float ax = cx; float ay = cy - r; - float bx = cx + v1x; float by = cy + v1y; - float cx2 = cx - v1x; float cy2 = by; +/* Расстояние от точки до отрезка */ +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); +} - /* barycentric tests: point inside triangle if all cross products have same sign */ - float s1 = (px - bx)*(ay - by) - (ax - bx)*(py - by); - float s2 = (px - cx2)*(by - cy2) - (bx - cx2)*(py - cy2); - float s3 = (px - ax)*(cy2 - ay) - (cx2 - ax)*(py - ay); - int neg = (s1 < 0) + (s2 < 0) + (s3 < 0); - int pos = (s1 > 0) + (s2 > 0) + (s3 > 0); - if (neg == 0 || pos == 0) - { - draw = 1; - } - break; - } - default: - break; - } - - if (draw) - { - uint32_t col = border_pixel ? border_color : fill_color; - pixels[y * w + x] = col; +/* Рисование круга */ +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); } } } } -/* C symbol exported to be called by minimal assembly wrapper */ +/* Рисование треугольника */ +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; + } +} + diff --git a/wayland/src/wayland-runtime.c b/wayland/src/wayland-runtime.c index 5d6b91a..5c578fe 100644 --- a/wayland/src/wayland-runtime.c +++ b/wayland/src/wayland-runtime.c @@ -101,7 +101,7 @@ static void *window_aux_loop(void *arg) // free(pts); pthread_mutex_unlock(&draw_info->figure_mutex); - usleep(30 * 1000); + usleep(33 * 1000); } return NULL; } diff --git a/wayland/src/window.c b/wayland/src/window.c index 67d2c5c..3130479 100644 --- a/wayland/src/window.c +++ b/wayland/src/window.c @@ -132,7 +132,7 @@ static void draw(struct wayland_window *win) } /* Draw figure into buffer. border thickness in pixels = 3.0f */ - figure_draw(&win->draw_info, 3.0f, 0xFFFFFFFF, color); + figure_draw(&win->draw_info, 3.0f, 0xFF000000, color); pthread_mutex_unlock(&draw_info->figure_mutex); @@ -255,8 +255,8 @@ int window_init(struct wl_display *display, struct wl_event_queue *queue, struct draw_info->figure.velocity.x = 0.5f; draw_info->figure.velocity.y = 0.5f; draw_info->figure.angle = 0.0f; - draw_info->figure.angular_velocity = 1; /* radians per frame */ - draw_info->figure.speed = 20; /* speed multiplier */ + draw_info->figure.angular_velocity = 0.01; /* radians per frame */ + draw_info->figure.speed = 10; /* speed multiplier */ draw_info->figure.radius = 25.0f; /* radius in pixels */ win->buffer = NULL;