Расстановка точек на окружности

This commit is contained in:
2025-11-17 20:43:01 +03:00
parent 58eb3695f1
commit 494686a203
3 changed files with 216 additions and 190 deletions

View File

@@ -13,24 +13,13 @@ enum figure_type
struct figure_animation_info {
enum figure_type type;
struct vec2 position;
/* Direction vector; its components are not in pixels. Anim code converts
* them to pixel-space and normalizes them so that `speed` is applied as
* pixels/sec uniformly in both axes (aspect ratio is accounted for). */
struct vec2 velocity;
float angle;
float angular_velocity;
/* Speed in pixels per second. This value is applied uniformly to both
* axes (X and Y); the animation code converts this pixel speed to
* normalized increments for position updates taking window aspect ratio
* into account. */
float speed;
/* Radius of the figure in pixels (float)
* This field is used by animation code to check collisions
* with the left/right/top/bottom borders. The animation code
* will convert this pixel radius to normalized coordinates for
* collision checks against normalized positions. */
float radius;
};

View File

@@ -1,203 +1,191 @@
; Макрос для локальных переменных
%macro local 2
%assign __local_offset __local_offset - %2
%define %1 (__local_offset)
%endmacro
; Подключаем автоматически сгенерированные offsets из C структур
%include "offsets.inc"
section .rodata
PI: dd 3.1415926
TWO_PI: dd 6.2831852
NEG_ONE_CONST: dd -1.0
ONE_CONST: dd 1.0
ZERO_CONST: dd 0.0
section .text
; void animation_step(struct window_draw_info* draw_info);
; void figure_animation_step(struct window_draw_info* draw_info);
; Параметры:
; rdi - указатель на struct window_draw_info
%assign __local_offset 0
global figure_animation_step
figure_animation_step:
; Создаём локальную область на стеке и будем туда копировать поля структуры
enter 0, 0
; rdi - pointer to struct window_draw_info
; figure is embedded at offset WDI_FIGURE
mov rax, rdi
add rax, WDI_FIGURE
enter 48, 0
; --- compute movement increments so that `speed` means pixels/second
; Convert velocity to pixel-space (vx = vel.x * width, vy = vel.y * height),
; normalize that vector and apply `speed*dt` so movement in pixel units equals speed.
; using SSE for float math
movss xmm0, dword [rax + FIG_SPEED] ; xmm0 = speed (pixels/sec)
movss xmm9, [rel DT] ; xmm9 = dt
mulss xmm0, xmm9 ; xmm0 = speed * dt (pixels)
; Отработка коллизий
call figure_handle_collision;
; load width/height
mov ebx, dword [rdi + WDI_WIDTH]
cvtsi2ss xmm10, ebx ; xmm10 = width
mov ebx, dword [rdi + WDI_HEIGHT]
cvtsi2ss xmm11, ebx ; xmm11 = height
; Сохранить регистры
; vx_pixels = vel.x * width
movss xmm1, dword [rax + FIG_VELOCITY] ; vel.x
mulss xmm1, xmm10 ; xmm1 = vx_pixels
; Сдвиги переменных
; Аргументы
local width, 4
local height, 4
local fig_type, 4
local pos_x, 4
local pos_y, 4
local vel_x, 4
local vel_y, 4
local angle, 4
local angular_vel, 4
local speed, 4
local radius, 4
; vy_pixels = vel.y * height
movss xmm2, dword [rax + FIG_VELOCITY + 4]
mulss xmm2, xmm11 ; xmm2 = vy_pixels
; Чтение аргументов
mov eax, dword [rdi + WDI_WIDTH]
mov [rbp - width], eax
; length = sqrt(vx^2 + vy^2)
movss xmm3, xmm1
mulss xmm3, xmm1 ; xmm3 = vx^2
movss xmm4, xmm2
mulss xmm4, xmm4 ; xmm4 = vy^2
addss xmm3, xmm4 ; xmm3 = vx^2 + vy^2
sqrtss xmm3, xmm3 ; xmm3 = length (pixels)
mov eax, dword [rdi + WDI_HEIGHT]
mov [rbp - height], eax
; if length == 0 -> skip movement
ucomiss xmm3, [rel ZERO_CONST]
je .skip_move
mov eax, dword [rdi + WDI_FIGURE + FIG_TYPE]
mov [rbp - fig_type], eax
; scalar = (speed * dt) / length
movss xmm4, xmm0 ; xmm4 = speed*dt
divss xmm4, xmm3 ; xmm4 = scalar
mov eax, dword [rdi + WDI_FIGURE + FIG_POSITION]
mov [rbp - pos_x], eax
; dx = vel.x * scalar
movss xmm1, dword [rax + FIG_VELOCITY]
mulss xmm1, xmm4
movss xmm2, dword [rax + FIG_POSITION] ; position.x
addss xmm2, xmm1
movss dword [rax + FIG_POSITION], xmm2
mov eax, dword [rdi + WDI_FIGURE + FIG_POSITION + 4]
mov [rbp - pos_x], eax
; dy = vel.y * scalar
movss xmm1, dword [rax + FIG_VELOCITY + 4]
mulss xmm1, xmm4
movss xmm2, dword [rax + FIG_POSITION + 4]
addss xmm2, xmm1
movss dword [rax + FIG_POSITION + 4], xmm2
mov eax, dword [rdi + WDI_FIGURE + FIG_VELOCITY]
mov [rbp - vel_x], eax
.skip_move:
mov eax, dword [rdi + WDI_FIGURE + FIG_VELOCITY + 4]
mov [rbp - vel_y], eax
; rotate: angle += angular_velocity * dt
movss xmm3, dword [rax + FIG_ANG_VEL]
mulss xmm3, [rel DT]
movss xmm4, dword [rax + FIG_ANGLE]
addss xmm4, xmm3
movss dword [rax + FIG_ANGLE], xmm4
mov eax, dword [rdi + WDI_FIGURE + FIG_ANGLE]
mov [rbp - angle], eax
; If circle -> call collision check helper
mov ecx, dword [rax + FIG_TYPE]
cmp ecx, 0
jne .no_collision_check
; pass pointer to window_draw_info (original rdi) in rdi
; rdi already has draw_info pointer
call figure_check_collision_circle
mov eax, dword [rdi + WDI_FIGURE + FIG_ANG_VEL]
mov [rbp - angular_vel], eax
.no_collision_check:
mov eax, dword [rdi + WDI_FIGURE + FIG_SPEED]
mov [rbp - speed], eax
mov eax, dword [rdi + WDI_FIGURE + FIG_RADIUS]
mov [rbp - radius], eax
; Восстановить регистры
leave
ret
; Функция для обработки коллизии, изменяет velocity при обнаружении коллизии с границами
; Параметры:
; rdi - указатель на struct window_draw_info
%assign __local_offset 0
figure_handle_collision:
enter 128,0
local point_buffer, 128
; Вызов place_points_on_circle
movss xmm0, [rdi + WDI_FIGURE + FIG_POSITION]
movss xmm1, [rdi + WDI_FIGURE + FIG_POSITION + 4]
mov rsi, 4
mov rdx, 0
push rdi
mov rdi, rbp
add rdi, point_buffer
call place_points_on_circle
pop rdi
leave
ret
; helper: check collision for circle and reflect velocity & angular velocity
; rdi - pointer to struct window_draw_info
global figure_check_collision_circle
figure_check_collision_circle:
push rbx
; rdi -> window_draw_info
; rax -> figure_animation_info
mov rax, rdi
add rax, WDI_FIGURE
; Функция для расположения точек на окружности
; Вход:
; xmm0 - pos_x
; xmm1 - pos_y
; xmm2 - смещение точек на окружности в радианах
; xmm3 - радиус
; rdi - адрес буфера для точек
; rsi - количество точек
; Уничтожает: rax, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7
global place_points_on_circle
place_points_on_circle:
enter 0, 0
movss xmm0, dword [rax + FIG_POSITION] ; pos.x
movss xmm1, dword [rax + FIG_POSITION + 4] ; pos.y
; Сохранить координаты центра
movss xmm6, xmm0
movss xmm7, xmm1
movss xmm2, dword [rax + FIG_VELOCITY] ; vel.x
movss xmm3, dword [rax + FIG_VELOCITY + 4] ; vel.y
; Рассчитать TWO_PI / rsi и сохранить в xmm4
movss xmm4, [rel TWO_PI]
cvtsi2ss xmm5, rsi
divss xmm4, xmm5
movss xmm4, dword [rax + FIG_ANG_VEL] ; angvel
movss xmm5, [rel ZERO_CONST] ; счётчик
mov rcx, rsi
.cycle:
movss xmm0, xmm5
; radius is stored in figure as pixel count (float)
; we keep positions normalized (0..1) so convert radius to normalized
; coordinates for x and y: r_x = radius / width, r_y = radius / height
movss xmm5, dword [rax + FIG_RADIUS] ; xmm5 = radius_pixels
mulss xmm0, xmm4 ; Счётчик*шаг
addss xmm0, xmm2 ; Прибавить смещение
call sincos_f32_rbp ; Посчитать sincos
mulss xmm0, xmm3 ; sin *= radius
mulss xmm1, xmm3 ; cos *= radius
; load width/height from window_draw_info
mov ebx, dword [rdi + WDI_WIDTH]
cvtsi2ss xmm10, ebx ; xmm10 = width
mov ebx, dword [rdi + WDI_HEIGHT]
cvtsi2ss xmm11, ebx ; xmm11 = height
addss xmm1, xmm6 ; x = center_x + cos*radius
addss xmm0, xmm7 ; y = center_y + sin*radius
; compute r_x = radius / width
movss xmm12, xmm5
divss xmm12, xmm10 ; xmm12 = r_x
movss [rdi], xmm1
movss [rdi + 4], xmm0
add rdi, 8
; compute r_y = radius / height
movss xmm13, xmm5
divss xmm13, xmm11 ; xmm13 = r_y
; left edge: pos.x < r_x ?
ucomiss xmm0, xmm12
jae .check_right
; pos.x = radius
movss dword [rax + FIG_POSITION], xmm12
; vel.x = -vel.x
movss xmm6, xmm2
mulss xmm6, [rel NEG_ONE]
movss dword [rax + FIG_VELOCITY], xmm6
; invert angular velocity
movss xmm7, xmm4
mulss xmm7, [rel NEG_ONE]
movss dword [rax + FIG_ANG_VEL], xmm7
jmp .done_x
addss xmm5, [rel ONE_CONST]
loop .cycle
.check_right:
; pos.x + radius > 1 ?
movss xmm8, [rel ONE_CONST]
addss xmm8, xmm12 ; xmm8 = 1 + r_x
; Actually want pos.x + radius > 1 <=> pos.x > 1 - radius
; compute bound = 1 - r_x
movss xmm9, [rel ONE_CONST]
subss xmm9, xmm12
ucomiss xmm0, xmm9
jbe .done_x
; pos.x = 1 - radius
movss dword [rax + FIG_POSITION], xmm9
; vel.x = -vel.x
movss xmm6, xmm2
mulss xmm6, [rel NEG_ONE]
movss dword [rax + FIG_VELOCITY], xmm6
; invert angular velocity
movss xmm7, xmm4
mulss xmm7, [rel NEG_ONE]
movss dword [rax + FIG_ANG_VEL], xmm7
.done_x:
; check bottom / top boundaries for y
; bottom: pos.y < r_y
ucomiss xmm1, xmm13
jae .check_top2
movss dword [rax + FIG_POSITION + 4], xmm13
movss xmm6, xmm3
mulss xmm6, [rel NEG_ONE]
movss dword [rax + FIG_VELOCITY + 4], xmm6
; invert angular
movss xmm7, xmm4
mulss xmm7, [rel NEG_ONE]
movss dword [rax + FIG_ANG_VEL], xmm7
jmp .done_y
.check_top2:
; top: pos.y > 1 - r_y
movss xmm9, [rel ONE_CONST]
subss xmm9, xmm13
ucomiss xmm1, xmm9
jbe .done_y
movss dword [rax + FIG_POSITION + 4], xmm9
movss xmm6, xmm3
mulss xmm6, [rel NEG_ONE]
movss dword [rax + FIG_VELOCITY + 4], xmm6
movss xmm7, xmm4
mulss xmm7, [rel NEG_ONE]
movss dword [rax + FIG_ANG_VEL], xmm7
.done_y:
pop rbx
leave
ret
section .rodata
DT: dd 0.1
NEG_ONE: dd -1.0
ONE_CONST: dd 1.0
ZERO_CONST: dd 0.0
; Функция для рассчёта sin и cos
; Вход:
; xmm0 - угол в радианах (float)
; Выход:
; xmm0 - sin(angle)
; xmm1 - cos(angle)
; Уничтожает: rax, flags
sincos_f32_rbp:
enter 24, 0 ; 24 байта локального места:
; [rbp-8] — временно угол (для fld)
; [rbp-16] — sin
; [rbp-24] — cos
; (выравнивание по 16 будет соблюдено за счёт enter)
; Сохраняем входной угол как float32 в стек для загрузки в x87
movss [rbp-8], xmm0
fld dword [rbp-8] ; ST(0) = angle (в extended precision)
fsincos ; ST(0) = cos, ST(1) = sin
; Сохраняем результаты обратно в память как float32
fstp dword [rbp-24] ; pop cos → [rbp-24]
fstp dword [rbp-16] ; pop sin → [rbp-16]
; Загружаем результаты в xmm0 и xmm1
movss xmm0, [rbp-16] ; xmm0 = sin
movss xmm1, [rbp-24] ; xmm1 = cos
leave ; эквивалент: mov rsp, rbp / pop rbp
ret

View File

@@ -41,6 +41,26 @@ static void signal_thread_exit(struct window_thread_slot *slot)
pthread_mutex_unlock(&g_thread_lock);
}
#include <stdio.h>
#include <math.h>
#include <stdint.h>
#include <stdlib.h>
// Прототип ASM-функции
void place_points_on_circle(
float pos_x,
float pos_y,
float offset_rad,
float radius,
float *points,
size_t count);
struct Point
{
float x;
float y;
};
static void *window_aux_loop(void *arg)
{
struct window_thread_slot *slot = arg;
@@ -49,7 +69,36 @@ static void *window_aux_loop(void *arg)
{
/* На время обновления позиции фигуры локаем мутекс */
pthread_mutex_lock(&draw_info->figure_mutex);
figure_animation_step(draw_info);
// figure_animation_step(draw_info);
const size_t n = 8;
struct vec2 *pts = malloc(sizeof(struct vec2) * n);
if (!pts)
{
printf("malloc failed\n");
}
float center_x = 0.0f;
float center_y = 0.0f;
float offset = 0.78f;
float radius = 1.0f;
// Вызов ASM-функции
place_points_on_circle(
center_x,
center_y,
offset,
radius,
(float *)pts, // адрес выходного массива
n);
// Вывод для проверки (и удобной точки останова)
for (size_t i = 0; i < n; i++)
{
printf("%zu: x = %f, y = %f\n", i, pts[i].x, pts[i].y);
}
free(pts);
pthread_mutex_unlock(&draw_info->figure_mutex);
usleep(30 * 1000);