#include "options.h" #include "argparser.h" #include "colormaps/colormaps.h" #include "rule_presets.h" #include #include #include static Color parse_hex_color(const char *begin, const char *end, const char *&parse_end, argparser::internal::parser_allow_undelimited) { if (begin >= end) { throw std::runtime_error("cannot parse empty string"); } if (*begin == '#') { throw std::runtime_error("input is not a hex color"); } if (end - begin < 7) { throw std::runtime_error("input is too short"); } uint8_t red, green, blue; auto res = std::from_chars(begin + 1, begin + 3, red, 16); if (res.ec != std::errc{} || res.ptr < begin + 3) { throw std::runtime_error("input is not a hex color"); } res = std::from_chars(begin + 3, begin + 5, green, 16); if (res.ec != std::errc{} || res.ptr < begin + 5) { throw std::runtime_error("input is not a hex color"); } res = std::from_chars(begin + 5, begin + 7, blue, 16); if (res.ec != std::errc{} || res.ptr < begin + 7) { throw std::runtime_error("input is not a hex color"); } return Color::from_rgb(red, green, blue); } Options do_parse_arguments(const std::vector &args) { std::vector colormap_names = {}; for (const auto &c : colormaps) { colormap_names.push_back(c.first); } std::vector preset_names = {}; for (const auto &preset_ruleset : rule_presets) { preset_names.push_back(preset_ruleset.first); } argparser::parser p{}; auto uint8 = p.basic_type("uint8"); auto uint16 = p.basic_type("uint16"); auto uint32 = p.basic_type("uint32"); auto doubleType = p.basic_type("float"); auto neighbourcount = p.basic_type("neighbourcount")->validate([](uint8_t val) { return val >= 0 && val <= 8; }); auto neighbourList = p.list_type("neighbourcount-list", neighbourcount); auto sizeType = p.tuple_type("size", uint16, uint16); auto boolType = p.enum_type("bool", {{"true", true}, {"false", false}}); auto hexColor = p.basic_type("hex-color", parse_hex_color); auto rgbColor = p.tuple_type("rgb", std::make_tuple(uint8, uint8, uint8), [](uint8_t r, uint8_t g, uint8_t b) { return Color::from_rgb(r, g, b); }); auto normalizedRgbColor = p.tuple_type("rgb-n", std::make_tuple(doubleType, doubleType, doubleType), [](double r, double g, double b) { return Color::from_rgb(r, g, b); }); auto labColor = p.tuple_type("lab", std::make_tuple(doubleType, doubleType, doubleType), [](double L, double a, double b) { return Color{L, a, b, ColorSpace::CIELAB}; }); auto luvColor = p.tuple_type("luv", std::make_tuple(doubleType, doubleType, doubleType), [](double L, double u, double v) { return Color{L, u, v, ColorSpace::CIELUV}; }); auto oklabColor = p.tuple_type("oklab", std::make_tuple(doubleType, doubleType, doubleType), [](double L, double a, double b) { return Color{L, a, b, ColorSpace::OKLAB}; }); auto color = p.union_type("color", hexColor, rgbColor, normalizedRgbColor, labColor, luvColor, oklabColor); auto colorList = p.list_type("colorlist", color); auto colormapScaleEnum = p.enum_type("colormap-scale-enum", {{"inherent", ColorMapScale::INHERENT}, {"max-age", ColorMapScale::MAX_AGE}, {"global-max-age", ColorMapScale::GLOBAL_MAX_AGE}}); auto colormapScale = p.union_type("colormap-scale", colormapScaleEnum, uint16); auto map = p.tuple_type("map", colorList); auto colormapName = p.enum_type("colormap-name", colormap_names); auto colormapDef = p.union_type("colormapdef", colormapName, map); auto colormapInvert = p.enum_type("colormap-invert", {{"invert", true}}); auto colormap1 = p.tuple_type("colormapopt1", colormapDef); auto colormap2 = p.tuple_type("colormapopt2", colormapDef, colormapScale); auto colormap3 = p.tuple_type("colormapopt3", colormapDef, colormapInvert); auto colormap4 = p.tuple_type("colormapopt4", colormapDef, colormapScale, colormapInvert); auto colormap5 = p.tuple_type("colormapopt5", colormapDef, colormapInvert, colormapScale); auto colormap = p.union_type("colormapopt", colormap1, colormap2, colormap3, colormap4, colormap5); auto colorOrColormap = p.union_type("color-or-colormap", color, colormap); auto initRect = p.tuple_type>("rect", std::make_tuple(uint16, uint16), [](uint16_t w, uint16_t h) { return std::make_shared(w, h); }); auto initSquare = p.tuple_type>("square", std::make_tuple(uint16), [](uint16_t w) { return std::make_shared(w, w); }); ; auto initEllipse = p.tuple_type>("ellipse", std::make_tuple(uint16, uint16), [](uint16_t a, uint16_t b) { return std::make_shared(a, b); }); auto initCircle = p.tuple_type>("circle", std::make_tuple(uint16), [](uint16_t r) { return std::make_shared(r, r); }); auto initPerlin = p.tuple_type>("perlin", std::make_tuple(doubleType, doubleType), [](double scale, double cutoff) { return std::make_shared(scale, cutoff); }); auto init = p.union_type("init", initRect, initSquare, initEllipse, initCircle, initPerlin); auto noMaxAge = p.enum_type>("none", {{"none", nullptr}}); auto uniformMaxAge = p.tuple_type>("uniform", std::make_tuple(uint16), [](uint16_t v) { return std::make_shared(v); }); auto radialMaxAge = p.tuple_type>("radial", std::make_tuple(uint16, uint16), [](uint16_t center, uint16_t corners) { return std::make_shared(center, corners); }); auto radialFitMaxAge = p.tuple_type>("radial-fit", std::make_tuple(uint16, uint16), [](uint16_t center, uint16_t corners) { return std::make_shared(center, corners); }); auto perlinStaticMaxAge = p.tuple_type>("perlin-static", std::make_tuple(doubleType, uint16, uint16), [](double scale, uint16_t min, uint16_t max) { return std::make_shared(scale, min, max); }); auto perlinDynamicMaxAge = p.tuple_type>("perlin-dynamic", std::make_tuple(doubleType, uint16, uint16, doubleType, uint16), [](double scale, uint16_t min, uint16_t max, double time_scale, uint16_t time_step) { return std::make_shared(scale, min, max, time_scale, time_step); }); auto maxAge = p.union_type("max-age", noMaxAge, uniformMaxAge, radialMaxAge, radialFitMaxAge, perlinStaticMaxAge, perlinDynamicMaxAge); auto presetName = p.enum_type("preset-name", preset_names); auto presetOpt = p.option("--preset", "-p", presetName); auto sizeOpt = p.option("--size", "-s", sizeType)->default_value(std::make_tuple(32, 32)); auto birthOpt = p.option("--birth", "-b", neighbourList)->default_value({}); auto surviveOpt = p.option("--survive", "-u", neighbourList)->default_value({}); auto maxAgeOpt = p.option("--max-age", "-a", maxAge)->default_value(nullptr); auto starveDelayOpt = p.option("--starve-delay", "-d", uint16)->default_value(0); auto starveRecoverOpt = p.option("--starve-recover", "-r", boolType)->default_value(false); auto initialiseOpt = p.option("--init", "-i", init)->default_value(std::make_shared(4, 4)); auto displaySizeOpt = p.option("--display-size", "-S", sizeType)->default_value(std::make_tuple(256, 256)); auto fullscreenFlag = p.flag("--fullscreen", "-F"); auto colorAliveOpt = p.option("--color-alive", "-A", colorOrColormap)->default_value(Color::from_rgb(1., 1., 1.)); auto colorDeadOpt = p.option("--color-dead", "-D", color)->default_value(Color::from_rgb(0., 0., 0.)); auto generationDurationOpt = p.option("--generation-duration", "-G", uint16)->default_value(0); auto blendFlag = p.flag("--blend", "-B"); auto pr = p.parse(args); Options options{}; auto size = sizeOpt->get(pr); options.automaton_options.width = std::get<0>(size); options.automaton_options.height = std::get<1>(size); if (initialiseOpt->has(pr)) { options.automaton_options.initialiser = initialiseOpt->get(pr); } bool uses_preset = false; if (presetOpt->has(pr)) { uses_preset = true; options.automaton_options.rule = rule_presets.at(presetOpt->get(pr)); } if (birthOpt->has(pr) || !uses_preset) { options.automaton_options.rule.birth = birthOpt->get(pr); } if (surviveOpt->has(pr) || !uses_preset) { options.automaton_options.rule.survive = surviveOpt->get(pr); } if (maxAgeOpt->has(pr) || !uses_preset) { options.automaton_options.rule.max_age = maxAgeOpt->get(pr); } if (starveDelayOpt->has(pr) || !uses_preset) { options.automaton_options.rule.starve_delay = starveDelayOpt->get(pr); } if (starveRecoverOpt->has(pr) || !uses_preset) { options.automaton_options.rule.starve_recover = starveRecoverOpt->get(pr); } auto displaySize = displaySizeOpt->get(pr); options.display_options.width = std::get<0>(displaySize); options.display_options.height = std::get<1>(displaySize); options.display_options.fullscreen = fullscreenFlag->get(pr); options.display_options.dead_color = colorDeadOpt->get(pr); options.display_options.alive_color = colorAliveOpt->get(pr); options.display_options.generation_duration_ms = generationDurationOpt->get(pr); options.display_options.blend = blendFlag->get(pr); return options; } Options parse_arguments(int argc, const char *const *argv) { std::vector args(argc - 1); for (size_t i = 1; i < argc; ++i) { args[i - 1] = std::string(argv[i]); } return do_parse_arguments(args); }