From 937a2e0a2e145d23376c06d0ccdbc08326488093 Mon Sep 17 00:00:00 2001 From: VegOwOtenks Date: Sat, 1 Mar 2025 22:32:56 +0100 Subject: [PATCH] Implemented the program --- .gitmodules | 3 + CMakeLists.txt | 9 ++ src/CMakeLists.txt | 3 + src/spiral-galaxy.c | 209 ++++++++++++++++++++++++++++++++++++++ submodules/CMakeLists.txt | 1 + submodules/utilitiec | 1 + 6 files changed, 226 insertions(+) create mode 100644 .gitmodules create mode 100644 CMakeLists.txt create mode 100644 src/CMakeLists.txt create mode 100644 src/spiral-galaxy.c create mode 100644 submodules/CMakeLists.txt create mode 160000 submodules/utilitiec diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..820145f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "submodules/utilitiec"] + path = submodules/utilitiec + url = https://git.jossco.de/VegOwOtenks/utilitiec.git diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..10bd7aa --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.5.0) + +project(spiral-galaxy C) + +find_package(SDL3 REQUIRED CONFIG REQUIRED COMPONENTS SDL3-shared) + +add_subdirectory(submodules/) + +add_subdirectory(src/) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..5191a5d --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(spiral-galaxy WIN32 spiral-galaxy.c) + +target_link_libraries(spiral-galaxy PRIVATE SDL3::SDL3 rand) diff --git a/src/spiral-galaxy.c b/src/spiral-galaxy.c new file mode 100644 index 0000000..b8baf19 --- /dev/null +++ b/src/spiral-galaxy.c @@ -0,0 +1,209 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SDL_MAIN_USE_CALLBACKS +#include + +#include + +#define WINDOW_SIZE 600 +#define SWIRLINESS 35 +#define ARM_COUNT 3 +#define STAR_COUNT 2000 + +#include "../submodules/utilitiec/src/Vector/Vec2DF64.h" +#include "../submodules/utilitiec/src/rand/xoshiro256.h" + +static SDL_Window *window = NULL; +static SDL_Renderer *renderer = NULL; + +static SDL_Color star_colors[] = { + { 188, 66, 73, 255}, + { 252, 182, 79, 255}, + { 248, 215, 84, 255}, + { 213, 221, 234, 255}, + { 89, 159, 195, 255}, +}; + +static uint64_t star_color_weights[] = { + 3, + 5, + 8, + 13, + 21 +}; + +typedef struct Star { + Vec2DF64 position; + SDL_Color color; +} Star; + +static Star stars[STAR_COUNT]; + +double SDL_norm2(double x, double y) +{ + return SDL_sqrt(SDL_pow(x, 2) + SDL_pow(y, 2)); +} + +double Galaxy_ArmDistance(double x, double y, double swirliness, size_t arm_count) +{ + double angle = SDL_atan2(0.5 - y, 0.5 - x); + double center_distance = SDL_norm2(x - 0.5, y - 0.5); + double arm_distance = SDL_sin(swirliness * center_distance + angle * arm_count) + 1; + + return arm_distance / 2; +} + +SDL_Color SDL_ColorMix(SDL_Color self, SDL_Color other) +{ + return (SDL_Color) { + .r = (self.r + other.r) / 2, + .g = (self.g + other.g) / 2, + .b = (self.b + other.b) / 2, + .a = (self.a + other.a) / 2, + }; +} + +SDL_Color SDL_ColorLerp(SDL_Color self, SDL_Color other, uint progress) +{ + uint inverse_progress = 100 - progress; + return (SDL_Color) { + .r = (self.r * inverse_progress + other.r * progress) / 100, + .g = (self.g * inverse_progress + other.g * progress) / 100, + .b = (self.b * inverse_progress + other.b * progress) / 100, + .a = (self.a * inverse_progress + other.a * progress) / 100, + }; +} + + +size_t _SelectWeighed(uint64_t weights[], size_t weight_count, Xoshiro256State* state) +{ + uint64_t sum = 0; + for (size_t i = 0; i < weight_count; i++) sum += weights[i]; + + uint64_t chosen = xoshiro256_next(state) % sum; + + for (size_t i = 0; i < weight_count; i++) { + if (chosen < weights[i]) return i; + else chosen -= weights[i]; + } + + return SIZE_MAX; +} + +void GenerateStars() +{ + Xoshiro256State random_state = { 1, 2, 3, 4 }; + for (size_t i = 0; i < SDL_arraysize(stars); i++) { + Star* star = stars + i; + + double arm_distance; + uint64_t discard_probability; + do { + star->position.x = xoshiro256_next(&random_state) % WINDOW_SIZE; + star->position.y = xoshiro256_next(&random_state) % WINDOW_SIZE; + size_t color_index = _SelectWeighed( + star_color_weights, + SDL_arraysize(star_color_weights), + &random_state + ); + star->color = star_colors[color_index]; + + arm_distance = Galaxy_ArmDistance( + star->position.x / WINDOW_SIZE, + star->position.y / WINDOW_SIZE, + SWIRLINESS, + ARM_COUNT + ); + discard_probability = xoshiro256_next(&random_state) % 1000; + } while (arm_distance * 1000 > discard_probability); + } +} + +void SDL_RenderStar(Star* star) +{ + SDL_SetRenderDrawColor(renderer, star->color.r, star->color.g, star->color.b, star->color.a); + SDL_RenderPoint(renderer, star->position.y, star->position.x); + + SDL_Color white = { 255 }; + SDL_Color adjacent_color = SDL_ColorLerp(star->color, white, 5); + SDL_SetRenderDrawColor( + renderer, + adjacent_color.r, + adjacent_color.g, + adjacent_color.b, + adjacent_color.a + ); + SDL_RenderPoint(renderer, star->position.y - 1, star->position.x); + SDL_RenderPoint(renderer, star->position.y + 1, star->position.x); + SDL_RenderPoint(renderer, star->position.y, star->position.x - 1); + SDL_RenderPoint(renderer, star->position.y, star->position.x + 1); +} + +void SDL_RenderArms() +{ + for (size_t y = 0; y < WINDOW_SIZE; y++) { + double norm_y = (double) y / WINDOW_SIZE; + for (size_t x = 0; x < WINDOW_SIZE; x++) { + double norm_x = (double) x / WINDOW_SIZE; + double distance = Galaxy_ArmDistance(norm_x, norm_y, SWIRLINESS, ARM_COUNT); + uint progress = distance * 100; + SDL_Color black = { 0, 0, 0, 255 }; + SDL_Color grey = { 31, 31, 31, 255 }; + SDL_Color arm_color = SDL_ColorLerp(black, grey, 101 - progress); + SDL_SetRenderDrawColor(renderer, arm_color.r, arm_color.g, arm_color.b, arm_color.a); + SDL_RenderPoint(renderer, y, x); + } + } +} + +SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) +{ + GenerateStars(); + + if (!SDL_CreateWindowAndRenderer("Spiral Galaxy", WINDOW_SIZE, WINDOW_SIZE, 0, &window, &renderer)) { + SDL_Log("Couldn't create window and renderer: %s\n", SDL_GetError()); + return SDL_APP_FAILURE; + } + + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); + SDL_RenderClear(renderer); + + SDL_RenderArms(); + for (size_t i = 0; i < SDL_arraysize(stars); i++) { + SDL_RenderStar(stars + i); + } + + SDL_RenderPresent(renderer); + + + return SDL_APP_CONTINUE; +} + +SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) +{ + switch (event->type) { + case SDL_EVENT_QUIT: + return SDL_APP_SUCCESS; + + default: + break; + } + + return SDL_APP_CONTINUE; +} + +SDL_AppResult SDL_AppIterate(void *appstate) +{ + return SDL_APP_CONTINUE; +} + +void SDL_AppQuit(void *appstate, SDL_AppResult result) +{ +} diff --git a/submodules/CMakeLists.txt b/submodules/CMakeLists.txt new file mode 100644 index 0000000..e35c9a7 --- /dev/null +++ b/submodules/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(utilitiec/) diff --git a/submodules/utilitiec b/submodules/utilitiec new file mode 160000 index 0000000..e8bea89 --- /dev/null +++ b/submodules/utilitiec @@ -0,0 +1 @@ +Subproject commit e8bea8924bf48dd025ae2968b3885b9d6a1bc523