diff --git a/Makefile.am b/Makefile.am index f3ef99a3..5ce14be5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -16,6 +16,9 @@ UTIL_FILES = \ bezier.hpp \ bitwise.cpp \ bitwise.hpp \ + cmdopt.cpp \ + cmdopt.hpp \ + cmdopt.ipp \ colour.cpp \ colour.hpp \ colour.ipp \ diff --git a/cmdopt.cpp b/cmdopt.cpp new file mode 100644 index 00000000..968bdc7f --- /dev/null +++ b/cmdopt.cpp @@ -0,0 +1,263 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright 2013 Danny Robson + */ + +#include "cmdopt.hpp" + +#include "debug.hpp" + +#include +#include +#include + +using util::cmdopt::option::base; +using util::cmdopt::option::bytes; +using util::cmdopt::option::count; +using util::cmdopt::option::value; +using util::cmdopt::parser; + + +/////////////////////////////////////////////////////////////////////////////// +base::base (std::string _name): + m_name (_name) +{ ; } + + +//----------------------------------------------------------------------------- +base::~base () +{ ; } + + +//----------------------------------------------------------------------------- +void +base::execute (void) +{ + throw invalid_null (m_name); +} + + +//----------------------------------------------------------------------------- +void +base::execute (const char *restrict) +{ + throw invalid_value (m_name); +} + + +//----------------------------------------------------------------------------- +void +base::start (void) +{ ; } + + +//----------------------------------------------------------------------------- +void +base::finish (void) +{ ; } + + +//----------------------------------------------------------------------------- +std::string +base::name (void) const +{ + return m_name; +} + + +/////////////////////////////////////////////////////////////////////////////// +template +value::value (std::string _name, T &_data): + base (_name), + m_data (_data) +{ ; } + + +//----------------------------------------------------------------------------- +template +void +value::execute (const char *restrict str) +{ + try { + std::istringstream os (str); + os.exceptions ( + std::istringstream::failbit + | std::istringstream::badbit + ); + os >> m_data; + + if (!os.eof ()) + throw invalid_value (__func__); + } catch (...) { + throw invalid_value (__func__); + } +} + + +//----------------------------------------------------------------------------- +template +T +value::data (void) const +{ + return m_data; +} + + +//----------------------------------------------------------------------------- +template +T& +value::data (void) +{ + return m_data; +} + + +//----------------------------------------------------------------------------- +namespace util { namespace cmdopt { namespace option { + template class value; + template class value; + template class value; +} } } + + +/////////////////////////////////////////////////////////////////////////////// +template +void +count::execute (void) +{ + ++ this->data(); +} + + +//----------------------------------------------------------------------------- +namespace util { namespace cmdopt { namespace option { + template class count; +} } } + + +/////////////////////////////////////////////////////////////////////////////// +int +parser::scan (int argc, char *const *argv) +{ + CHECK_GE (argc, 0); + CHECK (argv); + + // start iterating after our program's name + int i = 1; + while (i < argc) { + auto len = strlen (argv[i]); + + // bail if there's no potential for an option + if (len < 2 || argv[i][0] != '-') + return i; + + // parse longopt + auto inc = argv[i][1] == '-' + ? parse_long (i, argc, argv) + : parse_short (i, argc, argv); + + CHECK_GT (inc, 0); + i += inc; + } + + return i; +} + + +//----------------------------------------------------------------------------- +int +parser::parse_long (int pos, int argc, char *const *argv) +{ + CHECK_LT (pos, argc); + CHECK_GE (pos, 0); + CHECK_GE (argc, 0); + CHECK (argv); + + CHECK_EQ (argv[pos][0], '-'); + CHECK_EQ (argv[pos][1], '-'); + + // break first atom into components and extract the key + const char *start = argv[pos] + 2; + const char *eq = strchr (start, '='); + const char *last = start + strlen (start); + + std::string key { start, eq ? eq : last }; + + // find the handler + auto handle_pos = std::find_if (m_long.begin (), + m_long.end (), + [&] (auto i) { return std::get<0> (i) == key; }); + if (handle_pos == m_long.end ()) + throw invalid_key (key); + + auto &handler = std::get<1> (*handle_pos); + + // maybe grab a value from the next atom and dispatch + if (!eq) { + // check the next atom for the value + if (pos + 1 < argc) + if (argv[pos + 1][0] != '-') { + handler.execute (argv[pos+1]); + return 2; + } + + handler.execute (); + } else { + handler.execute (eq+1); + } + + return 1; +} + + +//----------------------------------------------------------------------------- +int +parser::parse_short (int pos, int argc, char *const *argv) +{ + CHECK_LT (pos, argc); + CHECK_GE (pos, 0); + CHECK_GE (argc, 0); + CHECK (argv); + + CHECK_EQ (argv[pos][0], '-'); + CHECK_NEQ (argv[pos][1], '-'); + + // we have a run of no-value keys + auto len = strlen (argv[pos]); + if (len > 2 || pos + 1 == argc || argv[pos+1][0] == '-') { + for (size_t i = 1; i < len; ++i) { + auto letter = argv[pos][i]; + + auto hpos = std::find_if (m_short.begin (), + m_short.end (), + [letter] (auto j) { return std::get<0> (j) == letter; }); + if (hpos == m_short.end ()) + throw invalid_key (std::to_string (letter)); + std::get<1> (*hpos).execute (); + } + + return 1; + } + + // we have a value following + auto letter = argv[pos][1]; + auto hpos = std::find_if (m_short.begin (), + m_short.end (), + [letter] (auto i) { return std::get<0> (i) == letter; }); + if (hpos == m_short.end ()) + throw invalid_key (std::to_string (letter)); + std::get<1> (*hpos).execute (argv[pos+1]); + + return 2; +} diff --git a/cmdopt.hpp b/cmdopt.hpp new file mode 100644 index 00000000..4c87496c --- /dev/null +++ b/cmdopt.hpp @@ -0,0 +1,117 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright 2013 Danny Robson + */ + + +#ifndef __UTIL_CMDLINE_HPP +#define __UTIL_CMDLINE_HPP + +#include +#include +#include +#include +#include + +namespace util { namespace cmdopt { + namespace option { + class base { + public: + base (std::string name); + virtual ~base (); + + virtual void execute (void); + virtual void execute (const char *restrict); + virtual void start (void); + virtual void finish (void); + + std::string name (void) const; + + private: + std::string m_name; + }; + + + template + class value : public base { + public: + value (std::string name, T&); + + void execute (const char *restrict) override; + + T data (void) const; + T& data (void); + + private: + T& m_data; + }; + + + template + class count : public value { + public: + count (std::string name); + void execute (void) override; + }; + + + class bytes : public value { + public: + bytes (std::string name); + void execute (const char *restrict) override; + }; + } + + + class error : public std::runtime_error + { using runtime_error::runtime_error; }; + + class invalid_key : public error + { using error::error; }; + + class invalid_value : public error + { using error::error; }; + + class invalid_null : public error + { using error::error; }; + + + class parser { + public: + template + T& add (char shortname, + std::string longname, + std::string description, + Args&&...); + + int scan (int argc, char *const *argv); + + private: + int parse_long (int pos, int argc, char *const *argv); + int parse_short (int pos, int argc, char *const *argv); + + + using short_t = std::tuple; + using long_t = std::tuple; + + std::vector m_short; + std::vector m_long; + + std::vector> m_options; + }; +} } + +#include "cmdopt.ipp" + +#endif diff --git a/cmdopt.ipp b/cmdopt.ipp new file mode 100644 index 00000000..8b17c131 --- /dev/null +++ b/cmdopt.ipp @@ -0,0 +1,37 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright 2013 Danny Robson + */ + +#ifdef __UTIL_CMDLINE_IPP +#error +#endif +#define __UTIL_CMDLINE_IPP + +namespace util { namespace cmdopt { + template + T& + parser::add (char shortname, std::string longname, std::string description, Args&&... args) + { + auto handler = std::make_unique (longname, std::forward (args)...); + T& ref = *handler; + + m_short.emplace_back (shortname, ref); + m_long .emplace_back (longname, ref); + + m_options.emplace_back (std::move (handler)); + + return ref; + } +} } diff --git a/tools/noise.cpp b/tools/noise.cpp index fca20524..bd8cec62 100644 --- a/tools/noise.cpp +++ b/tools/noise.cpp @@ -270,20 +270,33 @@ adjust_ocean (util::image::buffer &height, static const unsigned THERMAL_ITERATIONS = 10; static const unsigned HYDRAULIC_ITERATIONS = 100; +#include "cmdopt.hpp" + int -main (void) +main (int argc, char **argv) { - // setup the output buffer + // setup default variables #ifdef ENABLE_DEBUGGING - util::extent2u size {320, 240}; + util::extent2u res {320, 240}; #else - util::extent2u size {1920, 1080}; + util::extent2u res {1920, 1080}; #endif - util::image::buffer img (size); uint64_t seed = time (nullptr); + // fill variables from arguments + util::cmdopt::parser args; + args.add> ('w', "width", "output image width", res.w); + args.add> ('h', "height", "output image height", res.h); + args.add> ('s', "seed", "random seed", seed); + //args.add> ('o', "octaves", "total fractal iterations", octaves); + //args.add> ('H', "hurst", "Hurst exponent", H); + + args.scan (argc, argv); + + util::image::buffer img (res); + // setup the noise generator #if 0 //util::noise::fractal::fbm> b (seed); @@ -294,7 +307,7 @@ main (void) util::noise::fractal::hetero> b (seed); b.octaves (8); - b.frequency (10.f / size.w); + b.frequency (10.f / res.w); b.lacunarity = 2.f; b.H = 1.0f; b.seed (seed); @@ -306,21 +319,21 @@ main (void) util::noise::fractal::fbm> > b (seed, { 0.13f, 0.13f }); - b.data.frequency (1.f / size.w); + b.data.frequency (1.f / res.w); b.perturb[0].octaves (4); b.perturb[1].octaves (4); - b.perturb[0].frequency (10.f / size.w); - b.perturb[1].frequency (10.f / size.w); + b.perturb[0].frequency (10.f / res.w); + b.perturb[1].frequency (10.f / res.w); #endif // generate the values. offset positions slightly to observe simple axis issues with perlin basis { auto offset = util::vector2f { -100 }; - for (size_t y = 0; y < size.h; ++y) - for (size_t x = 0; x < size.w; ++x) { + for (size_t y = 0; y < res.h; ++y) + for (size_t x = 0; x < res.w; ++x) { auto v = b (util::point2f {float (x), float (y)} + offset); - img.data ()[y * size.w + x] = v; + img.data ()[y * res.w + x] = v; } } @@ -342,7 +355,7 @@ main (void) std::cerr << "thermal_erosion\n"; for (size_t i = 0; i < THERMAL_ITERATIONS; ++i) - thermal_erode (soft, to_radians (30.f), 1.f / size.w, 0.f); + thermal_erode (soft, to_radians (30.f), 1.f / res.w, 0.f); hydraulic_erode (soft, HYDRAULIC_ITERATIONS);