From d8dac1601b781bd3473c369192fc7dafbfc1c26d Mon Sep 17 00:00:00 2001 From: Brett Weiland Date: Sun, 30 Mar 2025 22:13:41 -0500 Subject: [PATCH] finished basic screen test, aboutta start converting to fixed point --- recon/screen_tests_c/.gdb_history | 17 ++++ recon/screen_tests_c/backup.c | 127 ++++++++++++++++++++++++++++++ recon/screen_tests_c/main.c | 127 ++++++++++++++++++------------ recon/screen_tests_c/makefile | 2 +- recon/screen_tests_c/mandelbrot | Bin 16104 -> 21560 bytes 5 files changed, 222 insertions(+), 51 deletions(-) create mode 100644 recon/screen_tests_c/.gdb_history create mode 100644 recon/screen_tests_c/backup.c diff --git a/recon/screen_tests_c/.gdb_history b/recon/screen_tests_c/.gdb_history new file mode 100644 index 0000000..4af8431 --- /dev/null +++ b/recon/screen_tests_c/.gdb_history @@ -0,0 +1,17 @@ +start +print Color +ptype Color +quit +break 58 +r +next +next +print key_pressed +print GetKeyPressed() +print (int)GetKeyPressed() +print (int)GetKeyPressed() +print (int)GetKeyPressed() +print (int)GetKeyPressed() +print (int)GetKeyPressed() +print (int)GetKeyPressed() +exit diff --git a/recon/screen_tests_c/backup.c b/recon/screen_tests_c/backup.c new file mode 100644 index 0000000..856e0f2 --- /dev/null +++ b/recon/screen_tests_c/backup.c @@ -0,0 +1,127 @@ +#include +#include +#include +#include +#include +#include + +#define RES_X 160 +#define RES_Y 80 +#define DEFAULT_CENTER_X 0 +#define DEFAULT_CENTER_Y 0 +#define MOUSE_BUTTON 0 +#define STEP_SIZE .25 +#define ZOOM_SIZE .25 + +#define INFTY 2 +#define INFTY_SQR INFTY * INFTY +#define ITERS 255 + +struct camera { + double min_r, min_i, max_r, max_i; +}; + +//blllluuuuurg, matracies and vectors in raylib are floats and we need doubles +void shift_cam(struct camera *cam, double step_r, double step_i) { + double i_offset = (cam->max_i - cam->min_i) * step_i; + double r_offset = (cam->max_r - cam->min_r) * step_r; + cam->min_i += i_offset; + cam->max_i += i_offset; + cam->min_r += r_offset; + cam->max_r += r_offset; +} + +void zoom_cam(struct camera *cam, double zoom) { + double i_scale = (cam->max_i - cam->min_i) * zoom; + double r_scale = (cam->max_r - cam->min_r) * zoom; + cam->min_i += i_scale; + cam->max_i -= i_scale; + cam->min_r += r_scale; + cam->max_r -= r_scale; +} + +int main() { + int key_pressed; + Color *pixels = malloc(RES_X * RES_Y * sizeof(Color)); + struct camera cam = { + .min_r = -1, + .max_r = 1, + .min_i = -1, + .max_i = 1 + }; + InitWindow(RES_X, RES_Y, "mandelbrot fixed point test"); + + Image img = GenImageColor(RES_X, RES_Y, BLUE); + Texture tex = LoadTextureFromImage(img); + UnloadImage(img); + + while(!WindowShouldClose()) { + switch(GetKeyPressed()) { + case KEY_UP: + shift_cam(&cam, 0, STEP_SIZE); + break; + case KEY_DOWN: + shift_cam(&cam, 0, -STEP_SIZE); + break; + case KEY_RIGHT: + shift_cam(&cam, STEP_SIZE, 0); + break; + case KEY_LEFT: + shift_cam(&cam, -STEP_SIZE, 0); + break; + case KEY_W: + printf("a\n"); + zoom_cam(&cam, ZOOM_SIZE); + break; + case KEY_S: + zoom_cam(&cam, -ZOOM_SIZE); + break; + default: + BeginDrawing(); + EndDrawing(); + continue; + break; + } + printf("(%f, %f) - (%f, %f)\n", cam.min_r, cam.min_i, cam.max_r, cam.max_i); + { + //double scale_i = (cam.max_i - cam.min_i) / (double)GetRenderHeight(); + //double scale_r = (cam.max_r - cam.min_r) / (double)GetRenderWidth(); + double scale_i = (cam.max_i - cam.min_i) / (double)RES_Y; + double scale_r = (cam.max_r - cam.min_r) / (double)RES_X; + double c_i = cam.max_i; + double c_r; + double z_i; + double z_r; + double zn_r, zn_i; + int i; + uint8_t color; + for(int y = 0; y < RES_Y; y++) { + c_r = cam.min_r; + for(int x = 0; x < RES_X; x++) { + z_i = 0; + z_r = 0; + for(i = 0; i < ITERS; i++) { + zn_r = pow(z_r, 2.0) - pow(z_i, 2.0) + c_r; + zn_i = (2.0 * z_r * z_i) + c_i; + z_i = zn_i; + z_r = zn_r; + if((pow(z_i, 2.0) + pow(z_r, 2.0)) > INFTY_SQR) { + break; + } + } + color = ((float)i / ITERS) * UINT8_MAX; + pixels[((y * RES_X) + x)] = (Color){color, color, color, 255}; + c_r += scale_r; + } + c_i -= scale_i; + } + } + + BeginDrawing(); + UpdateTexture(tex, pixels); + DrawTexture(tex, 0, 0, WHITE); + EndDrawing(); + } + + return 0; +} diff --git a/recon/screen_tests_c/main.c b/recon/screen_tests_c/main.c index be7b240..9c936e4 100644 --- a/recon/screen_tests_c/main.c +++ b/recon/screen_tests_c/main.c @@ -1,24 +1,33 @@ +#include #include +#include +#include #include #include -#define RES_X 160 -#define RES_Y 80 -#define DEFAULT_CENTER_X 0 -#define DEFAULT_CENTER_Y 0 -#define MOUSE_BUTTON 0 -#define STEP_SIZE .25 -#define ZOOM_SIZE .25 +#define RES_X 160 +#define RES_Y 80 +#define DEFAULT_CENTER_X 0 +#define DEFAULT_CENTER_Y 0 +#define MOUSE_BUTTON 0 +#define STEP_SIZE .25 +#define ZOOM_SIZE .25 -#define INFTY 4 +#define INFTY 2 #define INFTY_SQR INFTY * INFTY -#define ITERS 8 +#define ITERS 255 + +#define DECIMAL_LOC 26 +#define DOUBLE_SCALER (1 << DECIMAL_LOC) +#define DOUBLE_TO_FIXED(val) (uint32_t)(val << DECIMAL_LOC) +#define FIXED_MULTIPLY(x,y) ((uint64_t)(x)*(y)) >> DECIMAL_LOC + struct camera { double min_r, min_i, max_r, max_i; }; -//god damb maybe I shoulda made a vector class +//blllluuuuurg, matracies and vectors in raylib are floats and we need doubles void shift_cam(struct camera *cam, double step_r, double step_i) { double i_offset = (cam->max_i - cam->min_i) * step_i; double r_offset = (cam->max_r - cam->min_r) * step_r; @@ -39,67 +48,85 @@ void zoom_cam(struct camera *cam, double zoom) { int main() { int key_pressed; + Color *pixels = malloc(RES_X * RES_Y * sizeof(Color)); struct camera cam = { .min_r = -1, .max_r = 1, - .min_i = -1, - .max_i = 1 + // .min_i = -1, + // .max_i = 1 }; - InitWindow(160, 80, "mandelbrot fixed point test"); + cam.min_i = ((double)RES_Y / RES_X) * cam.min_r; + cam.max_i = ((double)RES_Y / RES_X) * cam.max_r; + InitWindow(RES_X, RES_Y, "mandelbrot fixed point test"); Image img = GenImageColor(RES_X, RES_Y, BLUE); Texture tex = LoadTextureFromImage(img); + UnloadImage(img); - SetTargetFPS(30); while(!WindowShouldClose()) { - if(key_pressed = GetKeyPressed()) { - switch(key_pressed) { - case KEY_UP: - shift_cam(&cam, 0, STEP_SIZE); - break; - case KEY_DOWN: - shift_cam(&cam, 0, -STEP_SIZE); - break; - case KEY_RIGHT: - shift_cam(&cam, STEP_SIZE, 0); - break; - case KEY_LEFT: - shift_cam(&cam, -STEP_SIZE, 0); - break; - case KEY_W: - printf("a\n"); - zoom_cam(&cam, ZOOM_SIZE); - break; - case KEY_S: - zoom_cam(&cam, -ZOOM_SIZE); - break; - default: - break; - } - printf("(%f, %f) - (%f, %f)\n", cam.min_r, cam.min_i, cam.max_r, cam.max_i); + switch(GetKeyPressed()) { + case KEY_UP: + shift_cam(&cam, 0, STEP_SIZE); + break; + case KEY_DOWN: + shift_cam(&cam, 0, -STEP_SIZE); + break; + case KEY_RIGHT: + shift_cam(&cam, STEP_SIZE, 0); + break; + case KEY_LEFT: + shift_cam(&cam, -STEP_SIZE, 0); + break; + case KEY_W: + printf("a\n"); + zoom_cam(&cam, ZOOM_SIZE); + break; + case KEY_S: + zoom_cam(&cam, -ZOOM_SIZE); + break; + default: + BeginDrawing(); + EndDrawing(); + continue; + break; } + printf("(%f, %f) - (%f, %f)\n", cam.min_r, cam.min_i, cam.max_r, cam.max_i); { - double scale_i = (cam.max_i - cam.min_i) / (double)GetScreenHeight(); - double scale_r = (cam.max_r - cam.min_r) / (double)GetScreenWidth(); - double c_i = cam.min_i; - double c_r = cam.min_r; - double z_i = 0; - double z_r = 0; + //double scale_i = (cam.max_i - cam.min_i) / (double)GetRenderHeight(); + //double scale_r = (cam.max_r - cam.min_r) / (double)GetRenderWidth(); + double scale_i = (cam.max_i - cam.min_i) / (double)RES_Y; + double scale_r = (cam.max_r - cam.min_r) / (double)RES_X; + double c_i = cam.max_i; + double c_r; + double z_i; + double z_r; + double zn_r, zn_i; + int i; + uint8_t color; for(int y = 0; y < RES_Y; y++) { - for(int x = 0; x < RES_Y; x++) { - for(int i = 0; i < ITERS; i++) { - - if(pow(c_i, 2) + pow(c_r, 2) >= INFTY_SQR) { + c_r = cam.min_r; + for(int x = 0; x < RES_X; x++) { + z_i = 0; + z_r = 0; + for(i = 0; i < ITERS; i++) { + zn_r = pow(z_r, 2.0) - pow(z_i, 2.0) + c_r; + zn_i = (2.0 * z_r * z_i) + c_i; + z_i = zn_i; + z_r = zn_r; + if((pow(z_i, 2.0) + pow(z_r, 2.0)) > INFTY_SQR) { break; } } + color = ((float)i / ITERS) * UINT8_MAX; + pixels[((y * RES_X) + x)] = (Color){color, color, color, 255}; c_r += scale_r; } - c_i += scale_i; + c_i -= scale_i; } } BeginDrawing(); + UpdateTexture(tex, pixels); DrawTexture(tex, 0, 0, WHITE); EndDrawing(); } diff --git a/recon/screen_tests_c/makefile b/recon/screen_tests_c/makefile index f01bedb..bacda0e 100644 --- a/recon/screen_tests_c/makefile +++ b/recon/screen_tests_c/makefile @@ -1,6 +1,6 @@ .default: make make: - gcc main.c -o mandelbrot -lraylib + gcc main.c -o mandelbrot -lraylib -lm -Wall -ggdb clean: rm -f mandelbrot diff --git a/recon/screen_tests_c/mandelbrot b/recon/screen_tests_c/mandelbrot index fcb80a4704e57646bf01cb61ed36463828a91a50..0c32b5b7e52881e2f14d0b749ef0fbd801584119 100755 GIT binary patch literal 21560 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 LiOvLt1d{z1!Q0@T delta 2904 zcmaJ@eQZX7*)eGO{q|~v_WlJjm1D|oK}@g|ELmGl|bq=qgKMCod#&r+qw7M z=Z3Y~wS3O`-Sa!=ex3L3o1UP#6Q}JBvX$`W)f9;~3ImmwY$Ok&tRX8?Eh%Y2(p(c@ zrOQ&^JY>FDqIFy>fePw!^ivJ$n#dZakfOCx+MuBf3{YErcAbW@&G+lP$dRY;0e#u} zyi;=*G=vNXtkF8Hx1iyY;eazOQl>>pU3dt zEVk1@ZcU=q#;tk3o!*0M#(h8E7Kw)sAB#jfcgNb}QBr_PBz7bkCtD&%Vx7KY2VaVH z9wED9o$XyOg`-_PN86t~+Vx_Dgd_214jwxaiEj;tp~KV$Go~u|!nE0R*!5zU>v4Fb z!~@Y1xsMX|DXbd5woW6VyOlR zsnCp!*k!$cQq)ne7fXgU-O&rq(zUb{0;M*GC?M{iZ{dpIY)BcLD4n5ZXqIenR4Oa? zdNm)buX6YW_MXMw;F4Yoye@^|x>1^%bhMh@k!1>XR_9aaU4(qs;j+{Y@&wAM<|a2G z$|>U`Of}t!S1BXLP9!PGbX-!eTWb3m@ruyMmpK11gByNhT|pm%32V)os~A9fHnb^; z<_cyi!+(#LD296Tx6112H(Wv9N<7g%Q85M5lirBu;4G{{;`6e<55q+U&8$} z=!TN)uF2cSRyJJ&fvR= zsKZx=U-v<^!?SrKGFipvX71pAnGq%ObTDm6rg6L3V}~c+mTOnmR`O>rZp`gs5hMHV z%w3rpa`c*}Wb-_IrEb@@ta$yf7}l~gbVbpoN6|9kfBKfHMo~}XRh2z^=WeU&Y1F$H zRdo_|>9?wyMNQyp-Kv@(vL0s0S0Xd@6a2GG3TDX@FgfcD(rsAdtf6nfc4w{4gIT<+ z7@l}ge%bJ3xk*VB6>TV<>T|9y1KHfPTD)WRcPc(mywwXs#~a}0x?1a`P4hYgD-8}; zEzLla%T2QoLwi3AyIN=v=3KSbJ>tDAu5L00cU>*cJp$t1cd<132)HngUGS`X)R1k) zi*ETVp64p?~C9$@5h5Ih*3Q|sF zSn!P+CT;M7|6%%PxPce&$8g(kqYKP-e2X-hCGmTf8^`9b#En`M3?`@Iua~}J{ zIef`ttM7@Z?qwRC7X_Lsg+FY2AAYGcz+aT}YnR^p-+wdw=qCg`yZyPT%iEn43}*jS z?^AV@UWec9a9gvitg-2OKP+IcOPsr|SA!$qUa41uuDv{x@T;0aB(O>v6LDjZ3N#jK zO0TUTECl*!9%zvbx_8*h2tz>`>_LyDe~n}y6l|=}D^UN)#E;1{!IN}I8z59I=~dJXp-`iw z*GE63LM^l(u7w&>lf`g6TxlKmXfv6>Yn8Q!;9S@(DeU3KW`u=sqdD-nhL%&P-cj>k DzHm$N