diff --git a/doc/changelog.dox b/doc/changelog.dox index bc76c4d48..074b70788 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -38,6 +38,14 @@ See also: @section changelog-latest Changes since 2018.10 +@subsection changelog-latest-new New features + +@subsubsection changelog-latest-new-math Math library + +- Support for using the @ref Math::Deg, @ref Math::Rad, @ref Math::Half, + @ref Math::Color3 and @ref Math::Color4 literals with the new experimental + @ref Corrade::Utility::Tweakable utility. See also @ref tweakableliterals. + @subsection changelog-latest-bugfixes Bug fixes - Fixed @ref Platform::Sdl2Application and @ref Platform::GlfwApplication to diff --git a/src/Magnum/CMakeLists.txt b/src/Magnum/CMakeLists.txt index 1b05ac225..54293b36c 100644 --- a/src/Magnum/CMakeLists.txt +++ b/src/Magnum/CMakeLists.txt @@ -132,6 +132,7 @@ endif() # Files shared between main library and math unit test library set(MagnumMath_SRCS + Math/Angle.cpp Math/Color.cpp Math/Half.cpp Math/Functions.cpp diff --git a/src/Magnum/Math/Angle.cpp b/src/Magnum/Math/Angle.cpp new file mode 100644 index 000000000..2b5440e8c --- /dev/null +++ b/src/Magnum/Math/Angle.cpp @@ -0,0 +1,125 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "Angle.h" + +#if defined(DOXYGEN_GENERATING_OUTPUT) || defined(CORRADE_TARGET_UNIX) || (defined(CORRADE_TARGET_WINDOWS) && !defined(CORRADE_TARGET_WINDOWS_RT)) || defined(CORRADE_TARGET_EMSCRIPTEN) +#include +#include +#include +#include + +namespace Corrade { namespace Utility { + +std::pair> TweakableParser>::parse(const Containers::ArrayView value) { + char* end; + const Magnum::Float result = std::strtof(value, &end); + + if(end == value.begin() || std::find(value.begin(), value.end(), '.') == value.end()) { + Warning{} << "Utility::TweakableParser:" << std::string{value, value.size()} << "is not an angle literal"; + return {TweakableState::Recompile, {}}; + } + + if(!String::viewEndsWith(value, "_degf")) { + Warning{} << "Utility::TweakableParser:" << std::string{value, value.size()} << "has an unexpected suffix, expected _degf"; + return {TweakableState::Recompile, {}}; + } + + if(end != value.end() - 5) { + Warning{} << "Utility::TweakableParser: unexpected characters" << std::string{const_cast(end), value.end()} << "after an angle literal"; + return {TweakableState::Recompile, {}}; + } + + return {TweakableState::Success, Magnum::Math::Deg{result}}; +} + +std::pair> TweakableParser>::parse(const Containers::ArrayView value) { + char* end; + const Magnum::Double result = std::strtod(value, &end); + + if(end == value.begin() || std::find(value.begin(), value.end(), '.') == value.end()) { + Warning{} << "Utility::TweakableParser:" << std::string{value, value.size()} << "is not an angle literal"; + return {TweakableState::Recompile, {}}; + } + + if(!String::viewEndsWith(value, "_deg")) { + Warning{} << "Utility::TweakableParser:" << std::string{value, value.size()} << "has an unexpected suffix, expected _deg"; + return {TweakableState::Recompile, {}}; + } + + if(end != value.end() - 4) { + Warning{} << "Utility::TweakableParser: unexpected characters" << std::string{const_cast(end), value.end()} << "after an angle literal"; + return {TweakableState::Recompile, {}}; + } + + return {TweakableState::Success, Magnum::Math::Deg{result}}; +} + +std::pair> TweakableParser>::parse(const Containers::ArrayView value) { + char* end; + const Magnum::Float result = std::strtof(value, &end); + + if(end == value.begin() || std::find(value.begin(), value.end(), '.') == value.end()) { + Warning{} << "Utility::TweakableParser:" << std::string{value, value.size()} << "is not an angle literal"; + return {TweakableState::Recompile, {}}; + } + + if(!String::viewEndsWith(value, "_radf")) { + Warning{} << "Utility::TweakableParser:" << std::string{value, value.size()} << "has an unexpected suffix, expected _radf"; + return {TweakableState::Recompile, {}}; + } + + if(end != value.end() - 5) { + Warning{} << "Utility::TweakableParser: unexpected characters" << std::string{const_cast(end), value.end()} << "after an angle literal"; + return {TweakableState::Recompile, {}}; + } + + return {TweakableState::Success, Magnum::Math::Rad{result}}; +} + +std::pair> TweakableParser>::parse(const Containers::ArrayView value) { + char* end; + const Magnum::Double result = std::strtod(value, &end); + + if(end == value.begin() || std::find(value.begin(), value.end(), '.') == value.end()) { + Warning{} << "Utility::TweakableParser:" << std::string{value, value.size()} << "is not an angle literal"; + return {TweakableState::Recompile, {}}; + } + + if(!String::viewEndsWith(value, "_rad")) { + Warning{} << "Utility::TweakableParser:" << std::string{value, value.size()} << "has an unexpected suffix, expected _rad"; + return {TweakableState::Recompile, {}}; + } + + if(end != value.end() - 4) { + Warning{} << "Utility::TweakableParser: unexpected characters" << std::string{const_cast(end), value.end()} << "after an angle literal"; + return {TweakableState::Recompile, {}}; + } + + return {TweakableState::Success, Magnum::Math::Rad{result}}; +} + +}} +#endif diff --git a/src/Magnum/Math/Angle.h b/src/Magnum/Math/Angle.h index e5bb0cabb..5b5eb7992 100644 --- a/src/Magnum/Math/Angle.h +++ b/src/Magnum/Math/Angle.h @@ -282,6 +282,60 @@ template struct ConfigurationValue> { } }; +#if defined(DOXYGEN_GENERATING_OUTPUT) || defined(CORRADE_TARGET_UNIX) || (defined(CORRADE_TARGET_WINDOWS) && !defined(CORRADE_TARGET_WINDOWS_RT)) || defined(CORRADE_TARGET_EMSCRIPTEN) +/** +@tweakableliteral{Magnum::Math::Deg} + +Parses the @link Magnum::Math::Literals::operator""_degf @endlink literal. +@experimental +*/ +template<> struct MAGNUM_EXPORT TweakableParser> { + TweakableParser() = delete; + + /** @brief Parse the value */ + static std::pair> parse(Containers::ArrayView value); +}; + +/** +@tweakableliteral{Magnum::Math::Deg} + +Parses the @link Magnum::Math::Literals::operator""_deg @endlink literal. +@experimental +*/ +template<> struct MAGNUM_EXPORT TweakableParser> { + TweakableParser() = delete; + + /** @brief Parse the value */ + static std::pair> parse(Containers::ArrayView value); +}; + +/** +@tweakableliteral{Magnum::Math::Rad} + +Parses the @link Magnum::Math::Literals::operator""_radf @endlink literal. +@experimental +*/ +template<> struct MAGNUM_EXPORT TweakableParser> { + TweakableParser() = delete; + + /** @brief Parse the value */ + static std::pair> parse(Containers::ArrayView value); +}; + +/** +@tweakableliteral{Magnum::Math::Rad} + +Parses the @link Magnum::Math::Literals::operator""_rad @endlink literal. +@experimental +*/ +template<> struct MAGNUM_EXPORT TweakableParser> { + TweakableParser() = delete; + + /** @brief Parse the value */ + static std::pair> parse(Containers::ArrayView value); +}; + }} +#endif #endif diff --git a/src/Magnum/Math/Color.cpp b/src/Magnum/Math/Color.cpp index 24913199f..b4ecbe36e 100644 --- a/src/Magnum/Math/Color.cpp +++ b/src/Magnum/Math/Color.cpp @@ -25,6 +25,12 @@ #include "Color.h" +#if defined(DOXYGEN_GENERATING_OUTPUT) || defined(CORRADE_TARGET_UNIX) || (defined(CORRADE_TARGET_WINDOWS) && !defined(CORRADE_TARGET_WINDOWS_RT)) || defined(CORRADE_TARGET_EMSCRIPTEN) +#include +#include +#include +#endif + namespace Magnum { namespace Math { namespace { @@ -56,3 +62,129 @@ Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug& debug, const Color4 } }} + +#if defined(DOXYGEN_GENERATING_OUTPUT) || defined(CORRADE_TARGET_UNIX) || (defined(CORRADE_TARGET_WINDOWS) && !defined(CORRADE_TARGET_WINDOWS_RT)) || defined(CORRADE_TARGET_EMSCRIPTEN) +namespace Corrade { namespace Utility { + +std::pair> TweakableParser>::parse(const Containers::ArrayView value) { + if(value.size() < 2 || value[0] != '0' || (value[1] != 'x' && value[1] != 'X')) { + Warning{} << "Utility::TweakableParser:" << std::string{value, value.size()} << "is not a hexadecimal color literal"; + return {TweakableState::Recompile, {}}; + } + + const bool isSrgb = String::viewEndsWith(value, "_srgb"); + if(!isSrgb && !String::viewEndsWith(value, "_rgb")) { + Warning{} << "Utility::TweakableParser:" << std::string{value, value.size()} << "has an unexpected suffix, expected _rgb or _srgb"; + return {TweakableState::Recompile, {}}; + } + + char* end; + const Magnum::UnsignedInt result = std::strtoul(value, &end, 16); + + if(end != value.end() - (isSrgb ? 5 : 4)) { + Warning{} << "Utility::TweakableParser: unexpected characters" << std::string{const_cast(end), value.end()} << "after a color literal"; + return {TweakableState::Recompile, {}}; + } + + if(value.size() != (isSrgb ? 13 : 12)) { + Error{} << "Utility::TweakableParser:" << std::string{value, value.size()} << "doesn't have expected number of digits"; + return {TweakableState::Error, {}}; + } + + return {TweakableState::Success, + isSrgb ? Magnum::Math::Literals::operator "" _srgb(result) : + Magnum::Math::Literals::operator "" _rgb(result)}; +} + +std::pair> TweakableParser>::parse(const Containers::ArrayView value) { + if(value.size() < 2 || value[0] != '0' || (value[1] != 'x' && value[1] != 'X')) { + Warning{} << "Utility::TweakableParser:" << std::string{value, value.size()} << "is not a hexadecimal color literal"; + return {TweakableState::Recompile, {}}; + } + + const bool isSrgb = String::viewEndsWith(value, "_srgba"); + if(!isSrgb && !String::viewEndsWith(value, "_rgba")) { + Warning{} << "Utility::TweakableParser:" << std::string{value, value.size()} << "has an unexpected suffix, expected _rgba or _srgba"; + return {TweakableState::Recompile, {}}; + } + + char* end; + const Magnum::UnsignedInt result = std::strtoul(value, &end, 16); + + if(end != value.end() - (isSrgb ? 6 : 5)) { + Warning{} << "Utility::TweakableParser: unexpected characters" << std::string{const_cast(end), value.end()} << "after a color literal"; + return {TweakableState::Recompile, {}}; + } + + if(value.size() != (isSrgb ? 16 : 15)) { + Error{} << "Utility::TweakableParser:" << std::string{value, value.size()} << "doesn't have expected number of digits"; + return {TweakableState::Error, {}}; + } + + return {TweakableState::Success, + isSrgb ? Magnum::Math::Literals::operator "" _srgba(result) : + Magnum::Math::Literals::operator "" _rgba(result)}; +} + +std::pair> TweakableParser>::parse(const Containers::ArrayView value) { + if(value.size() < 2 || value[0] != '0' || (value[1] != 'x' && value[1] != 'X')) { + Warning{} << "Utility::TweakableParser:" << std::string{value, value.size()} << "is not a hexadecimal color literal"; + return {TweakableState::Recompile, {}}; + } + + const bool isSrgb = String::viewEndsWith(value, "_srgbf"); + if(!isSrgb && !String::viewEndsWith(value, "_rgbf")) { + Warning{} << "Utility::TweakableParser:" << std::string{value, value.size()} << "has an unexpected suffix, expected _rgbf or _srgbf"; + return {TweakableState::Recompile, {}}; + } + + char* end; + const Magnum::UnsignedInt result = std::strtoul(value, &end, 16); + + if(end != value.end() - (isSrgb ? 6 : 5)) { + Warning{} << "Utility::TweakableParser: unexpected characters" << std::string{const_cast(end), value.end()} << "after a color literal"; + return {TweakableState::Recompile, {}}; + } + + if(value.size() != (isSrgb ? 14 : 13)) { + Error{} << "Utility::TweakableParser:" << std::string{value, value.size()} << "doesn't have expected number of digits"; + return {TweakableState::Error, {}}; + } + + return {TweakableState::Success, + isSrgb ? Magnum::Math::Literals::operator "" _srgbf(result) : + Magnum::Math::Literals::operator "" _rgbf(result)}; +} + +std::pair> TweakableParser>::parse(const Containers::ArrayView value) { + if(value.size() < 2 || value[0] != '0' || (value[1] != 'x' && value[1] != 'X')) { + Warning{} << "Utility::TweakableParser:" << std::string{value, value.size()} << "is not a hexadecimal color literal"; + return {TweakableState::Recompile, {}}; + } + + const bool isSrgb = String::viewEndsWith(value, "_srgbaf"); + if(!isSrgb && !String::viewEndsWith(value, "_rgbaf")) { + Warning{} << "Utility::TweakableParser:" << std::string{value, value.size()} << "has an unexpected suffix, expected _rgbaf or _srgbaf"; + return {TweakableState::Recompile, {}}; + } + + char* end; + const Magnum::UnsignedInt result = std::strtoul(value, &end, 16); + + if(end != value.end() - (isSrgb ? 7 : 6)) { + Warning{} << "Utility::TweakableParser: unexpected characters" << std::string{const_cast(end), value.end()} << "after a color literal"; + return {TweakableState::Recompile, {}}; + } + + if(value.size() != (isSrgb ? 17 : 16)) { + Error{} << "Utility::TweakableParser:" << std::string{value, value.size()} << "doesn't have expected number of digits"; + return {TweakableState::Error, {}}; + } + + return {TweakableState::Success, + isSrgb ? Magnum::Math::Literals::operator "" _srgbaf(result) : + Magnum::Math::Literals::operator "" _rgbaf(result)}; +} + +}} +#endif diff --git a/src/Magnum/Math/Color.h b/src/Magnum/Math/Color.h index 71223ff8a..b3605c7ea 100644 --- a/src/Magnum/Math/Color.h +++ b/src/Magnum/Math/Color.h @@ -1277,11 +1277,77 @@ namespace Implementation { }} namespace Corrade { namespace Utility { - /** @configurationvalue{Magnum::Color3} */ - template struct ConfigurationValue>: public ConfigurationValue> {}; - /** @configurationvalue{Magnum::Color4} */ - template struct ConfigurationValue>: public ConfigurationValue> {}; +/** @configurationvalue{Magnum::Color3} */ +template struct ConfigurationValue>: ConfigurationValue> {}; + +/** @configurationvalue{Magnum::Color4} */ +template struct ConfigurationValue>: ConfigurationValue> {}; + +/** +@tweakableliteral{Magnum::Math::Color3} + +Parses the @link Magnum::Math::Literals::operator""_rgb @endlink and +@link Magnum::Math::Literals::operator""_srgb @endlink literals. +@experimental +*/ +template<> struct MAGNUM_EXPORT TweakableParser> { + TweakableParser() = delete; + + /** @brief Parse the value */ + static std::pair> parse(Containers::ArrayView value); +}; + +#ifndef DOXYGEN_GENERATING_OUTPUT +template<> struct MAGNUM_EXPORT TweakableParser>: TweakableParser> {}; +#endif + +/** +@tweakableliteral{Magnum::Math::Color4} + +Parses the @link Magnum::Math::Literals::operator""_rgba @endlink and +@link Magnum::Math::Literals::operator""_srgba @endlink literals. +@experimental +*/ +template<> struct MAGNUM_EXPORT TweakableParser> { + TweakableParser() = delete; + + /** @brief Parse the value */ + static std::pair> parse(Containers::ArrayView value); +}; + +#ifndef DOXYGEN_GENERATING_OUTPUT +template<> struct MAGNUM_EXPORT TweakableParser>: TweakableParser> {}; +#endif + +/** +@tweakableliteral{Magnum::Math::Color3} + +Parses the @link Magnum::Math::Literals::operator""_rgbf @endlink and +@link Magnum::Math::Literals::operator""_srgbf @endlink literals. +@experimental +*/ +template<> struct MAGNUM_EXPORT TweakableParser> { + TweakableParser() = delete; + + /** @brief Parse the value */ + static std::pair> parse(Containers::ArrayView value); +}; + +/** +@tweakableliteral{Magnum::Math::Color4} + +Parses the @link Magnum::Math::Literals::operator""_rgbaf @endlink and +@link Magnum::Math::Literals::operator""_srgbaf @endlink literals. +@experimental +*/ +template<> struct MAGNUM_EXPORT TweakableParser> { + TweakableParser() = delete; + + /** @brief Parse the value */ + static std::pair> parse(Containers::ArrayView value); +}; + }} #endif diff --git a/src/Magnum/Math/Half.cpp b/src/Magnum/Math/Half.cpp index 1d62fb0ed..3eed8423a 100644 --- a/src/Magnum/Math/Half.cpp +++ b/src/Magnum/Math/Half.cpp @@ -28,6 +28,12 @@ #include #include +#if defined(DOXYGEN_GENERATING_OUTPUT) || defined(CORRADE_TARGET_UNIX) || (defined(CORRADE_TARGET_WINDOWS) && !defined(CORRADE_TARGET_WINDOWS_RT)) || defined(CORRADE_TARGET_EMSCRIPTEN) +#include +#include +#include +#endif + namespace Magnum { namespace Math { Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug& debug, Half value) { @@ -39,3 +45,31 @@ Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug& debug, Half value) } }} + +#if defined(DOXYGEN_GENERATING_OUTPUT) || defined(CORRADE_TARGET_UNIX) || (defined(CORRADE_TARGET_WINDOWS) && !defined(CORRADE_TARGET_WINDOWS_RT)) || defined(CORRADE_TARGET_EMSCRIPTEN) +namespace Corrade { namespace Utility { + +std::pair TweakableParser::parse(const Containers::ArrayView value) { + char* end; + const Magnum::Float result = std::strtof(value, &end); + + if(end == value.begin() || std::find(value.begin(), value.end(), '.') == value.end()) { + Warning{} << "Utility::TweakableParser:" << std::string{value, value.size()} << "is not a half literal"; + return {TweakableState::Recompile, {}}; + } + + if(!String::viewEndsWith(value, "_h")) { + Warning{} << "Utility::TweakableParser:" << std::string{value, value.size()} << "has an unexpected suffix, expected _h"; + return {TweakableState::Recompile, {}}; + } + + if(end != value.end() - 2) { + Warning{} << "Utility::TweakableParser: unexpected characters" << std::string{const_cast(end), value.end()} << "after a half literal"; + return {TweakableState::Recompile, {}}; + } + + return {TweakableState::Success, Magnum::Math::Half{result}}; +} + +}} +#endif diff --git a/src/Magnum/Math/Half.h b/src/Magnum/Math/Half.h index 8f64edf2e..617ce32a9 100644 --- a/src/Magnum/Math/Half.h +++ b/src/Magnum/Math/Half.h @@ -163,4 +163,20 @@ MAGNUM_EXPORT Corrade::Utility::Debug& operator<<(Corrade::Utility::Debug& debug }} +namespace Corrade { namespace Utility { + +/** +@tweakableliteral{Magnum::Math::Half} + +Parses the @link Magnum::Math::Literals::operator""_h @endlink literal. +*/ +template<> struct MAGNUM_EXPORT TweakableParser { + TweakableParser() = delete; + + /** @brief Parse the value */ + static std::pair parse(Containers::ArrayView value); +}; + +}} + #endif diff --git a/src/Magnum/Math/Test/AngleTest.cpp b/src/Magnum/Math/Test/AngleTest.cpp index c6751dc43..83e2d4194 100644 --- a/src/Magnum/Math/Test/AngleTest.cpp +++ b/src/Magnum/Math/Test/AngleTest.cpp @@ -23,9 +23,13 @@ DEALINGS IN THE SOFTWARE. */ +#include #include +#include #include #include +#include +#include #include "Magnum/Math/Angle.h" @@ -47,6 +51,8 @@ struct AngleTest: Corrade::TestSuite::Tester { void debugRad(); void configurationDeg(); void configurationRad(); + template void tweakable(); + template void tweakableError(); }; typedef Math::Deg Deg; @@ -54,6 +60,58 @@ typedef Math::Rad Rad; typedef Math::Deg Degd; typedef Math::Rad Radd; +using namespace Literals; + +namespace { + +constexpr struct { + const char* name; + const char* data; + float result; +} TweakableData[] { + {"fixed", "35.0_{}", 35.0f}, + {"no zero before", ".5_{}", 0.5f}, + {"no zero after", "35._{}", 35.0f}, + {"positive", "+35.0_{}", 35.0f}, + {"negative", "-35.0_{}", -35.0} +}; + +constexpr struct { + const char* name; + const char* data; + Corrade::Utility::TweakableState state; + const char* error; +} TweakableErrorData[] { + {"empty", "", Corrade::Utility::TweakableState::Recompile, + "Utility::TweakableParser: is not an angle literal\n"}, + {"integral", "42_{}", Corrade::Utility::TweakableState::Recompile, + "Utility::TweakableParser: 42_{} is not an angle literal\n"}, + {"garbage after", "42.b_{}", Corrade::Utility::TweakableState::Recompile, + "Utility::TweakableParser: unexpected characters b_{} after an angle literal\n"}, + {"different suffix", "42.0u", Corrade::Utility::TweakableState::Recompile, /* not for double */ + "Utility::TweakableParser: 42.0u has an unexpected suffix, expected _{}\n"} +}; + +template struct TweakableTraits; +template<> struct TweakableTraits { + static const char* name() { return "Deg"; } + static const char* literal() { return "degf"; } +}; +template<> struct TweakableTraits { + static const char* name() { return "Degd"; } + static const char* literal() { return "deg"; } +}; +template<> struct TweakableTraits { + static const char* name() { return "Rad"; } + static const char* literal() { return "radf"; } +}; +template<> struct TweakableTraits { + static const char* name() { return "Radd"; } + static const char* literal() { return "rad"; } +}; + +} + AngleTest::AngleTest() { addTests({&AngleTest::construct, &AngleTest::constructDefault, @@ -68,6 +126,20 @@ AngleTest::AngleTest() { &AngleTest::debugRad, &AngleTest::configurationDeg, &AngleTest::configurationRad}); + + addInstancedTests({ + &AngleTest::tweakable, + &AngleTest::tweakable, + &AngleTest::tweakable, + &AngleTest::tweakable}, + Corrade::Containers::arraySize(TweakableData)); + + addInstancedTests({ + &AngleTest::tweakableError, + &AngleTest::tweakableError, + &AngleTest::tweakableError, + &AngleTest::tweakableError}, + Corrade::Containers::arraySize(TweakableErrorData)); } void AngleTest::construct() { @@ -162,8 +234,6 @@ void AngleTest::constructCopy() { } void AngleTest::literals() { - using namespace Literals; - constexpr auto a = 25.0_deg; CORRADE_VERIFY((std::is_same::value)); CORRADE_COMPARE(Double(a), 25.0); @@ -234,6 +304,32 @@ void AngleTest::configurationRad() { CORRADE_COMPARE(c.value("angle"), angle); } +template void AngleTest::tweakable() { + auto&& data = TweakableData[testCaseInstanceId()]; + setTestCaseName(Corrade::Utility::formatString("tweakable<{}>", TweakableTraits::name())); + setTestCaseDescription(data.name); + std::string input = Corrade::Utility::formatString(data.data, TweakableTraits::literal()); + Corrade::Utility::TweakableState state; + T result; + std::tie(state, result) = Corrade::Utility::TweakableParser::parse({input.data(), input.size()}); + CORRADE_COMPARE(state, Corrade::Utility::TweakableState::Success); + CORRADE_COMPARE(result, T(typename T::Type(data.result))); +} + +template void AngleTest::tweakableError() { + auto&& data = TweakableErrorData[testCaseInstanceId()]; + setTestCaseName(Corrade::Utility::formatString("tweakableError<{}>", TweakableTraits::name())); + setTestCaseDescription(data.name); + std::string input = Corrade::Utility::formatString(data.data, TweakableTraits::literal()); + + std::ostringstream out; + Warning redirectWarning{&out}; + Error redirectError{&out}; + Corrade::Utility::TweakableState state = Corrade::Utility::TweakableParser::parse({input.data(), input.size()}).first; + CORRADE_COMPARE(out.str(), Corrade::Utility::formatString(data.error, TweakableTraits::literal())); + CORRADE_COMPARE(state, data.state); +} + }}} CORRADE_TEST_MAIN(Magnum::Math::Test::AngleTest) diff --git a/src/Magnum/Math/Test/ColorTest.cpp b/src/Magnum/Math/Test/ColorTest.cpp index d3f6ee5e4..d294cacd9 100644 --- a/src/Magnum/Math/Test/ColorTest.cpp +++ b/src/Magnum/Math/Test/ColorTest.cpp @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include "Magnum/Math/Color.h" @@ -105,6 +107,24 @@ struct ColorTest: Corrade::TestSuite::Tester { void debug(); void debugUb(); void configuration(); + + void tweakableRgb(); + void tweakableSrgb(); + void tweakableRgbf(); + void tweakableSrgbf(); + void tweakableRgba(); + void tweakableSrgba(); + void tweakableRgbaf(); + void tweakableSrgbaf(); + + void tweakableErrorRgb(); + void tweakableErrorSrgb(); + void tweakableErrorRgba(); + void tweakableErrorSrgba(); + void tweakableErrorRgbf(); + void tweakableErrorSrgbf(); + void tweakableErrorRgbaf(); + void tweakableErrorSrgbaf(); }; typedef Math::Vector3 Vector3; @@ -117,6 +137,44 @@ typedef Math::Color4 Color4ub; typedef Math::Deg Deg; +using namespace Literals; + +namespace { + +const struct { + const char* name; + const char* dataRgb; + const char* dataRgba; + Color4 result; + Color4 resultSrgba; + Color4ub resultUb; +} TweakableData[] { + {"lowercase", "0xff3366_{}", "0xff3366aa_{}", 0xff3366aa_rgbaf, 0xff3366aa_srgbaf, 0xff3366aa_rgba}, + {"uppercase", "0XFF3366_{}", "0XFF3366AA_{}", 0xff3366aa_rgbaf, 0xff3366aa_srgbaf, 0xff3366aa_rgba} +}; + +constexpr struct { + const char* name; + const char* data; + Corrade::Utility::TweakableState state; + const char* error; +} TweakableErrorData[] { + {"empty", "", Corrade::Utility::TweakableState::Recompile, + "Utility::TweakableParser: is not a hexadecimal color literal\n"}, + {"char", "'a'", Corrade::Utility::TweakableState::Recompile, + "Utility::TweakableParser: 'a' is not a hexadecimal color literal\n"}, + {"not hex", "{}_{}", Corrade::Utility::TweakableState::Recompile, + "Utility::TweakableParser: {0}_{2}{1} is not a hexadecimal color literal\n"}, + {"garbage after", "0x{}._{}", Corrade::Utility::TweakableState::Recompile, + "Utility::TweakableParser: unexpected characters ._{2}{1} after a color literal\n"}, + {"different suffix", "0x{}f", Corrade::Utility::TweakableState::Recompile, + "Utility::TweakableParser: 0x{0}f has an unexpected suffix, expected _{1} or _s{1}\n"}, + {"bad size", "0x333_{1}", Corrade::Utility::TweakableState::Error, + "Utility::TweakableParser: 0x333_{2}{1} doesn't have expected number of digits\n"}, +}; + +} + ColorTest::ColorTest() { addTests({&ColorTest::construct, &ColorTest::constructDefault, @@ -157,6 +215,26 @@ ColorTest::ColorTest() { &ColorTest::debug, &ColorTest::debugUb, &ColorTest::configuration}); + + addInstancedTests({&ColorTest::tweakableRgb, + &ColorTest::tweakableSrgb, + &ColorTest::tweakableRgba, + &ColorTest::tweakableSrgba, + &ColorTest::tweakableRgbf, + &ColorTest::tweakableSrgbf, + &ColorTest::tweakableRgbaf, + &ColorTest::tweakableSrgbaf}, + Corrade::Containers::arraySize(TweakableData)); + + addInstancedTests({&ColorTest::tweakableErrorRgb, + &ColorTest::tweakableErrorSrgb, + &ColorTest::tweakableErrorRgba, + &ColorTest::tweakableErrorSrgba, + &ColorTest::tweakableErrorRgbf, + &ColorTest::tweakableErrorSrgbf, + &ColorTest::tweakableErrorRgbaf, + &ColorTest::tweakableErrorSrgbaf}, + Corrade::Containers::arraySize(TweakableErrorData)); } void ColorTest::construct() { @@ -365,8 +443,6 @@ void ColorTest::data() { } void ColorTest::literals() { - using namespace Literals; - constexpr Color3ub a = 0x33b27f_rgb; CORRADE_COMPARE(a, (Color3ub{0x33, 0xb2, 0x7f})); @@ -423,8 +499,6 @@ void ColorTest::colors() { } void ColorTest::hue() { - using namespace Literals; - CORRADE_COMPARE(Color3::fromHsv( 27.0_degf, 1.0f, 1.0f), (Color3{1.0f, 0.45f, 0.0f})); CORRADE_COMPARE(Color3::fromHsv( 86.0_degf, 1.0f, 1.0f), (Color3{0.566667f, 1.0f, 0.0f})); CORRADE_COMPARE(Color3::fromHsv(134.0_degf, 1.0f, 1.0f), (Color3{0.0f, 1.0f, 0.233333f})); @@ -453,8 +527,6 @@ void ColorTest::hue() { } void ColorTest::saturation() { - using namespace Literals; - CORRADE_COMPARE(Color3::fromHsv(0.0_degf, 0.702f, 1.0f), (Color3{1.0f, 0.298f, 0.298f})); CORRADE_COMPARE((Color3{1.0f, 0.298f, 0.298f}).saturation(), 0.702f); @@ -476,8 +548,6 @@ void ColorTest::saturation() { } void ColorTest::value() { - using namespace Literals; - CORRADE_COMPARE(Color3::fromHsv(0.0_degf, 1.0f, 0.522f), (Color3{0.522f, 0.0f, 0.0f})); CORRADE_COMPARE((Color3{0.522f, 0.0f, 0.0f}).value(), 0.522f); @@ -499,8 +569,6 @@ void ColorTest::value() { } void ColorTest::hsv() { - using namespace Literals; - CORRADE_COMPARE(Color3::fromHsv(std::make_tuple(230.0_degf, 0.749f, 0.427f)), (Color3{0.107177f, 0.160481f, 0.427f})); CORRADE_COMPARE(Color3::fromHsv(230.0_degf, 0.749f, 0.427f), @@ -552,8 +620,6 @@ void ColorTest::hsv() { } void ColorTest::fromHsvHueOverflow() { - using namespace Literals; - CORRADE_COMPARE(Color3::fromHsv( 27.0_degf - 360.0_degf, 1.0f, 1.0f), (Color3{1.0f, 0.45f, 0.0f})); CORRADE_COMPARE(Color3::fromHsv( 86.0_degf - 360.0_degf, 1.0f, 1.0f), (Color3{0.566667f, 1.0f, 0.0f})); CORRADE_COMPARE(Color3::fromHsv(134.0_degf - 360.0_degf, 1.0f, 1.0f), (Color3{0.0f, 1.0f, 0.233333f})); @@ -570,8 +636,6 @@ void ColorTest::fromHsvHueOverflow() { } void ColorTest::fromHsvDefaultAlpha() { - using namespace Literals; - CORRADE_COMPARE(Color4::fromHsv(std::make_tuple(230.0_degf, 0.749f, 0.427f)), (Color4{0.107177f, 0.160481f, 0.427f, 1.0f})); CORRADE_COMPARE(Color4::fromHsv(230.0_degf, 0.749f, 0.427f), @@ -697,8 +761,6 @@ void ColorTest::srgbMonotonic() { } void ColorTest::srgbLiterals() { - using namespace Literals; - constexpr Math::Vector3 a = 0x33b27f_srgb; CORRADE_COMPARE(a, (Math::Vector3{0x33, 0xb2, 0x7f})); @@ -808,8 +870,6 @@ void ColorTest::debug() { } void ColorTest::debugUb() { - using namespace Magnum::Math::Literals; - std::ostringstream o; Debug(&o) << 0x123456_rgb << 0x789abc_rgb; CORRADE_COMPARE(o.str(), "#123456 #789abc\n"); @@ -837,6 +897,198 @@ void ColorTest::configuration() { CORRADE_COMPARE(c.value("color4"), color4); } +void ColorTest::tweakableRgb() { + auto&& data = TweakableData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + std::string input = Corrade::Utility::formatString(data.dataRgb, "rgb"); + Corrade::Utility::TweakableState state; + Color3ub result; + std::tie(state, result) = Corrade::Utility::TweakableParser::parse({input.data(), input.size()}); + CORRADE_COMPARE(state, Corrade::Utility::TweakableState::Success); + CORRADE_COMPARE(result, data.resultUb.rgb()); +} + +void ColorTest::tweakableSrgb() { + auto&& data = TweakableData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + std::string input = Corrade::Utility::formatString(data.dataRgb, "srgb"); + Corrade::Utility::TweakableState state; + Math::Vector3 result; + std::tie(state, result) = Corrade::Utility::TweakableParser>::parse({input.data(), input.size()}); + CORRADE_COMPARE(state, Corrade::Utility::TweakableState::Success); + CORRADE_COMPARE(result, data.resultUb.rgb()); +} + +void ColorTest::tweakableRgba() { + auto&& data = TweakableData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + std::string input = Corrade::Utility::formatString(data.dataRgba, "rgba"); + Corrade::Utility::TweakableState state; + Color4ub result; + std::tie(state, result) = Corrade::Utility::TweakableParser::parse({input.data(), input.size()}); + CORRADE_COMPARE(state, Corrade::Utility::TweakableState::Success); + CORRADE_COMPARE(result, data.resultUb); +} + +void ColorTest::tweakableSrgba() { + auto&& data = TweakableData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + std::string input = Corrade::Utility::formatString(data.dataRgba, "srgba"); + Corrade::Utility::TweakableState state; + Math::Vector4 result; + std::tie(state, result) = Corrade::Utility::TweakableParser>::parse({input.data(), input.size()}); + CORRADE_COMPARE(state, Corrade::Utility::TweakableState::Success); + CORRADE_COMPARE(result, data.resultUb); +} + +void ColorTest::tweakableRgbf() { + auto&& data = TweakableData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + std::string input = Corrade::Utility::formatString(data.dataRgb, "rgbf"); + Corrade::Utility::TweakableState state; + Color3 result; + std::tie(state, result) = Corrade::Utility::TweakableParser::parse({input.data(), input.size()}); + CORRADE_COMPARE(state, Corrade::Utility::TweakableState::Success); + CORRADE_COMPARE(result, data.result.rgb()); +} + +void ColorTest::tweakableSrgbf() { + auto&& data = TweakableData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + std::string input = Corrade::Utility::formatString(data.dataRgb, "srgbf"); + Corrade::Utility::TweakableState state; + Color3 result; + std::tie(state, result) = Corrade::Utility::TweakableParser::parse({input.data(), input.size()}); + CORRADE_COMPARE(state, Corrade::Utility::TweakableState::Success); + CORRADE_COMPARE(result, data.resultSrgba.rgb()); +} + +void ColorTest::tweakableRgbaf() { + auto&& data = TweakableData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + std::string input = Corrade::Utility::formatString(data.dataRgba, "rgbaf"); + Corrade::Utility::TweakableState state; + Color4 result; + std::tie(state, result) = Corrade::Utility::TweakableParser::parse({input.data(), input.size()}); + CORRADE_COMPARE(state, Corrade::Utility::TweakableState::Success); + CORRADE_COMPARE(result, data.result); +} + +void ColorTest::tweakableSrgbaf() { + auto&& data = TweakableData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + std::string input = Corrade::Utility::formatString(data.dataRgba, "srgbaf"); + Corrade::Utility::TweakableState state; + Color4 result; + std::tie(state, result) = Corrade::Utility::TweakableParser::parse({input.data(), input.size()}); + CORRADE_COMPARE(state, Corrade::Utility::TweakableState::Success); + CORRADE_COMPARE(result, data.resultSrgba); +} + +void ColorTest::tweakableErrorRgb() { + auto&& data = TweakableErrorData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + std::string input = Corrade::Utility::formatString(data.data, "ff3366", "rgb"); + + std::ostringstream out; + Warning redirectWarning{&out}; + Error redirectError{&out}; + Corrade::Utility::TweakableState state = Corrade::Utility::TweakableParser::parse({input.data(), input.size()}).first; + CORRADE_COMPARE(out.str(), Corrade::Utility::formatString(data.error, "ff3366", "rgb", "")); + CORRADE_COMPARE(state, data.state); +} + +void ColorTest::tweakableErrorSrgb() { + auto&& data = TweakableErrorData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + std::string input = Corrade::Utility::formatString(data.data, "ff3366", "srgb"); + + std::ostringstream out; + Warning redirectWarning{&out}; + Error redirectError{&out}; + Corrade::Utility::TweakableState state = Corrade::Utility::TweakableParser>::parse({input.data(), input.size()}).first; + CORRADE_COMPARE(out.str(), Corrade::Utility::formatString(data.error, "ff3366", "rgb", "s")); + CORRADE_COMPARE(state, data.state); +} + +void ColorTest::tweakableErrorRgba() { + auto&& data = TweakableErrorData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + std::string input = Corrade::Utility::formatString(data.data, "ff3366aa", "rgba"); + + std::ostringstream out; + Warning redirectWarning{&out}; + Error redirectError{&out}; + Corrade::Utility::TweakableState state = Corrade::Utility::TweakableParser::parse({input.data(), input.size()}).first; + CORRADE_COMPARE(out.str(), Corrade::Utility::formatString(data.error, "ff3366aa", "rgba", "")); + CORRADE_COMPARE(state, data.state); +} + +void ColorTest::tweakableErrorSrgba() { + auto&& data = TweakableErrorData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + std::string input = Corrade::Utility::formatString(data.data, "ff3366aa", "srgba"); + + std::ostringstream out; + Warning redirectWarning{&out}; + Error redirectError{&out}; + Corrade::Utility::TweakableState state = Corrade::Utility::TweakableParser>::parse({input.data(), input.size()}).first; + CORRADE_COMPARE(out.str(), Corrade::Utility::formatString(data.error, "ff3366aa", "rgba", "s")); + CORRADE_COMPARE(state, data.state); +} + +void ColorTest::tweakableErrorRgbf() { + auto&& data = TweakableErrorData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + std::string input = Corrade::Utility::formatString(data.data, "ff3366", "rgbf"); + + std::ostringstream out; + Warning redirectWarning{&out}; + Error redirectError{&out}; + Corrade::Utility::TweakableState state = Corrade::Utility::TweakableParser::parse({input.data(), input.size()}).first; + CORRADE_COMPARE(out.str(), Corrade::Utility::formatString(data.error, "ff3366", "rgbf", "")); + CORRADE_COMPARE(state, data.state); +} + +void ColorTest::tweakableErrorSrgbf() { + auto&& data = TweakableErrorData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + std::string input = Corrade::Utility::formatString(data.data, "ff3366", "srgbf"); + + std::ostringstream out; + Warning redirectWarning{&out}; + Error redirectError{&out}; + Corrade::Utility::TweakableState state = Corrade::Utility::TweakableParser::parse({input.data(), input.size()}).first; + CORRADE_COMPARE(out.str(), Corrade::Utility::formatString(data.error, "ff3366", "rgbf", "s")); + CORRADE_COMPARE(state, data.state); +} + +void ColorTest::tweakableErrorRgbaf() { + auto&& data = TweakableErrorData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + std::string input = Corrade::Utility::formatString(data.data, "ff3366aa", "rgbaf"); + + std::ostringstream out; + Warning redirectWarning{&out}; + Error redirectError{&out}; + Corrade::Utility::TweakableState state = Corrade::Utility::TweakableParser::parse({input.data(), input.size()}).first; + CORRADE_COMPARE(out.str(), Corrade::Utility::formatString(data.error, "ff3366aa", "rgbaf", "")); + CORRADE_COMPARE(state, data.state); +} + +void ColorTest::tweakableErrorSrgbaf() { + auto&& data = TweakableErrorData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + std::string input = Corrade::Utility::formatString(data.data, "ff3366aa", "srgbaf"); + + std::ostringstream out; + Warning redirectWarning{&out}; + Error redirectError{&out}; + Corrade::Utility::TweakableState state = Corrade::Utility::TweakableParser::parse({input.data(), input.size()}).first; + CORRADE_COMPARE(out.str(), Corrade::Utility::formatString(data.error, "ff3366aa", "rgbaf", "s")); + CORRADE_COMPARE(state, data.state); +} + }}} CORRADE_TEST_MAIN(Magnum::Math::Test::ColorTest) diff --git a/src/Magnum/Math/Test/HalfTest.cpp b/src/Magnum/Math/Test/HalfTest.cpp index e49f70304..279bdd8dd 100644 --- a/src/Magnum/Math/Test/HalfTest.cpp +++ b/src/Magnum/Math/Test/HalfTest.cpp @@ -23,8 +23,11 @@ DEALINGS IN THE SOFTWARE. */ +#include +#include #include #include +#include #include "Magnum/Math/Half.h" #include "Magnum/Math/Vector3.h" @@ -59,6 +62,8 @@ struct HalfTest: Corrade::TestSuite::Tester { void literal(); void debug(); + void tweakable(); + void tweakableError(); private: /* Naive / ground-truth packing helpers */ @@ -79,6 +84,42 @@ struct HalfTest: Corrade::TestSuite::Tester { typedef Math::Constants Constants; +using namespace Literals; + +namespace { + +const struct { + const char* name; + const char* data; + Half result; +} TweakableData[] { + {"fixed", "35.0_h", 35.0_h}, + {"no zero before", ".5_h", 0.5_h}, + {"no zero after", "35._h", 35.0_h}, + {"exponential positive", "3.5e+1_h", 3.5e+1_h}, + {"exponential negative", "350.0e-1_h", 350.0e-1_h}, + {"positive", "+35.0_h", +35.0_h}, + {"negative", "-35.0_h", -35.0_h} +}; + +constexpr struct { + const char* name; + const char* data; + Corrade::Utility::TweakableState state; + const char* error; +} TweakableErrorData[] { + {"empty", "", Corrade::Utility::TweakableState::Recompile, + "Utility::TweakableParser: is not a half literal\n"}, + {"integral", "42_h", Corrade::Utility::TweakableState::Recompile, + "Utility::TweakableParser: 42_h is not a half literal\n"}, + {"garbage after", "42.b_h", Corrade::Utility::TweakableState::Recompile, + "Utility::TweakableParser: unexpected characters b_h after a half literal\n"}, + {"different suffix", "42.0u", Corrade::Utility::TweakableState::Recompile, /* not for double */ + "Utility::TweakableParser: 42.0u has an unexpected suffix, expected _h\n"} +}; + +} + HalfTest::HalfTest() { addTests({&HalfTest::unpack, &HalfTest::pack}); @@ -109,6 +150,12 @@ HalfTest::HalfTest() { &HalfTest::literal, &HalfTest::debug}); + addInstancedTests({&HalfTest::tweakable}, + Corrade::Containers::arraySize(TweakableData)); + + addInstancedTests({&HalfTest::tweakableError}, + Corrade::Containers::arraySize(TweakableErrorData)); + /* Calculate tables for table-based benchmark */ _mantissaTable[0] = 0; for(std::size_t i = 1; i != 1024; ++i) @@ -597,16 +644,12 @@ void HalfTest::negation() { } void HalfTest::literal() { - using namespace Literals; - Half a = 3.5_h; CORRADE_COMPARE(a, Half{UnsignedShort(0x4300)}); CORRADE_COMPARE(a, Half{3.5f}); } void HalfTest::debug() { - using namespace Literals; - std::ostringstream out; Debug{&out} << -36.41_h << Half{Constants::inf()} @@ -618,6 +661,28 @@ void HalfTest::debug() { #endif } +void HalfTest::tweakable() { + auto&& data = TweakableData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + Corrade::Utility::TweakableState state; + Half result; + std::tie(state, result) = Corrade::Utility::TweakableParser::parse({data.data, std::strlen(data.data)}); + CORRADE_COMPARE(state, Corrade::Utility::TweakableState::Success); + CORRADE_COMPARE(result, data.result); +} + +void HalfTest::tweakableError() { + auto&& data = TweakableErrorData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + std::ostringstream out; + Warning redirectWarning{&out}; + Error redirectError{&out}; + Corrade::Utility::TweakableState state = Corrade::Utility::TweakableParser::parse({data.data, std::strlen(data.data)}).first; + CORRADE_COMPARE(out.str(), data.error); + CORRADE_COMPARE(state, data.state); +} + }}} CORRADE_TEST_MAIN(Magnum::Math::Test::HalfTest)