pretty-automata/src/options.h

301 lines
9.7 KiB
C++

#ifndef SHADER_AUTOMATON_CLI_PARSE_H
#define SHADER_AUTOMATON_CLI_PARSE_H
#include "color.h"
#include "colormaps/colormaps.h"
#include <PerlinNoise.hpp>
#include <algorithm>
#include <chrono>
#include <cstdint>
#include <format>
#include <iostream>
#include <map>
#include <memory>
#include <optional>
#include <random>
#include <stdexcept>
#include <string>
#include <variant>
#include <vector>
enum class ColorMapScale {
INHERENT,
MAX_AGE,
GLOBAL_MAX_AGE,
};
struct ColorMapOption {
ColorMapOption(const ColorMapOption &) = default;
explicit ColorMapOption(std::variant<std::string, ColorMapData> source) : ColorMapOption(source, false, ColorMapScale::INHERENT) {}
ColorMapOption(std::variant<std::string, ColorMapData> source, std::variant<ColorMapScale, uint16_t> scale) : ColorMapOption(source, false, scale) {}
ColorMapOption(std::variant<std::string, ColorMapData> source, bool invert) : ColorMapOption(source, invert, ColorMapScale::INHERENT) {}
ColorMapOption(std::variant<std::string, ColorMapData> source, std::variant<ColorMapScale, uint16_t> scale, bool invert) : ColorMapOption(source, invert, scale) {}
ColorMapOption(std::variant<std::string, ColorMapData> source, bool invert, std::variant<ColorMapScale, uint16_t> scale) : invert(invert), scale(scale), source(source) {}
std::variant<std::string, ColorMapData> source;
std::variant<ColorMapScale, uint16_t> 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<double>(grid_width);
auto height = static_cast<double>(grid_height);
auto X = static_cast<double>(x) - (width / 2);
auto Y = static_cast<double>(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<double>(grid_width) / 2.;
auto cy = static_cast<double>(grid_height) / 2.;
auto corner_center_distance = sqrt(pow(cx, 2.) + pow(cy, 2.));
auto px = static_cast<double>(x);
auto py = static_cast<double>(y);
auto point_center_distance = sqrt(pow(px - cx, 2.) + pow(py - cy, 2.));
auto center_val = static_cast<double>(center_);
auto corner_val = static_cast<double>(corners_);
auto val = center_val + point_center_distance / corner_center_distance * (corner_val - center_val);
return static_cast<uint16_t>(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<double>(grid_width) / 2.;
auto cy = static_cast<double>(grid_height) / 2.;
auto px = static_cast<double>(x);
auto py = static_cast<double>(y);
auto point_center_distance = sqrt(pow((px - cx) / cx, 2.) + pow((py - cy) / cy, 2.));
auto center_val = static_cast<double>(center_);
auto corner_val = static_cast<double>(corners_);
auto val = center_val + point_center_distance * (corner_val - center_val);
return static_cast<uint16_t>(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<double>(x) * scale_;
auto y_ = static_cast<double>(y) * scale_;
auto v = noise.noise2D_01(x_, y_);
return static_cast<uint16_t>(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<double>(x) * scale_;
auto y_ = static_cast<double>(y) * scale_;
auto t_ = static_cast<double>(generation) / static_cast<double>(time_step_) * time_scale_;
auto v = noise.noise3D_01(x_, y_, t_);
return static_cast<uint16_t>(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<Color, ColorMapOption> alive_color;
uint64_t generation_duration_ms;
bool blend;
};
struct AutomatonRule {
std::vector<uint8_t> birth;
std::vector<uint8_t> survive;
std::shared_ptr<MaxAgeProvider> 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<GridInitialiser> initialiser;
};
struct Options {
AutomatonOptions automaton_options;
DisplayOptions display_options;
};
Options parse_arguments(int argc, const char *const *argv);
#endif // SHADER_AUTOMATON_CLI_PARSE_H