diff --git a/cmake/f3dOptions.cmake b/cmake/f3dOptions.cmake index e7c5e5924c..c5ce696e05 100644 --- a/cmake/f3dOptions.cmake +++ b/cmake/f3dOptions.cmake @@ -129,6 +129,11 @@ function(_parse_json_option _top_json) elseif(_option_type STREQUAL "ratio") set(_option_actual_type "f3d::ratio_t") set(_option_variant_type "double") + elseif(_option_type STREQUAL "vector") + set(_option_actual_type "f3d::vector3_t") + set(_option_variant_type "std::vector") + set(_option_default_value_start "{") + set(_option_default_value_end "}") endif() # Add option to struct and methods diff --git a/library/options.json b/library/options.json index 4f843ddce8..e96d007006 100644 --- a/library/options.json +++ b/library/options.json @@ -4,6 +4,10 @@ "type": "string", "default_value": "+Y" }, + "up_direction2": { + "type": "vector", + "default_value": "0, 1, 0" + }, "animation": { "autoplay": { "type": "bool", diff --git a/library/private/options_tools.h.in b/library/private/options_tools.h.in index 221dc674b9..c06ffd5b67 100644 --- a/library/private/options_tools.h.in +++ b/library/private/options_tools.h.in @@ -5,7 +5,9 @@ #include "types.h" #include +#include #include +#include namespace f3d { @@ -181,6 +183,78 @@ ratio_t parse(const std::string& str) } } +//---------------------------------------------------------------------------- +/** + * Parse provided string into a vector3_t. + */ +template<> +vector3_t parse(const std::string& str) +{ + auto parseCommaSeparated = [](const std::string& s) -> vector3_t + { + std::vector vec = parse>(s); + if (vec.size() == 2) + { + return f3d::vector3_t::fromSphericalCoordinates(vec[0], vec[1]); + } + if (vec.size() == 3) + { + return { vec[0], vec[1], vec[2] }; + } + else + { + throw options::parsing_exception("cannot parse " + s + " to a vector3_t"); + } + }; + auto parseYSyntax = [](const std::string& s) -> vector3_t + { + auto ss = trim(s); + if (ss.size() != 2) + { + throw options::parsing_exception("cannot parse " + s + " to a vector3_t"); + } + int sign = +1; + if (ss[0] == '-') + { + sign = -1; + } + else if (ss[0] == '+') + { + sign = +1; + } + else + { + throw options::parsing_exception("cannot parse " + s + " to a vector3_t"); + } + if (ss[1] == 'X') + { + return { double(sign) * 1, 0, 0 }; + } + else if (ss[1] == 'Y') + { + return { 0, double(sign) * 1, 0 }; + } + else if (ss[1] == 'Z') + { + return { 0, 0, double(sign) * 1 }; + } + else + { + throw options::parsing_exception("cannot parse " + s + " to a vector3_t"); + } + }; + bool isNotCommaSeparated = std::find_if(str.begin(), str.end(), + [](unsigned char c) { return std::isalpha(c); }) != str.end(); + if (!isNotCommaSeparated) + { + return parseCommaSeparated(str); + } + else + { + return parseYSyntax(str); + } +} + //---------------------------------------------------------------------------- /** * Return provided string stripped of leading and trailing spaces. @@ -270,6 +344,17 @@ std::string format(const std::vector& var) return stream.str(); } +//---------------------------------------------------------------------------- +/** + * Format provided var into a string from provided vector3_t + * rely on format(double&) + */ +template<> +std::string format(const vector3_t& var) +{ + return format(static_cast>(var)); +} + //---------------------------------------------------------------------------- /** * Generated method, see `options::set` diff --git a/library/public/types.h b/library/public/types.h index 238a005184..60912d4221 100644 --- a/library/public/types.h +++ b/library/public/types.h @@ -1,33 +1,37 @@ #ifndef f3d_types_h #define f3d_types_h +#include "exception.h" #include "export.h" #include +#include +#include +#include #include #include namespace f3d { + /** - * Describe a 3D point. + * An exception that can be thrown when we fail to create a type */ -struct F3D_EXPORT point3_t : std::array +struct type_creation_exception : public exception { - template - point3_t(Args&&... args) - : array({ double(std::forward(args))... }) + explicit type_creation_exception(const std::string& what = "") + : exception(what) { } }; /** - * Describe a 3D vector. + * Describe a 3D point. */ -struct F3D_EXPORT vector3_t : std::array +struct F3D_EXPORT point3_t : std::array { template - vector3_t(Args&&... args) + point3_t(Args&&... args) : array({ double(std::forward(args))... }) { } @@ -82,6 +86,139 @@ struct mesh_t */ F3D_EXPORT std::pair isValid() const; }; + +/** + * Describe a 3D vector. + */ +struct F3D_EXPORT vector3_t +{ + vector3_t() = default; + vector3_t(double x, double y, double z) + : Value{ x, y, z } + { + } + vector3_t(const std::vector& vec) + { + if (vec.size() != 3) + { + throw type_creation_exception("cannot create a vector3_t"); + } + Value[0] = vec[0]; + Value[1] = vec[1]; + Value[2] = vec[2]; + } + vector3_t(const std::array& arr) + { + Value[0] = arr[0]; + Value[1] = arr[1]; + Value[2] = arr[2]; + } + vector3_t(const double* ptr) + { + Value[0] = ptr[0]; + Value[1] = ptr[1]; + Value[2] = ptr[2]; + } + vector3_t(std::initializer_list l) + { + if (l.size() != 3) + { + throw type_creation_exception("cannot create a vector3_t"); + } + std::copy(l.begin(), l.end(), std::begin(Value)); + } + + double* data() + { + return Value.data(); + } + const double* data() const + { + return Value.data(); + } + + double& operator[](int idx) + { + return Value[idx]; + } + double operator[](int idx) const + { + return Value[idx]; + } + operator std::vector() const + { + return { Value[0], Value[1], Value[2] }; + } + operator std::array() const + { + return { Value[0], Value[1], Value[2] }; + } + bool operator==(const vector3_t& vec) const + { + return Value[0] == vec.Value[0] && Value[1] == vec.Value[1] && Value[2] == vec.Value[2]; + } + bool operator!=(const vector3_t& vec) const + { + return !(*this == vec); + } + + auto begin() + { + return Value.begin(); + } + // auto begin() const + // { + // return Value.begin(); + // } + auto cbegin() const + { + return Value.cbegin(); + } + auto end() + { + return Value.end(); + } + // auto end() const + // { + // return Value.end(); + // } + auto cend() const + { + return Value.cend(); + } + + static vector3_t fromSphericalCoordinates(double theta, double phi) + { + auto sinPhi = std::sin(phi); + return { sinPhi * std::cos(theta), sinPhi * std::sin(theta), std::cos(phi) }; + } + static vector3_t x() + { + return { 1, 0, 0 }; + } + static vector3_t y() + { + return { 0, 1, 0 }; + } + static vector3_t z() + { + return { 0, 0, 1 }; + } + static vector3_t zero() + { + return { 0, 0, 0 }; + } + +private: + std::array Value; +}; + +inline std::ostream& operator<<(std::ostream& os, const f3d::vector3_t& vec) +{ + os << "{ " << vec[0] << ", " << vec[1] << ", " << vec[2] << " }"; + return os; +} + } #endif diff --git a/library/src/options.cxx b/library/src/options.cxx index 23174d6799..88224bc1cf 100644 --- a/library/src/options.cxx +++ b/library/src/options.cxx @@ -4,6 +4,7 @@ #include "export.h" #include "init.h" #include "log.h" +#include "types.h" #include "utils.h" #include "vtkF3DConfigure.h" @@ -178,6 +179,7 @@ F3D_DECL_TYPE(bool); F3D_DECL_TYPE(int); F3D_DECL_TYPE(double); F3D_DECL_TYPE(f3d::ratio_t); +F3D_DECL_TYPE(f3d::vector3_t); F3D_DECL_TYPE(std::string); //---------------------------------------------------------------------------- diff --git a/library/testing/PseudoUnitTest.h b/library/testing/PseudoUnitTest.h index 55ebbd7e79..31fe855f90 100644 --- a/library/testing/PseudoUnitTest.h +++ b/library/testing/PseudoUnitTest.h @@ -1,6 +1,7 @@ #ifndef PseudoUnitTest_h #define PseudoUnitTest_h +#include "types.h" #include #include #include diff --git a/library/testing/TestSDKOptionsIO.cxx b/library/testing/TestSDKOptionsIO.cxx index 333fb22321..ac146d2ef3 100644 --- a/library/testing/TestSDKOptionsIO.cxx +++ b/library/testing/TestSDKOptionsIO.cxx @@ -2,6 +2,7 @@ #include #include "PseudoUnitTest.h" +#include "types.h" #include @@ -71,5 +72,19 @@ int TestSDKOptionsIO(int argc, char* argv[]) test.parse>( "std::vector", " foo, bar , baz ", { "foo", "bar", "baz" }); + test.parse("vector3_t", "1, 2, 3", { 1, 2, 3 }); + test.parse("vector3_t", " 1, 2, 3 ", { 1, 2, 3 }); + test.parse("vector3_t", "+Y", { 0, 1, 0 }); + test.parse("vector3_t", " +Y ", { 0, 1, 0 }); + test.parse("vector3_t", "-Y", { 0, -1, 0 }); + test.parse("vector3_t", "+X", { 1, 0, 0 }); + test.parse("vector3_t", "-X", { -1, 0, 0 }); + test.parse("vector3_t", "+Z", { 0, 0, 1 }); + test.parse("vector3_t", "-Z", { 0, 0, -1 }); + test.parse("vector3_t", "0, 0", { 0, 0, 1 }); + test.expect("cannot create a vector3_t", [&]() { + f3d::vector3_t{ 1., 2., 3., 4. }; + }); + return test.result(); } diff --git a/python/F3DPythonBindings.cxx b/python/F3DPythonBindings.cxx index 3c915d3b25..27363df9d4 100644 --- a/python/F3DPythonBindings.cxx +++ b/python/F3DPythonBindings.cxx @@ -23,7 +23,7 @@ bool load_array(const py::handle& src, bool convert, std::array& value) return false; } const py::sequence l = py::reinterpret_borrow(src); - if (l.size() != S) + if (l.size() != 3) { return false; } @@ -36,6 +36,26 @@ bool load_array(const py::handle& src, bool convert, std::array& value) return true; } +bool load_array(const py::handle& src, bool convert, f3d::vector3_t& value) +{ + if (!py::isinstance(src)) + { + return false; + } + const py::sequence l = py::reinterpret_borrow(src); + if (l.size() != 3) + { + return false; + } + + size_t i = 0; + for (auto it : l) + { + value[i++] = py::cast(it); + } + return true; +} + template<> class py::detail::type_caster {