#ifndef SHADER_AUTOMATON_CLI_PARSE_H #define SHADER_AUTOMATON_CLI_PARSE_H #include "color.h" #include "colormaps/colormaps.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include enum class ColorMapScale { INHERENT, MAX_AGE, GLOBAL_MAX_AGE, }; struct ColorMapOption { ColorMapOption(const ColorMapOption &) = default; explicit ColorMapOption(std::variant source) : ColorMapOption(source, false, ColorMapScale::INHERENT) {} ColorMapOption(std::variant source, std::variant scale) : ColorMapOption(source, false, scale) {} ColorMapOption(std::variant source, bool invert) : ColorMapOption(source, invert, ColorMapScale::INHERENT) {} ColorMapOption(std::variant source, std::variant scale, bool invert) : ColorMapOption(source, invert, scale) {} ColorMapOption(std::variant source, bool invert, std::variant scale) : invert(invert), scale(scale), source(source) {} std::variant source; std::variant scale; bool invert; }; struct GridInitialiser { virtual void setup(unsigned int width, unsigned int height) { this->grid_width = width; this->grid_height = height; } virtual bool getCell(unsigned int x, unsigned int y) = 0; unsigned int grid_width{}; unsigned int grid_height{}; }; struct RectGridInitialiser : GridInitialiser { RectGridInitialiser(unsigned int width, unsigned int height) { this->width = width; this->height = height; } unsigned int width, height; bool getCell(unsigned int x, unsigned int y) override { auto margin_x = (grid_width - this->width) / 2; auto margin_y = (grid_height - this->height) / 2; return x >= margin_x && x < grid_width - margin_x && y >= margin_y && y < grid_height - margin_y; } }; struct EllipseGridInitialiser : GridInitialiser { EllipseGridInitialiser(double rx, double ry) { this->radius_x = rx; this->radius_y = ry; } double radius_x; double radius_y; bool getCell(unsigned int x, unsigned int y) override { auto width = static_cast(grid_width); auto height = static_cast(grid_height); auto X = static_cast(x) - (width / 2); auto Y = static_cast(y) - (height / 2); auto z = X * X / (radius_x * radius_x) + Y * Y / (radius_y * radius_y); return z <= 1; } }; struct PerlinGridInitialiser : GridInitialiser { PerlinGridInitialiser(double scale, double density) : scale(scale), density(density) { std::random_device r; std::default_random_engine e1(r()); const siv::PerlinNoise::seed_type seed = e1(); noise = siv::PerlinNoise{seed}; } bool getCell(unsigned int x, unsigned int y) override { auto val = noise.noise2D_01(x * scale, y * scale); return val >= (1. - density); } double scale, density; siv::PerlinNoise noise{}; }; struct MaxAgeProvider { public: virtual void setup(unsigned int width, unsigned int height) { this->grid_width = width; this->grid_height = height; } virtual uint16_t getAt(uint16_t x, uint16_t y, uint64_t generation) = 0; virtual uint16_t global_max() const = 0; virtual uint16_t global_min() const = 0; virtual bool is_dynamic() const { return false; } virtual uint16_t dynamic_step() const { return 0; } protected: unsigned int grid_width{}; unsigned int grid_height{}; }; struct UniformMaxAgeProvider : MaxAgeProvider { private: uint16_t value_; public: explicit UniformMaxAgeProvider(uint16_t value) : value_(value) {} uint16_t getAt(uint16_t x, uint16_t y, uint64_t generation) override { return value_; } [[nodiscard]] uint16_t global_max() const override { return value_; } [[nodiscard]] uint16_t global_min() const override { return value_; } [[nodiscard]] uint16_t value() const { return value_; } }; struct RadialMaxAgeProvider : MaxAgeProvider { private: uint16_t center_; uint16_t corners_; public: RadialMaxAgeProvider(uint16_t center, uint16_t corners) : center_(center), corners_(corners) {} uint16_t getAt(uint16_t x, uint16_t y, uint64_t generation) override { auto cx = static_cast(grid_width) / 2.; auto cy = static_cast(grid_height) / 2.; auto corner_center_distance = sqrt(pow(cx, 2.) + pow(cy, 2.)); auto px = static_cast(x); auto py = static_cast(y); auto point_center_distance = sqrt(pow(px - cx, 2.) + pow(py - cy, 2.)); auto center_val = static_cast(center_); auto corner_val = static_cast(corners_); auto val = center_val + point_center_distance / corner_center_distance * (corner_val - center_val); return static_cast(round(val)); } [[nodiscard]] uint16_t global_max() const override { return center_ > corners_ ? center_ : corners_; } [[nodiscard]] uint16_t global_min() const override { return center_ < corners_ ? center_ : corners_; } [[nodiscard]] uint16_t center() const { return center_; } [[nodiscard]] uint16_t corners() const { return corners_; } }; struct RadialFitMaxAgeProvider : MaxAgeProvider { private: uint16_t center_; uint16_t corners_; public: RadialFitMaxAgeProvider(uint16_t center, uint16_t corners) : center_(center), corners_(corners) {} uint16_t getAt(uint16_t x, uint16_t y, uint64_t generation) override { auto cx = static_cast(grid_width) / 2.; auto cy = static_cast(grid_height) / 2.; auto px = static_cast(x); auto py = static_cast(y); auto point_center_distance = sqrt(pow((px - cx) / cx, 2.) + pow((py - cy) / cy, 2.)); auto center_val = static_cast(center_); auto corner_val = static_cast(corners_); auto val = center_val + point_center_distance * (corner_val - center_val); return static_cast(round(val)); } [[nodiscard]] uint16_t global_max() const override { return center_ > corners_ ? center_ : corners_; } [[nodiscard]] uint16_t global_min() const override { return center_ < corners_ ? center_ : corners_; } [[nodiscard]] uint16_t center() const { return center_; } [[nodiscard]] uint16_t corners() const { return corners_; } }; struct PerlinStaticMaxAgeProvider : MaxAgeProvider { private: double scale_; uint16_t min_; uint16_t max_; siv::PerlinNoise noise{}; public: PerlinStaticMaxAgeProvider(double scale, uint16_t min, uint16_t max) : scale_(scale), min_(min), max_(max) { std::random_device r; std::default_random_engine e1(r()); const siv::PerlinNoise::seed_type seed = e1(); noise = siv::PerlinNoise{seed}; } uint16_t getAt(uint16_t x, uint16_t y, uint64_t generation) override { auto x_ = static_cast(x) * scale_; auto y_ = static_cast(y) * scale_; auto v = noise.noise2D_01(x_, y_); return static_cast(round(min_ + (max_ - min_) * v)); } [[nodiscard]] uint16_t global_max() const override { return max_; } [[nodiscard]] uint16_t global_min() const override { return min_; } [[nodiscard]] double scale() const { return scale_; } [[nodiscard]] uint16_t min() const { return min_; } [[nodiscard]] uint16_t max() const { return max_; } }; struct PerlinDynamicMaxAgeProvider : MaxAgeProvider { private: double scale_; uint16_t min_; uint16_t max_; double time_scale_; uint16_t time_step_; siv::PerlinNoise noise{}; public: PerlinDynamicMaxAgeProvider(double scale, uint16_t min, uint16_t max, double time_scale, uint16_t time_step) : scale_(scale), min_(min), max_(max), time_scale_(time_scale), time_step_(time_step) { std::random_device r; std::default_random_engine e1(r()); const siv::PerlinNoise::seed_type seed = e1(); noise = siv::PerlinNoise{seed}; } uint16_t getAt(uint16_t x, uint16_t y, uint64_t generation) override { auto x_ = static_cast(x) * scale_; auto y_ = static_cast(y) * scale_; auto t_ = static_cast(generation) / static_cast(time_step_) * time_scale_; auto v = noise.noise3D_01(x_, y_, t_); return static_cast(round(min_ + (max_ - min_) * v)); } [[nodiscard]] uint16_t global_max() const override { return max_; } [[nodiscard]] uint16_t global_min() const override { return min_; } [[nodiscard]] bool is_dynamic() const override { return true; } [[nodiscard]] uint16_t dynamic_step() const override { return time_step_; } [[nodiscard]] double scale() const { return scale_; } [[nodiscard]] uint16_t min() const { return min_; } [[nodiscard]] uint16_t max() const { return max_; } [[nodiscard]] double time_scale() const { return time_scale_; } [[nodiscard]] uint16_t time_step() const { return time_step_; } }; struct DisplayOptions { uint16_t width; uint16_t height; bool fullscreen; size_t fullscreen_screen_number; Color dead_color; std::variant alive_color; uint64_t generation_duration_ms; bool blend; }; struct AutomatonRule { std::vector birth; std::vector survive; std::shared_ptr max_age; uint8_t starve_delay; bool starve_recover; }; struct AutomatonOptions { uint16_t width; uint16_t height; // bool wrap_edges; AutomatonRule rule; std::shared_ptr initialiser; }; struct Options { AutomatonOptions automaton_options; DisplayOptions display_options; }; Options parse_arguments(int argc, const char *const *argv); #endif // SHADER_AUTOMATON_CLI_PARSE_H