Почти красота
This commit is contained in:
@@ -1,116 +1,239 @@
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#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. */
|
||||
#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;
|
||||
|
||||
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]
|
||||
/* Координаты приходят в относительных единицах
|
||||
* Y: [0..1] -> умножаем на высоту
|
||||
* X: нормализован относительно высоты -> умножаем на высоту же
|
||||
*/
|
||||
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;
|
||||
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;
|
||||
|
||||
/* 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;
|
||||
|
||||
/* 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)
|
||||
{
|
||||
switch (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;
|
||||
}
|
||||
draw_circle(draw_info, cx, cy, radius, border_thickness, border_color, fill_color);
|
||||
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;
|
||||
draw_triangle(draw_info, cx, cy, radius, angle, border_thickness, border_color, fill_color);
|
||||
break;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
case FIGURE_SQUARE:
|
||||
draw_square(draw_info, cx, cy, radius, angle, border_thickness, border_color, fill_color);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (draw)
|
||||
{
|
||||
uint32_t col = border_pixel ? border_color : fill_color;
|
||||
pixels[y * w + x] = col;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* C symbol exported to be called by minimal assembly wrapper */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user