diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/common.h | 256 | ||||
-rw-r--r-- | src/main.c | 90 | ||||
-rw-r--r-- | src/physics.c | 244 | ||||
-rw-r--r-- | src/renderer.c | 473 | ||||
-rw-r--r-- | src/ui.c | 461 |
5 files changed, 1524 insertions, 0 deletions
diff --git a/src/common.h b/src/common.h new file mode 100644 index 0000000..ee9c3fe --- /dev/null +++ b/src/common.h @@ -0,0 +1,256 @@ +#include <stdlib.h> +#include <inttypes.h> +#include <string.h> +#include <assert.h> +#include <stdbool.h> +#include <math.h> +#include <SDL2/SDL.h> + +#define PROGRAM_NAME "Symulator pól elektromagnetycznych" + +#include <stdio.h> +#define con_printf printf + +#define max2(a, b) ((a) > (b) ? (a) : (b)) +#define max3(a, b, c) (max2(max2(a, b), c)) +#define min2(a, b) ((a) < (b) ? (a) : (b)) +#define min3(a, b, c) (min2(min2(a, b), c)) + +// main + +char *va(const char *fmt, ...); +int64_t get_time(void); + +// math + +typedef float vec_t; +typedef vec_t vec2_t[2]; +typedef vec_t vec3_t[3]; +typedef vec_t vec4_t[4]; +typedef vec_t mst2_t[4]; +/* mst2_t is a 3x3 scale&translate matrix (only 4 components are needed): + * [ dw 0 dx ] + * [ 0 dh dy ] + * [ 0 0 1 ] + */ + +static inline void v2_copy(vec_t *R, vec_t *A) +{ + R[0] = A[0]; + R[1] = A[1]; +} + +static inline void v3_copy(vec_t *R, vec_t *A) +{ + R[0] = A[0]; + R[1] = A[1]; + R[2] = A[2]; +} + +static inline void v4_copy(vec_t *R, vec_t *A) +{ + R[0] = A[0]; + R[1] = A[1]; + R[2] = A[2]; + R[3] = A[3]; +} + +static inline void v2_set(vec_t *R, vec_t a, vec_t b) +{ + R[0] = a; + R[1] = b; +} + +static inline void v3_set(vec_t *R, vec_t a, vec_t b, vec_t c) +{ + R[0] = a; + R[1] = b; + R[2] = c; +} + +static inline void v2_add(vec_t *R, vec_t *A, vec_t *B) +{ + R[0] = A[0] + B[0]; + R[1] = A[1] + B[1]; +} + +static inline void v2_sub(vec_t *R, vec_t *A, vec_t *B) +{ + R[0] = A[0] - B[0]; + R[1] = A[1] - B[1]; +} + +static inline void v2_mul(vec_t *R, vec_t *A, vec_t b) +{ + R[0] = A[0] * b; + R[1] = A[1] * b; +} + +static inline void mst2_set_identity(mst2_t R) +{ + R[0] = 0; + R[1] = 0; + R[2] = 1; + R[3] = 1; +} + +static inline void mst2_set(mst2_t R, vec_t dx, vec_t dy, vec_t dw, vec_t dh) +{ + R[0] = dx; + R[1] = dy; + R[2] = dw; + R[3] = dh; +} + +static inline void mst2_inv(mst2_t R, mst2_t A) +{ + mst2_t copy = {A[0], A[1], A[2], A[3]}; + + R[0] = -copy[0] / copy[2]; + R[1] = -copy[1] / copy[3]; + R[2] = 1 / copy[2]; + R[3] = 1 / copy[3]; +} + +static inline void v2_mul_mst2(vec_t *R, vec_t *A, mst2_t M) +{ + R[0] = M[0] + A[0] * M[2]; + R[1] = M[1] + A[1] * M[3]; +} + +static inline void v2_div_mst2(vec_t *R, vec_t *A, mst2_t M) +{ + mst2_t inv; + + mst2_inv(inv, M); + v2_mul_mst2(R, A, inv); +} + +// v2_mul_mst2 but without translation +static inline void v2_mul_mst2_nt(vec_t *R, vec_t *A, mst2_t M) +{ + R[0] = A[0] * M[2]; + R[1] = A[1] * M[3]; +} + +// v2_div_mst2 but without translation +static inline void v2_div_mst2_nt(vec_t *R, vec_t *A, mst2_t M) +{ + mst2_t inv; + + mst2_inv(inv, M); + v2_mul_mst2_nt(R, A, inv); +} + +static inline void mst2_dump(mst2_t M) +{ + printf("[%f\t%f\t%f]\n", M[2], 0.0, M[0]); + printf("[%f\t%f\t%f]\n", 0.0, M[3], M[1]); + printf("[%f\t%f\t%f]\n", 0.0, 0.0, 1.0); +} + +// physics + +typedef struct { + size_t width, height, depth; + size_t zstr, ystr, xstr, size; // strides in no. of points (not bytes) + size_t zstr1, ystr1, xstr1, size1; // same as above but for associated + // scalar fields + float spacing; // in meters +} phy_field_info; + +typedef struct { + float *E; + float *H; +} phy_field_em; + +/* +typedef struct { + int num; + void *data; +} phy_command; +*/ + +typedef struct { + phy_field_info field_info; + phy_field_em fields[3]; + float *field_eps, *field_mu; //permittivity and permeability + float time; + + SDL_mutex *rotate_lock; +} phy_sim; + +void phy_sim_destroy(phy_sim *sim); +int phy_sim_create(phy_sim *sim); +void phy_sim_compute_const_fields(phy_sim *sim); +void phy_sim_step(phy_sim *sim, float dt); + +int phy_thread(phy_sim *sim); + +// renderer + +extern float r_color_white[4]; +extern float r_color_black[4]; +extern float r_color_red[4]; +extern float r_color_blue[4]; + +typedef struct r_window_s r_window; +struct r_window_s { + SDL_Window *window; + SDL_Renderer *renderer; + SDL_Texture *font_texture; + + r_window *prev, *next; +}; + +int r_init(void); +void r_quit(void); + +r_window *r_window_create(void); +void r_window_destroy(r_window *rw); + +void r_clear(r_window *rw, float *color); +void r_flip(r_window *rw); + +void r_clip_enable(r_window *rw, float x, float y, float w, float h); +void r_clip_disable(r_window *rw); +void r_draw_line(r_window *rw, float x0, float y0, float x1, float y1, + float *color); +void r_draw_rect(r_window *rw, float x, float y, float w, float h, + float *color); + +#define TEXT_CENTERX 0x0001 +#define TEXT_RIGHTX 0x0002 +float r_text_width(float h, char *text); +void r_draw_text(r_window *rw, float x, float y, float h, char *text, + float *color, int flags); + +typedef enum { + XSECTION_XY, + XSECTION_XZ, + XSECTION_YZ +} r_xsection_type; + +typedef struct { + SDL_Texture *texture; + float aspect_ratio; +} r_xsection; + +void r_xsection_create(r_xsection *xsection); +void r_xsection_destroy(r_xsection *xsection); +int r_xsection_update(r_window *rw, r_xsection *xsection, + phy_field_info *fi, float *field, + r_xsection_type type, float frac); +void r_xsection_draw(r_window *rw, r_xsection *xsection, + float x, float y, float w, float h); + +// ui + +int ui_init(void); +void ui_quit(void); + +void ui_renderer_window_register(r_window *rw); + +void ui_event(SDL_Event *event); +void ui_draw(phy_sim *sim); + diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..65a7ab3 --- /dev/null +++ b/src/main.c @@ -0,0 +1,90 @@ +#include "common.h" +#include <signal.h> +#include <time.h> + +char *va(const char *fmt, ...) +{ + va_list vl; + static char buffer[4096]; + + va_start(vl, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, vl); + va_end(vl); + + return buffer; +} + +int64_t get_time(void) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec * 1000000000 + ts.tv_nsec; +} + +int main(void) +{ + int rv = 0; + r_window *first_window; + phy_sim sim; + SDL_Thread *sim_thread; + + if (r_init()) { + con_printf("fatal error: renderer initialization failed\n"); + rv = 1; + goto quit; + } + + if (ui_init()) { + con_printf("fatal error: UI initialization failed\n"); + rv = 1; + goto quit; + } + + signal(SIGINT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + + first_window = r_window_create(); + if (!first_window) { + con_printf("fatal error: couldn't open first window\n"); + rv = 1; + goto quit; + } + + ui_renderer_window_register(first_window); + + if (phy_sim_create(&sim)) { + con_printf("fatal error: phy_sim_create failed\n"); + rv = 1; + goto quit; + } + + sim_thread = SDL_CreateThread((SDL_ThreadFunction)phy_thread, + "phy_thread", &sim); + if (!sim_thread) { + con_printf("fatal error: SDL_CreateThread failed: %s\n", + SDL_GetError()); + rv = 1; + goto quit; + } + + while (1) { + SDL_Event event; + + while (SDL_PollEvent(&event)) { + if (event.type == SDL_QUIT) + goto quit; + else + ui_event(&event); + } + + ui_draw(&sim); + } + +quit: + con_printf("main: exitting, rv=%i\n", rv); + phy_sim_destroy(&sim); + ui_quit(); + r_quit(); + SDL_Quit(); + return rv; +} diff --git a/src/physics.c b/src/physics.c new file mode 100644 index 0000000..be6a426 --- /dev/null +++ b/src/physics.c @@ -0,0 +1,244 @@ +#include "common.h" + +void phy_sim_destroy(phy_sim *sim) { + size_t i; + + SDL_DestroyMutex(sim->rotate_lock); + + for (i = 0; i < 3; i++) { + free(sim->fields[i].E); + free(sim->fields[i].H); + } +} + +int phy_sim_create(phy_sim *sim) { + size_t i; + phy_field_info *fi; + + memset(sim, 0, sizeof(phy_sim)); + + fi = &sim->field_info; + + fi->width = 30; + fi->height = 30; + fi->depth = 30; + fi->spacing = 1.5e10 / max3(fi->width, fi->height, fi->depth); + + fi->xstr = 3; + fi->ystr = fi->xstr * fi->width; + fi->zstr = fi->ystr * fi->height; + fi->size = fi->zstr * fi->depth; + + fi->xstr1 = 1; + fi->ystr1 = fi->xstr1 * fi->width; + fi->zstr1 = fi->ystr1 * fi->height; + fi->size1 = fi->zstr1 * fi->depth; + + for (i = 0; i < 3; i++) { + sim->fields[i].E = calloc(fi->size, sizeof(float)); + sim->fields[i].H = calloc(fi->size, sizeof(float)); + if (!sim->fields[i].E || !sim->fields[i].H) { + con_printf("phy_sim_create: out of memory\n"); + goto error; + } + } + + sim->field_eps = calloc(fi->size1, sizeof(float)); + sim->field_mu = calloc(fi->size1, sizeof(float)); + + phy_sim_compute_const_fields(sim); + + sim->rotate_lock = SDL_CreateMutex(); + assert(sim->rotate_lock); + + return 0; + +error: + phy_sim_destroy(sim); + return 1; +} + +void phy_sim_compute_const_fields(phy_sim *sim) +{ + size_t x, y, z; + phy_field_info *fi = &sim->field_info; + size_t border_width; + + border_width = 3; + + for (z = 0; z < fi->depth; z++) + for (y = 0; y < fi->height; y++) + for (x = 0; x < fi->width; x++) { + float *eps, *mu; + float border_fx, border_fy, border_fz, border_f; + + eps = sim->field_eps + z * fi->zstr1 + y * fi->ystr1 + + x * fi->xstr1; + mu = sim->field_mu + z * fi->zstr1 + y * fi->ystr1 + + x * fi->xstr1; + + *eps = 1.129409066837282e+11; + *mu = -7.957747154594767e+5; + +/* + if (x < border_width) { + border_fx = (float)x / border_width; + } else if (x >= fi->width - border_width) { + border_fx = 1.0f - ((float)x - fi->width + border_width + 1) / border_width; + } else { + border_fx = 1.0f; + } + + if (y < border_width) { + border_fy = (float)y / border_width; + } else if (y >= fi->height - border_width) { + border_fy = 1.0f - ((float)y - fi->height + border_width + 1) / border_width; + } else { + border_fy = 1.0f; + } + + if (z < border_width) { + border_fz = (float)y / border_width; + } else if (z >= fi->depth - border_width) { + border_fz = 1.0f - ((float)z - fi->depth + border_width + 1) / border_width; + } else { + border_fz = 1.0f; + } + + border_f = min3(border_fx, border_fy, border_fz); + + *eps *= border_f; + *mu *= border_f;*/ + } +} + +void phy_sim_step_1(phy_field_info *fi, float dt, float *mul_const, + const float *L0, float *L2, const float *R) +{ + size_t x, y, z; + +#define AT(F, x, y, z) ((F) + (z) * fi->zstr + (y) * fi->ystr + (x) * fi->xstr) +#define AT1(F, x, y, z) ((F)[(z) * fi->zstr1 + (y) * fi->ystr1 + \ + (x) * fi->xstr1]) + + // X + for (z = 1; z < fi->depth - 1; z++) + for (y = 1; y < fi->height - 1; y++) + for (x = 0; x < fi->width; x++) { + float dRzdy, dRydz, dLxdt; + + dRzdy = AT(R, x, y + 1, z)[2] - AT(R, x, y - 1, z)[2]; + dRzdy /= 2 * fi->spacing; + + dRydz = AT(R, x, y, z + 1)[1] - AT(R, x, y, z - 1)[1]; + dRydz /= 2 * fi->spacing; + + dLxdt = AT1(mul_const, x, y, z) * (dRzdy - dRydz); + + AT(L2, x, y, z)[0] = AT(L0, x, y, z)[0] + dLxdt * dt; + } + + // Y + for (z = 1; z < fi->depth - 1; z++) + for (y = 0; y < fi->height; y++) + for (x = 1; x < fi->width - 1; x++) { + float dRxdz, dRzdx, dLydt; + + dRxdz = AT(R, x, y, z + 1)[0] - AT(R, x, y, z - 1)[0]; + dRxdz /= 2 * fi->spacing; + + dRzdx = AT(R, x + 1, y, z)[2] - AT(R, x - 1, y, z)[2]; + dRzdx /= 2 * fi->spacing; + + dLydt = AT1(mul_const, x, y, z) * (dRxdz - dRzdx); + + AT(L2, x, y, z)[1] = AT(L0, x, y, z)[1] + dLydt * dt; + } + + // Z + for (z = 0; z < fi->depth; z++) + for (y = 1; y < fi->height - 1; y++) + for (x = 1; x < fi->width - 1; x++) { + float dRydx, dRxdy, dLzdt; + + dRydx = AT(R, x + 1, y, z)[1] - AT(R, x - 1, y, z)[1]; + dRydx /= 2 * fi->spacing; + + dRxdy = AT(R, x, y + 1, z)[0] - AT(R, x, y - 1, z)[0]; + dRxdy /= 2 * fi->spacing; + + dLzdt = AT1(mul_const, x, y, z) * (dRydx - dRxdy); + + AT(L2, x, y, z)[2] = AT(L0, x, y, z)[2] + dLzdt * dt; + } +#undef AT +#undef AT1 +} + +void phy_sim_add_sources(phy_sim *sim) +{ + phy_field_info *fi = &sim->field_info; + size_t x, y, z; + + for (z = 0; z < fi->depth; z++) + for (y = 0; y < fi->height; y++) + for (x = 0; x < fi->width; x++) { + float *E, *H, fx, fy, fz, chuj; + + fx = (float)x / fi->width * 2 - 1; + fy = (float)y / fi->height * 2 - 1; + fz = (float)z / fi->depth * 2 - 1; + + E = sim->fields[2].E + z * fi->zstr + y * fi->ystr + x * fi->xstr; + H = sim->fields[2].H + z * fi->zstr + y * fi->ystr + x * fi->xstr; + + chuj = (0.3 - sqrt(fx * fx + fy * fy + fz * fz)) / 0.3; + if (chuj < 0) + chuj = 0; + + chuj *= sin(sim->time) * 10; + //chuj *= exp(-10.0f * pow(sim->time - 0.5, 2)) * 2; + E[0] += chuj; + } +} + +void phy_sim_step(phy_sim *sim, float dt) { + phy_field_info *fi = &sim->field_info; + phy_field_em *fields = sim->fields; + + size_t i, size_in_bytes; + + phy_sim_add_sources(sim); + + size_in_bytes = fi->size * sizeof(float); + + SDL_mutexP(sim->rotate_lock); + for (i = 0; i < 2; i++) { + // TODO: avoid copying, just switch around pointers + memcpy(fields[i].E, fields[i + 1].E, size_in_bytes); + memcpy(fields[i].H, fields[i + 1].H, size_in_bytes); + } + SDL_mutexV(sim->rotate_lock); + + phy_sim_step_1(fi, dt, sim->field_eps, + fields[0].E, fields[2].E, fields[1].H); + phy_sim_step_1(fi, dt, sim->field_mu, + fields[0].H, fields[2].H, fields[1].E); + + sim->time += dt; +} + +/* +void phy_control(phy_command cmd, void *data) + +} */ + +int phy_thread(phy_sim *sim) +{ + while (1) { + phy_sim_step(sim, 0.1); + } + + printf("phy_thread: quitting\n"); +} + diff --git a/src/renderer.c b/src/renderer.c new file mode 100644 index 0000000..11c3706 --- /dev/null +++ b/src/renderer.c @@ -0,0 +1,473 @@ +#define _RENDERER_C +#include "common.h" +#include <SDL2/SDL_image.h> + +#define FONT_FILE "assets/font.png" + +float r_color_white[4] = {1, 1, 1, 1}; +float r_color_black[4] = {0, 0, 0, 1}; +float r_color_red[4] = {1, 0, 0, 1}; +float r_color_blue[4] = {0, 0, 1, 1}; + +static struct { + SDL_Surface *font; + float font_aspect_ratio; + + r_window *windows; +} rs; + +int r_init(void) +{ + memset(&rs, 0, sizeof(rs)); + + con_printf("r_init: initializing SDL...\n"); + if (SDL_Init(SDL_INIT_VIDEO) == -1) { + con_printf("r_init: SDL_Init(SDL_INIT_VIDEO) failed: %s\n", + SDL_GetError()); + goto error; + } + + con_printf("r_init: initializing SDL_image...\n"); + if (IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG) != + (IMG_INIT_JPG | IMG_INIT_PNG)) { + con_printf("r_init: IMG_Init failed: %s\n", SDL_GetError()); + goto error; + } + + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "2"); + + con_printf("r_init: loading font \""FONT_FILE"\"...\n"); + rs.font = IMG_Load("assets/font.png"); + if (!rs.font) { + con_printf("r_init: IMG_Load failed: %s\n", + IMG_GetError()); + goto error; + } + + rs.font_aspect_ratio = (float)rs.font->w / rs.font->h; + + con_printf("r_init: renderer init complete\n"); + return 0; + +error: + r_quit(); + return 1; +} + +r_window *r_window_create(void) +{ + r_window *rw; + + rw = calloc(1, sizeof(r_window)); + if (!rw) + abort(); + + rw->prev = NULL; + if (rs.windows) + rs.windows->prev = rw; + rw->next = rs.windows; + rs.windows = rw; + + rw->window = SDL_CreateWindow(PROGRAM_NAME, SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, 300, 300, + SDL_WINDOW_RESIZABLE); + if (!rw->window) { + con_printf("r_window_create: SDL_CreateWindow failed: %s\n", + SDL_GetError()); + goto error; + } + + rw->renderer = SDL_CreateRenderer(rw->window, -1, + SDL_RENDERER_ACCELERATED | + SDL_RENDERER_PRESENTVSYNC); + if (!rw->renderer) { + con_printf("r_window_create: SDL_CreateRenderer failed: %s\n", + SDL_GetError()); + goto error; + } + + SDL_SetRenderDrawBlendMode(rw->renderer, SDL_BLENDMODE_BLEND); + + rw->font_texture = SDL_CreateTextureFromSurface(rw->renderer, rs.font); + if (!rw->font_texture) { + con_printf("r_window_create: SDL_CreateTextureFromSurface" + " failed: %s\n", SDL_GetError()); + goto error; + } + + con_printf("r_window_create: created window #%i\n", + SDL_GetWindowID(rw->window)); + + return rw; + +error: + r_window_destroy(rw); + return NULL; +} + +void r_window_destroy(r_window *rw) +{ + SDL_DestroyTexture(rw->font_texture); + SDL_DestroyRenderer(rw->renderer); + SDL_DestroyWindow(rw->window); + + if (rw->prev) + rw->prev->next = rw->next; + if (rw->next) + rw->next->prev = rw->prev; + + if (rs.windows == rw) + rs.windows = rw->next; + + free(rw); +} + + +void r_quit(void) +{ + con_printf("r_quit: renderer is quitting\n"); + + while (rs.windows) + r_window_destroy(rs.windows); + + SDL_FreeSurface(rs.font); + IMG_Quit(); +} + +static void r_set_color(SDL_Renderer *renderer, float *color) +{ + SDL_SetRenderDrawColor(renderer, color[0] * 255, color[1] * 255, + color[2] * 255, color[3] * 255); +} + + +void r_clear(r_window *rw, float *color) +{ + r_set_color(rw->renderer, color); + SDL_RenderClear(rw->renderer); +} + +void r_flip(r_window *rw) +{ + SDL_RenderPresent(rw->renderer); +} + +void r_clip_enable(r_window *rw, float x, float y, float w, float h) +{ + SDL_Rect rect = {x, y, w, h}; + SDL_RenderSetClipRect(rw->renderer, &rect); +} + +void r_clip_disable(r_window *rw) +{ + SDL_RenderSetClipRect(rw->renderer, NULL); +} + +void r_draw_line(r_window *rw, float x0, float y0, float x1, float y1, + float *color) +{ + + r_set_color(rw->renderer, color); + SDL_RenderDrawLine(rw->renderer, x0, y0, x1, y1); +} + +void r_draw_rect(r_window *rw, float x, float y, float w, float h, float *color) +{ + SDL_Rect rect = {x, y, w, h}; + r_set_color(rw->renderer, color); + SDL_RenderFillRect(rw->renderer, &rect); +} + +static int r_iterate_chars(char **p) +{ + int rv; + + if ((*p)[0] == '\0') { + return -1; + } else if (((*p)[0] & 0b10000000) == 0b00000000) { + rv = (*p)[0]; + (*p)++; + return rv; + } else if (((*p)[0] & 0b11100000) == 0b11000000) { + if ((*p)[1] == '\0') + return -1; + + rv = (((*p)[0] & 0b00011111) << 6) | ((*p)[1] & 0b00111111); + (*p) += 2; + return rv; + } else { + (*p)++; + return 0; + } +} + +static int r_font_find_glyph(int ch) +{ + if (ch < 128) + return ch; + + switch (ch) { + case 0x105: return 128; // ą + case 0xE4: return 129; // ä + case 0x107: return 130; // ć + case 0x119: return 131; // ę + case 0xEB: return 132; // ë + case 0x142: return 133; // ł + case 0x144: return 134; // ń + case 0xF3: return 135; // ó + case 0xF6: return 136; // ö + case 0x15B: return 137; // ś + case 0xFC: return 138; // ü + case 0x17C: return 139; // ż + case 0x17A: return 140; // ź + //case 0x: return 141; // free + //case 0x: return 142; // free + //case 0x: return 143; // free + case 0x104: return 144; // Ą + case 0xC4: return 145; // Ä + case 0x106: return 146; // Ć + case 0x118: return 147; // Ę + case 0xCB: return 148; // Ë + case 0x141: return 149; // Ł + case 0x143: return 150; // Ń + case 0xD3: return 151; // Ó + case 0xD6: return 152; // Ö + case 0x15A: return 153; // Ś + case 0xDC: return 154; // Ü + case 0x17B: return 155; // Ż + case 0x179: return 156; // Ź + //case 0x: return 157; // free + //case 0x: return 158; // free + //case 0x: return 159; // free + } + + // lowercase greek + if (ch >= 0x3B1 && ch <= 0x3C1) + return ch - 0x3B1 + 160; + else if (ch >= 0x3C3 && ch <= 0x3C9) + return ch - 0x3C3 + 177; + + // uppercase greek + if (ch >= 0x391 && ch <= 0x3A1) + return ch - 0x391 + 192; + else if (ch >= 0x3A3 && ch <= 0x3A9) + return ch - 0x3A3 + 209; + + return 0; +} + +float r_text_width(float h, char *text) +{ + char *p = text; + float rv = 0; + + while(r_iterate_chars(&p) != -1) + rv += h * rs.font_aspect_ratio; + + return rv; +} + +void r_draw_text(r_window *rw, float x, float y, float h, char *text, + float *color, int flags) +{ + char *p = text; + float w = h * rs.font_aspect_ratio; + int ch; + float total_width; + + if (flags & (TEXT_CENTERX | TEXT_RIGHTX)) { + total_width = r_text_width(h, text); + + if (flags & TEXT_CENTERX) + x -= total_width / 2; + else + x -= total_width; + } + + SDL_SetTextureColorMod(rw->font_texture, color[0] * 255, color[1] * 255, + color[2] * 255); + SDL_SetTextureAlphaMod(rw->font_texture, color[3] * 255); + + while ((ch = r_iterate_chars(&p)) != -1) { + int glyph, glyph_x, glyph_y; + SDL_Rect src, dst; + + glyph = r_font_find_glyph(ch); + glyph_x = glyph % 16; + glyph_y = glyph / 16; + src.x = rs.font->w / 16 * glyph_x; + src.y = rs.font->h / 16 * glyph_y; + src.w = rs.font->w / 16; + src.h = rs.font->h / 16; + + dst.x = x; + dst.y = y; + dst.w = w; + dst.h = h; + + SDL_RenderCopy(rw->renderer, rw->font_texture, &src, &dst); + + x += w; + } +} + +static uint8_t r_float_to_u8(float num) +{ + if (num >= 1.0f) + return 255; + else if (num <= -1.0f) + return 0; + else + return floor((num + 1.0f) * 0.5f * 256.0f); +} + +static int r_xsection_alloc(SDL_Renderer *renderer, SDL_Texture **texture, + size_t width, size_t height) +{ + int old_width, old_height; + + if (*texture) { + SDL_QueryTexture(*texture, NULL, NULL, &old_width, &old_height); + + if (old_width != width || old_height != height) { + SDL_DestroyTexture(*texture); + *texture = NULL; + } + } + + if (!*texture) { + *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGR888, + SDL_TEXTUREACCESS_STREAMING, + width, height); + if (!*texture) { + con_printf("r_xsection_alloc: SDL_CreateTexture failed" + ": %s\n", SDL_GetError()); + return 1; + } + } + + return 0; +} + +void r_xsection_create(r_xsection *xsection) +{ + memset(xsection, 0, sizeof(r_xsection)); +} + +void r_xsection_destroy(r_xsection *xsection) +{ + SDL_DestroyTexture(xsection->texture); +} + +int r_xsection_update(r_window *rw, r_xsection *xsection, + phy_field_info *fi, float *field, + r_xsection_type type, float frac) +{ + size_t width, height, x, y, z; + uint8_t *pixels; + int pitch; + + switch (type) { + case XSECTION_XY: + width = fi->width; + height = fi->height; + break; + + case XSECTION_XZ: + width = fi->width; + height = fi->depth; + break; + + case XSECTION_YZ: + width = fi->height; + height = fi->depth; + break; + } + + if (frac < 0.0f) + frac = 0.0f; + else if (frac > 1.0f) + frac = 1.0f; + + if (r_xsection_alloc(rw->renderer, &xsection->texture, + width, height)) { + return 1; + } + + SDL_LockTexture(xsection->texture, NULL, (void**)&pixels, &pitch); + + switch (type) { + case XSECTION_XY: + z = frac * fi->depth; + if (z >= fi->depth) + z = fi->depth - 1; + + for (y = 0; y < fi->height; y++) + for (x = 0; x < fi->width; x++) { + uint8_t *pixel; + float *point; + + pixel = pixels + (y * pitch + x * 4); + point = field + z * fi->zstr + y * fi->ystr + + x * fi->xstr; + + pixel[0] = r_float_to_u8(point[0]); + pixel[1] = r_float_to_u8(point[1]); + pixel[2] = r_float_to_u8(point[2]); + } + break; + + case XSECTION_XZ: + y = frac * fi->height; + if (y >= fi->height) + y = fi->height - 1; + + for (z = 0; z < fi->depth; z++) + for (x = 0; x < fi->width; x++) { + uint8_t *pixel; + float *point; + + pixel = pixels + (z * pitch + x * 4); + point = field + z * fi->zstr + y * fi->ystr + + x * fi->xstr; + + pixel[0] = r_float_to_u8(point[0]); + pixel[1] = r_float_to_u8(point[1]); + pixel[2] = r_float_to_u8(point[2]); + } + break; + + case XSECTION_YZ: + x = frac * fi->width; + if (x >= fi->width) + x = fi->width - 1; + + for (z = 0; z < fi->depth; z++) + for (y = 0; y < fi->height; y++) { + uint8_t *pixel; + float *point; + + pixel = pixels + (z * pitch + y * 4); + point = field + z * fi->zstr + y * fi->ystr + + x * fi->xstr; + + pixel[0] = r_float_to_u8(point[0]); + pixel[1] = r_float_to_u8(point[1]); + pixel[2] = r_float_to_u8(point[2]); + } + break; + } + + SDL_UnlockTexture(xsection->texture); + + xsection->aspect_ratio = (float)width / height; + + return 0; +} + +void r_xsection_draw(r_window *rw, r_xsection *xsection, + float x, float y, float w, float h) +{ + SDL_Rect dst = {x, y, w, h}; + SDL_RenderCopy(rw->renderer, xsection->texture, NULL, &dst); +} diff --git a/src/ui.c b/src/ui.c new file mode 100644 index 0000000..0f4477b --- /dev/null +++ b/src/ui.c @@ -0,0 +1,461 @@ +#include "common.h" + +struct { + float font_size; + float color_text[4]; + float color_text_light[4]; + float color_background[4]; + float color_main[4]; + float color_info[4]; + float color_selection[4]; +} theme = +{ + .font_size = 20, + .color_text = {0, 0, 0, 1}, + .color_text_light = {0.5, 0.5, 0.5, 1}, + .color_background = {0.1, 0.1, 0.1, 1}, + .color_main = {0.75, 0.75, 0.75, 1}, + .color_info = {1, 1, 1, 1}, + .color_selection = {1, 0, 0, 1} +}; + +#define MAX_INFO 1024 + +typedef struct { + bool dragging; + r_xsection_type xsection_type; + float xsection_frac; + bool selected; + vec3_t selection; + + bool info_valid; + int64_t info_time; + char info[MAX_INFO]; + + float margin_bottom, margin_top; + + mst2_t origin, scale; // in virtual space + mst2_t tf_v2s, tf_x2s; + r_xsection xsection; +} ui_simview; + +typedef enum { + UI_WINDOW_SIMVIEW +} ui_window_type; + +typedef struct ui_window_s ui_window; +struct ui_window_s { + int w, h; + r_window *rw; + ui_window *prev, *next; + + bool initialized; + int64_t last_frame; + size_t frame_count; + vec2_t mouse; + + ui_window_type type; + ui_simview simview; +}; + +static struct { + ui_window *windows; + + bool use_window_open_timer; + int64_t window_open_timer; +} uis; + + +int ui_init(void) +{ + return 0; +} + +static void ui_window_destroy(ui_window *uiw) +{ + con_printf("ui_window_destroy: destroying uiw=%p\n", uiw); + + if (uiw->prev) + uiw->prev->next = uiw->next; + if (uiw->next) + uiw->next->prev = uiw->prev; + + if (uis.windows == uiw) + uis.windows = uiw->next; + + r_xsection_destroy(&uiw->simview.xsection); + + free(uiw); +} + +void ui_quit(void) +{ + while (uis.windows) + ui_window_destroy(uis.windows); +} + +void ui_renderer_window_register(r_window *rw) +{ + ui_window *uiw; + + uiw = calloc(1, sizeof(ui_window)); + if (!uiw) + abort(); + + uiw->prev = NULL; + if (uis.windows) + uis.windows->prev = uiw; + uiw->next = uis.windows; + uis.windows = uiw; + + uiw->rw = rw; + + con_printf("ui_renderer_window_register: registered window #%i" + ", uiw = %p\n", SDL_GetWindowID(rw->window), uiw); + + r_xsection_create(&uiw->simview.xsection); +} + +static ui_window *ui_find_window(int id) +{ + ui_window *uiw; + + for (uiw = uis.windows; uiw; uiw = uiw->next) + if (SDL_GetWindowID(uiw->rw->window) == id) + return uiw; + + return NULL; +} + +void ui_infof(ui_window *uiw, const char *fmt, ...) +{ + va_list vl; + + va_start(vl, fmt); + vsnprintf(uiw->simview.info, MAX_INFO, fmt, vl); + va_end(vl); + + uiw->simview.info_valid = true; + uiw->simview.info_time = get_time(); +} + +static void ui_simview_set_selection(ui_simview *sv, vec2_t sel_2d) +{ + if (sel_2d[0] < 0.0f || sel_2d[0] > 1.0f || + sel_2d[1] < 0.0f || sel_2d[1] > 1.0f) { + sv->selected = false; + return; + } + + switch (sv->xsection_type) { + case XSECTION_XY: + v3_set(sv->selection, sel_2d[0], sel_2d[1], sv->xsection_frac); + break; + + case XSECTION_XZ: + v3_set(sv->selection, sel_2d[0], sv->xsection_frac, sel_2d[1]); + break; + + case XSECTION_YZ: + v3_set(sv->selection, sv->xsection_frac, sel_2d[0], sel_2d[1]); + break; + } + + sv->selected = true; +} + +void ui_event_window(SDL_Event *event, ui_window *uiw) +{ + ui_simview *sv = &uiw->simview; + + if (!uiw->initialized) + return; + + switch (event->type) { + case SDL_MOUSEWHEEL: + { + vec2_t mouse_v; + float delta; + + v2_div_mst2(mouse_v, uiw->mouse, sv->tf_v2s); + + delta = pow(1.1, event->wheel.y); + + v2_mul(sv->scale, sv->scale, delta); + + v2_sub(sv->origin, sv->origin, mouse_v); + v2_mul(sv->origin, sv->origin, delta); + v2_add(sv->origin, sv->origin, mouse_v); + } + break; + + case SDL_MOUSEBUTTONDOWN: + if (event->button.button == SDL_BUTTON_LEFT && + event->button.y > sv->margin_top && + event->button.y < uiw->h - sv->margin_bottom) + sv->dragging = true; + + if (event->button.button == SDL_BUTTON_RIGHT) { + vec2_t sel_2d; + + v2_div_mst2(sel_2d, uiw->mouse, sv->tf_x2s); + ui_simview_set_selection(sv, sel_2d); + } + break; + + case SDL_MOUSEBUTTONUP: + if (event->button.button == SDL_BUTTON_LEFT) + sv->dragging = false; + break; + + case SDL_MOUSEMOTION: + uiw->mouse[0] = event->motion.x; + uiw->mouse[1] = event->motion.y; + + if (sv->dragging) { + vec2_t delta_v; + + v2_set(delta_v, event->motion.xrel, event->motion.yrel); + v2_div_mst2_nt(delta_v, delta_v, sv->tf_v2s); + v2_add(sv->origin, sv->origin, delta_v); + } + break; + + case SDL_KEYDOWN: + switch (event->key.keysym.sym) { + case SDLK_1: + sv->xsection_type = XSECTION_XY; + break; + + case SDLK_2: + sv->xsection_type = XSECTION_XZ; + break; + + case SDLK_3: + sv->xsection_type = XSECTION_YZ; + break; + + case SDLK_w: + sv->xsection_frac += 0.1f; + if (sv->xsection_frac > 1.0f) + sv->xsection_frac = 1.0f; + break; + + case SDLK_s: + sv->xsection_frac -= 0.1f; + if (sv->xsection_frac < 0.0f) + sv->xsection_frac = 0.0f; + break; + } + } +} + +void ui_event(SDL_Event *event) +{ + if (event->type == SDL_KEYDOWN && + event->key.keysym.sym == SDLK_RETURN) { + + if (!uis.use_window_open_timer || + uis.window_open_timer + 500000000 <= get_time()) { + r_window *rw; + + uis.use_window_open_timer = true; + uis.window_open_timer = get_time(); + + rw = r_window_create(); + if (rw) + ui_renderer_window_register(rw); + } + } else if (event->type == SDL_WINDOWEVENT && + event->window.event == SDL_WINDOWEVENT_CLOSE) { + ui_window *uiw; + + uiw = ui_find_window(event->window.windowID); + if (!uiw) + con_printf("WARNING: ui_event: SDL_WINDOWEVENT_" + "CLOSE for a window not registered" + " by ui\n"); + else { + r_window_destroy(uiw->rw); + ui_window_destroy(uiw); + } + } else { + ui_window *uiw; + + // SDL sends some SDL_WINDOWEVENTs after a window's been closed + + uiw = ui_find_window(event->window.windowID); + if (uiw) + ui_event_window(event, uiw); + } +} + +void ui_animate_exp(float *val, float targ, float lambda, float dt) +{ + *val = targ + (*val - targ) * exp(-lambda * dt); +} + +void ui_draw_window_simview(ui_window *uiw, phy_sim *sim, int64_t time, + float dt) +{ + ui_simview *sv = &uiw->simview; + float aspect_ratio; + vec2_t origin_s, scale_s; + phy_field_em *field_em = sim->fields + 2; + + const char *xsection_type_strings[] = { + "Przekrój XY przez Z", + "Przekrój XZ przez Y", + "Przekrój YZ przez X" + }; + + // xsection + + SDL_mutexP(sim->rotate_lock); + r_xsection_update(uiw->rw, &sv->xsection, &sim->field_info, + field_em->E, sv->xsection_type, sv->xsection_frac); + SDL_mutexV(sim->rotate_lock); + + aspect_ratio = sv->xsection.aspect_ratio; + + if (uiw->w > uiw->h * aspect_ratio) + mst2_set(sv->tf_v2s, (uiw->w - uiw->h * aspect_ratio) / 2.0f, 0, + uiw->h * aspect_ratio, uiw->h); + else + mst2_set(sv->tf_v2s, 0, (uiw->h - uiw->w / aspect_ratio) / 2.0f, + uiw->w, uiw->w / aspect_ratio); + + v2_mul_mst2(origin_s, sv->origin, sv->tf_v2s); + v2_mul_mst2_nt(scale_s, sv->scale, sv->tf_v2s); + + mst2_set(sv->tf_x2s, origin_s[0], origin_s[1], + scale_s[0], scale_s[1]); + + r_xsection_draw(uiw->rw, &sv->xsection, origin_s[0], origin_s[1], + scale_s[0], scale_s[1]); + + // xsection - selection + + if (sv->selected) + { + vec2_t selection_s; + + switch (sv->xsection_type) { + case XSECTION_XY: + v2_set(selection_s, sv->selection[0], sv->selection[1]); + break; + + case XSECTION_XZ: + v2_set(selection_s, sv->selection[0], sv->selection[2]); + break; + + case XSECTION_YZ: + v2_set(selection_s, sv->selection[1], sv->selection[2]); + break; + } + + v2_mul_mst2(selection_s, selection_s, sv->tf_x2s); + + r_draw_line(uiw->rw, selection_s[0], 0, selection_s[0], uiw->h, + theme.color_selection); + r_draw_line(uiw->rw, 0, selection_s[1], uiw->w, selection_s[1], + theme.color_selection); + + + r_draw_rect(uiw->rw, 0, uiw->h - theme.font_size, + uiw->w, theme.font_size, theme.color_main); + + r_draw_text(uiw->rw, 0, uiw->h - theme.font_size, + theme.font_size, + va("x = [%f %f %f]", sv->selection[0], + sv->selection[1], sv->selection[2]), + theme.color_text, 0); + + sv->margin_bottom = 1 * theme.font_size; + } + else + sv->margin_bottom = 0; + + // status + + r_draw_rect(uiw->rw, 0, 0, uiw->w, theme.font_size, theme.color_main); + + r_draw_text(uiw->rw, uiw->w, 0, theme.font_size, + va("%.0fkl./s %04zu", 1.0f / dt, uiw->frame_count % 10000), + theme.color_text_light, TEXT_RIGHTX); + + r_draw_text(uiw->rw, 0, 0, theme.font_size, + va("%s = %f", xsection_type_strings[sv->xsection_type], + sv->xsection_frac), + theme.color_text, 0); + + // info text + + if (sv->info_valid && sv->info_time + 2000000000ll > time) + { + vec4_t color; + + if (sv->info_time + 1000000000ll >= time) + color[3] = 1.0f; + else + color[3] = 1.0f - (time - sv->info_time - 1000000000ll) + / 1.0e9f; + + v3_copy(color, theme.color_info); + + r_draw_rect(uiw->rw, 0, theme.font_size, uiw->w, + theme.font_size, color); + + v3_copy(color, theme.color_text); + + r_draw_text(uiw->rw, uiw->w / 2.0f, theme.font_size, + theme.font_size, sv->info, color, TEXT_CENTERX); + } +} + +void ui_draw_window(ui_window *uiw, phy_sim *sim) +{ + ui_simview *sv = &uiw->simview; + int64_t time; + float dt; + + time = get_time(); + + if (!uiw->initialized) { + uiw->initialized = true; + uiw->last_frame = time; + + v2_set(sv->origin, 0, 0); + v2_set(sv->scale, 1, 1); + sv->xsection_type = XSECTION_XY; + sv->xsection_frac = 0.5f; + + sv->selected = false; + sv->margin_top = theme.font_size; + sv->margin_bottom = 0; + + sv->info_valid = false; + + r_clear(uiw->rw, r_color_black); + return; + } + + dt = (get_time() - uiw->last_frame) * 1.0e-9; + + r_clear(uiw->rw, r_color_black); + ui_draw_window_simview(uiw, sim, time, dt); + r_flip(uiw->rw); + + uiw->last_frame = time; +} + +void ui_draw(phy_sim *sim) +{ + ui_window *uiw; + + for (uiw = uis.windows; uiw; uiw = uiw->next) { + SDL_GetWindowSize(uiw->rw->window, &uiw->w, &uiw->h); + ui_draw_window(uiw, sim); + uiw->frame_count++; + } +} |