diff --git a/src/Imath/ImathTypeTraits.h b/src/Imath/ImathTypeTraits.h index c3ad212a..0bde27af 100644 --- a/src/Imath/ImathTypeTraits.h +++ b/src/Imath/ImathTypeTraits.h @@ -30,6 +30,12 @@ using enable_if_t = typename std::enable_if::type; #define IMATH_ENABLE_IF(...) \ IMATH_INTERNAL_NAMESPACE::enable_if_t<(__VA_ARGS__), int> = 0 +/// A type trait that identifies types for which Vec length/normalize operations +/// are meaningful. Defaults to std::is_floating_point, with a specialization +/// for half. +template +struct is_float_like : public std::is_floating_point {}; + #if IMATH_FOREIGN_VECTOR_INTEROP /// @{ diff --git a/src/Imath/ImathVec.h b/src/Imath/ImathVec.h index 86e73d88..bb1783b4 100644 --- a/src/Imath/ImathVec.h +++ b/src/Imath/ImathVec.h @@ -36,6 +36,9 @@ IMATH_INTERNAL_NAMESPACE_HEADER_ENTER +/// Specialization so that Vec supports length/normalize. +template <> struct is_float_like : public std::true_type {}; + template class Vec2; template class Vec3; template class Vec4; @@ -280,6 +283,7 @@ template class IMATH_EXPORT_TEMPLATE_TYPE Vec2 /// @name Query and Manipulation /// Return the Euclidean norm + template ::value)> IMATH_HOSTDEVICE T length () const IMATH_NOEXCEPT; /// Return the square of the Euclidean norm, i.e. the dot product @@ -287,27 +291,33 @@ template class IMATH_EXPORT_TEMPLATE_TYPE Vec2 IMATH_HOSTDEVICE constexpr T length2 () const IMATH_NOEXCEPT; /// Normalize in place. If length()==0, return a null vector. + template ::value)> IMATH_HOSTDEVICE const Vec2& normalize () IMATH_NOEXCEPT; /// Normalize in place. If length()==0, throw an exception. + template ::value)> const Vec2& normalizeExc (); /// Normalize without any checks for length()==0. Slightly faster /// than the other normalization routines, but if v.length() is /// 0.0, the result is undefined. + template ::value)> IMATH_HOSTDEVICE const Vec2& normalizeNonNull () IMATH_NOEXCEPT; /// Return a normalized vector. Does not modify *this. + template ::value)> IMATH_HOSTDEVICE Vec2 normalized () const IMATH_NOEXCEPT; /// Return a normalized vector. Does not modify *this. Throw an /// exception if length()==0. + template ::value)> Vec2 normalizedExc () const; /// Return a normalized vector. Does not modify *this, and does /// not check for length()==0. Slightly faster than the other /// normalization routines, but if v.length() is 0.0, the result /// is undefined. + template ::value)> IMATH_HOSTDEVICE Vec2 normalizedNonNull () const IMATH_NOEXCEPT; /// @} @@ -612,6 +622,7 @@ template class IMATH_EXPORT_TEMPLATE_TYPE Vec3 /// @name Query and Manipulation /// Return the Euclidean norm + template ::value)> IMATH_HOSTDEVICE T length () const IMATH_NOEXCEPT; /// Return the square of the Euclidean norm, i.e. the dot product @@ -619,28 +630,33 @@ template class IMATH_EXPORT_TEMPLATE_TYPE Vec3 IMATH_HOSTDEVICE constexpr T length2 () const IMATH_NOEXCEPT; /// Normalize in place. If length()==0, return a null vector. + template ::value)> IMATH_HOSTDEVICE const Vec3& normalize () IMATH_NOEXCEPT; /// Normalize in place. If length()==0, throw an exception. + template ::value)> const Vec3& normalizeExc (); /// Normalize without any checks for length()==0. Slightly faster /// than the other normalization routines, but if v.length() is /// 0.0, the result is undefined. + template ::value)> IMATH_HOSTDEVICE const Vec3& normalizeNonNull () IMATH_NOEXCEPT; /// Return a normalized vector. Does not modify *this. - IMATH_HOSTDEVICE Vec3 - normalized () const IMATH_NOEXCEPT; // does not modify *this + template ::value)> + IMATH_HOSTDEVICE Vec3 normalized () const IMATH_NOEXCEPT; /// Return a normalized vector. Does not modify *this. Throw an /// exception if length()==0. + template ::value)> Vec3 normalizedExc () const; /// Return a normalized vector. Does not modify *this, and does /// not check for length()==0. Slightly faster than the other /// normalization routines, but if v.length() is 0.0, the result /// is undefined. + template ::value)> IMATH_HOSTDEVICE Vec3 normalizedNonNull () const IMATH_NOEXCEPT; /// @} @@ -926,6 +942,7 @@ template class IMATH_EXPORT_TEMPLATE_TYPE Vec4 /// @name Query and Manipulation /// Return the Euclidean norm + template ::value)> IMATH_HOSTDEVICE T length () const IMATH_NOEXCEPT; /// Return the square of the Euclidean norm, i.e. the dot product @@ -933,28 +950,33 @@ template class IMATH_EXPORT_TEMPLATE_TYPE Vec4 IMATH_HOSTDEVICE constexpr T length2 () const IMATH_NOEXCEPT; /// Normalize in place. If length()==0, return a null vector. - IMATH_HOSTDEVICE const Vec4& normalize () IMATH_NOEXCEPT; // modifies *this + template ::value)> + IMATH_HOSTDEVICE const Vec4& normalize () IMATH_NOEXCEPT; /// Normalize in place. If length()==0, throw an exception. + template ::value)> const Vec4& normalizeExc (); /// Normalize without any checks for length()==0. Slightly faster /// than the other normalization routines, but if v.length() is /// 0.0, the result is undefined. + template ::value)> IMATH_HOSTDEVICE const Vec4& normalizeNonNull () IMATH_NOEXCEPT; /// Return a normalized vector. Does not modify *this. - IMATH_HOSTDEVICE Vec4 - normalized () const IMATH_NOEXCEPT; // does not modify *this + template ::value)> + IMATH_HOSTDEVICE Vec4 normalized () const IMATH_NOEXCEPT; /// Return a normalized vector. Does not modify *this. Throw an /// exception if length()==0. + template ::value)> Vec4 normalizedExc () const; /// Return a normalized vector. Does not modify *this, and does /// not check for length()==0. Slightly faster than the other /// normalization routines, but if v.length() is 0.0, the result /// is undefined. + template ::value)> IMATH_HOSTDEVICE Vec4 normalizedNonNull () const IMATH_NOEXCEPT; /// @} @@ -1084,178 +1106,6 @@ typedef Vec4 V4f; /// Vec4 of double typedef Vec4 V4d; -//---------------------------------------------------------------------------- -// Specializations for VecN, VecN -// -// Normalize and length don't make sense for integer vectors, so disable them. -//---------------------------------------------------------------------------- - -/// @cond Doxygen_Suppress - -// Vec2 -template <> -IMATH_HOSTDEVICE short Vec2::length () const IMATH_NOEXCEPT = delete; -template <> -IMATH_HOSTDEVICE const Vec2& - Vec2::normalize () IMATH_NOEXCEPT = delete; -template <> const Vec2& Vec2::normalizeExc () = delete; -template <> -IMATH_HOSTDEVICE const Vec2& - Vec2::normalizeNonNull () IMATH_NOEXCEPT = delete; -template <> -IMATH_HOSTDEVICE Vec2 - Vec2::normalized () const IMATH_NOEXCEPT = delete; -template <> Vec2 Vec2::normalizedExc () const = delete; -template <> -IMATH_HOSTDEVICE Vec2 -Vec2::normalizedNonNull () const IMATH_NOEXCEPT = delete; - -// Vec2 -template <> -IMATH_HOSTDEVICE int Vec2::length () const IMATH_NOEXCEPT = delete; -template <> -IMATH_HOSTDEVICE const Vec2& - Vec2::normalize () IMATH_NOEXCEPT = delete; -template <> const Vec2& Vec2::normalizeExc () = delete; -template <> -IMATH_HOSTDEVICE const Vec2& - Vec2::normalizeNonNull () IMATH_NOEXCEPT = delete; -template <> -IMATH_HOSTDEVICE Vec2 - Vec2::normalized () const IMATH_NOEXCEPT = delete; -template <> Vec2 Vec2::normalizedExc () const = delete; -template <> -IMATH_HOSTDEVICE Vec2 - Vec2::normalizedNonNull () const IMATH_NOEXCEPT = delete; - -// Vec2 -template <> -IMATH_HOSTDEVICE int64_t Vec2::length () const IMATH_NOEXCEPT = delete; -template <> -IMATH_HOSTDEVICE const Vec2& - Vec2::normalize () IMATH_NOEXCEPT = delete; -template <> const Vec2& Vec2::normalizeExc () = delete; -template <> -IMATH_HOSTDEVICE const Vec2& -Vec2::normalizeNonNull () IMATH_NOEXCEPT = delete; -template <> -IMATH_HOSTDEVICE Vec2 - Vec2::normalized () const IMATH_NOEXCEPT = delete; -template <> Vec2 Vec2::normalizedExc () const = delete; -template <> -IMATH_HOSTDEVICE Vec2 -Vec2::normalizedNonNull () const IMATH_NOEXCEPT = delete; - -// Vec3 -template <> -IMATH_HOSTDEVICE short Vec3::length () const IMATH_NOEXCEPT = delete; -template <> -IMATH_HOSTDEVICE const Vec3& - Vec3::normalize () IMATH_NOEXCEPT = delete; -template <> const Vec3& Vec3::normalizeExc () = delete; -template <> -IMATH_HOSTDEVICE const Vec3& - Vec3::normalizeNonNull () IMATH_NOEXCEPT = delete; -template <> -IMATH_HOSTDEVICE Vec3 - Vec3::normalized () const IMATH_NOEXCEPT = delete; -template <> Vec3 Vec3::normalizedExc () const = delete; -template <> -IMATH_HOSTDEVICE Vec3 -Vec3::normalizedNonNull () const IMATH_NOEXCEPT = delete; - -// Vec3 -template <> -IMATH_HOSTDEVICE int Vec3::length () const IMATH_NOEXCEPT = delete; -template <> -IMATH_HOSTDEVICE const Vec3& - Vec3::normalize () IMATH_NOEXCEPT = delete; -template <> const Vec3& Vec3::normalizeExc () = delete; -template <> -IMATH_HOSTDEVICE const Vec3& - Vec3::normalizeNonNull () IMATH_NOEXCEPT = delete; -template <> -IMATH_HOSTDEVICE Vec3 - Vec3::normalized () const IMATH_NOEXCEPT = delete; -template <> Vec3 Vec3::normalizedExc () const = delete; -template <> -IMATH_HOSTDEVICE Vec3 - Vec3::normalizedNonNull () const IMATH_NOEXCEPT = delete; - -// Vec3 -template <> -IMATH_HOSTDEVICE int64_t Vec3::length () const IMATH_NOEXCEPT = delete; -template <> -IMATH_HOSTDEVICE const Vec3& - Vec3::normalize () IMATH_NOEXCEPT = delete; -template <> const Vec3& Vec3::normalizeExc () = delete; -template <> -IMATH_HOSTDEVICE const Vec3& -Vec3::normalizeNonNull () IMATH_NOEXCEPT = delete; -template <> -IMATH_HOSTDEVICE Vec3 - Vec3::normalized () const IMATH_NOEXCEPT = delete; -template <> Vec3 Vec3::normalizedExc () const = delete; -template <> -IMATH_HOSTDEVICE Vec3 -Vec3::normalizedNonNull () const IMATH_NOEXCEPT = delete; - -// Vec4 -template <> -IMATH_HOSTDEVICE short Vec4::length () const IMATH_NOEXCEPT = delete; -template <> -IMATH_HOSTDEVICE const Vec4& - Vec4::normalize () IMATH_NOEXCEPT = delete; -template <> const Vec4& Vec4::normalizeExc () = delete; -template <> -IMATH_HOSTDEVICE const Vec4& - Vec4::normalizeNonNull () IMATH_NOEXCEPT = delete; -template <> -IMATH_HOSTDEVICE Vec4 - Vec4::normalized () const IMATH_NOEXCEPT = delete; -template <> Vec4 Vec4::normalizedExc () const = delete; -template <> -IMATH_HOSTDEVICE Vec4 -Vec4::normalizedNonNull () const IMATH_NOEXCEPT = delete; - -// Vec4 -template <> -IMATH_HOSTDEVICE int Vec4::length () const IMATH_NOEXCEPT = delete; -template <> -IMATH_HOSTDEVICE const Vec4& - Vec4::normalize () IMATH_NOEXCEPT = delete; -template <> const Vec4& Vec4::normalizeExc () = delete; -template <> -IMATH_HOSTDEVICE const Vec4& - Vec4::normalizeNonNull () IMATH_NOEXCEPT = delete; -template <> -IMATH_HOSTDEVICE Vec4 - Vec4::normalized () const IMATH_NOEXCEPT = delete; -template <> Vec4 Vec4::normalizedExc () const = delete; -template <> -IMATH_HOSTDEVICE Vec4 - Vec4::normalizedNonNull () const IMATH_NOEXCEPT = delete; - -// Vec4 -template <> -IMATH_HOSTDEVICE int64_t Vec4::length () const IMATH_NOEXCEPT = delete; -template <> -IMATH_HOSTDEVICE const Vec4& - Vec4::normalize () IMATH_NOEXCEPT = delete; -template <> const Vec4& Vec4::normalizeExc () = delete; -template <> -IMATH_HOSTDEVICE const Vec4& -Vec4::normalizeNonNull () IMATH_NOEXCEPT = delete; -template <> -IMATH_HOSTDEVICE Vec4 - Vec4::normalized () const IMATH_NOEXCEPT = delete; -template <> Vec4 Vec4::normalizedExc () const = delete; -template <> -IMATH_HOSTDEVICE Vec4 -Vec4::normalizedNonNull () const IMATH_NOEXCEPT = delete; - -/// @endcond Doxygen_Suppress - //------------------------ // Implementation of Vec2: //------------------------ @@ -1579,6 +1429,7 @@ Vec2::lengthTiny () const IMATH_NOEXCEPT } template +template ::value), int>> IMATH_HOSTDEVICE inline T Vec2::length () const IMATH_NOEXCEPT { @@ -1598,6 +1449,7 @@ Vec2::length2 () const IMATH_NOEXCEPT } template +template ::value), int>> IMATH_HOSTDEVICE inline const Vec2& Vec2::normalize () IMATH_NOEXCEPT { @@ -1619,6 +1471,7 @@ Vec2::normalize () IMATH_NOEXCEPT } template +template ::value), int>> inline const Vec2& Vec2::normalizeExc () { @@ -1633,6 +1486,7 @@ Vec2::normalizeExc () } template +template ::value), int>> IMATH_HOSTDEVICE inline const Vec2& Vec2::normalizeNonNull () IMATH_NOEXCEPT { @@ -1643,6 +1497,7 @@ Vec2::normalizeNonNull () IMATH_NOEXCEPT } template +template ::value), int>> IMATH_HOSTDEVICE inline Vec2 Vec2::normalized () const IMATH_NOEXCEPT { @@ -1654,6 +1509,7 @@ Vec2::normalized () const IMATH_NOEXCEPT } template +template ::value), int>> inline Vec2 Vec2::normalizedExc () const { @@ -1666,6 +1522,7 @@ Vec2::normalizedExc () const } template +template ::value), int>> IMATH_HOSTDEVICE inline Vec2 Vec2::normalizedNonNull () const IMATH_NOEXCEPT { @@ -2060,6 +1917,7 @@ Vec3::lengthTiny () const IMATH_NOEXCEPT } template +template ::value), int>> IMATH_HOSTDEVICE inline T Vec3::length () const IMATH_NOEXCEPT { @@ -2079,6 +1937,7 @@ Vec3::length2 () const IMATH_NOEXCEPT } template +template ::value), int>> IMATH_HOSTDEVICE inline const Vec3& Vec3::normalize () IMATH_NOEXCEPT { @@ -2101,6 +1960,7 @@ Vec3::normalize () IMATH_NOEXCEPT } template +template ::value), int>> inline const Vec3& Vec3::normalizeExc () { @@ -2116,6 +1976,7 @@ Vec3::normalizeExc () } template +template ::value), int>> IMATH_HOSTDEVICE inline const Vec3& Vec3::normalizeNonNull () IMATH_NOEXCEPT { @@ -2127,6 +1988,7 @@ Vec3::normalizeNonNull () IMATH_NOEXCEPT } template +template ::value), int>> IMATH_HOSTDEVICE inline Vec3 Vec3::normalized () const IMATH_NOEXCEPT { @@ -2138,6 +2000,7 @@ Vec3::normalized () const IMATH_NOEXCEPT } template +template ::value), int>> inline Vec3 Vec3::normalizedExc () const { @@ -2150,6 +2013,7 @@ Vec3::normalizedExc () const } template +template ::value), int>> IMATH_HOSTDEVICE inline Vec3 Vec3::normalizedNonNull () const IMATH_NOEXCEPT { @@ -2514,6 +2378,7 @@ Vec4::lengthTiny () const IMATH_NOEXCEPT } template +template ::value), int>> IMATH_HOSTDEVICE inline T Vec4::length () const IMATH_NOEXCEPT { @@ -2533,7 +2398,8 @@ Vec4::length2 () const IMATH_NOEXCEPT } template -IMATH_HOSTDEVICE const inline Vec4& +template ::value), int>> +IMATH_HOSTDEVICE inline const Vec4& Vec4::normalize () IMATH_NOEXCEPT { T l = length (); @@ -2556,7 +2422,8 @@ Vec4::normalize () IMATH_NOEXCEPT } template -const inline Vec4& +template ::value), int>> +inline const Vec4& Vec4::normalizeExc () { T l = length (); @@ -2572,6 +2439,7 @@ Vec4::normalizeExc () } template +template ::value), int>> IMATH_HOSTDEVICE inline const Vec4& Vec4::normalizeNonNull () IMATH_NOEXCEPT { @@ -2584,6 +2452,7 @@ Vec4::normalizeNonNull () IMATH_NOEXCEPT } template +template ::value), int>> IMATH_HOSTDEVICE inline Vec4 Vec4::normalized () const IMATH_NOEXCEPT { @@ -2595,6 +2464,7 @@ Vec4::normalized () const IMATH_NOEXCEPT } template +template ::value), int>> inline Vec4 Vec4::normalizedExc () const { @@ -2607,6 +2477,7 @@ Vec4::normalizedExc () const } template +template ::value), int>> IMATH_HOSTDEVICE inline Vec4 Vec4::normalizedNonNull () const IMATH_NOEXCEPT { diff --git a/src/ImathTest/testVec.cpp b/src/ImathTest/testVec.cpp index e653eaf1..2efe5d0d 100644 --- a/src/ImathTest/testVec.cpp +++ b/src/ImathTest/testVec.cpp @@ -13,6 +13,8 @@ #include #include #include +#include +#include // Include ImathForward *after* other headers to validate forward declarations #include @@ -23,6 +25,332 @@ using namespace IMATH_INTERNAL_NAMESPACE; namespace { +// +// Compile-time detection of whether an expression is well-formed. This is +// used to confirm that length(), normalize(), normalizeExc(), +// normalizeNonNull(), normalized(), normalizedExc(), and +// normalizedNonNull() are visible (via SFINAE) on Vec2/Vec3/Vec4 for +// floating-point-like element types (float, double, half), and are not +// visible at all for integer element types (short, int, int64_t). +// + +#define IMATH_TEST_HAS_METHOD(name) \ + template \ + struct has_##name : std::false_type \ + {}; \ + template \ + struct has_##name< \ + T, \ + decltype ((void) std::declval ().name ())> : std::true_type \ + {} + +IMATH_TEST_HAS_METHOD (length); +IMATH_TEST_HAS_METHOD (normalize); +IMATH_TEST_HAS_METHOD (normalizeExc); +IMATH_TEST_HAS_METHOD (normalizeNonNull); +IMATH_TEST_HAS_METHOD (normalized); +IMATH_TEST_HAS_METHOD (normalizedExc); +IMATH_TEST_HAS_METHOD (normalizedNonNull); + +#undef IMATH_TEST_HAS_METHOD + +// Confirm that the given VecN supports all of length()/normalize()/etc. +#define IMATH_STATIC_ASSERT_HAS_ALL(Vec, T) \ + static_assert (has_length>::value, #Vec "<" #T ">::length"); \ + static_assert (has_normalize>::value, #Vec "<" #T ">::normalize"); \ + static_assert ( \ + has_normalizeExc>::value, #Vec "<" #T ">::normalizeExc"); \ + static_assert ( \ + has_normalizeNonNull>::value, \ + #Vec "<" #T ">::normalizeNonNull"); \ + static_assert ( \ + has_normalized>::value, #Vec "<" #T ">::normalized"); \ + static_assert ( \ + has_normalizedExc>::value, #Vec "<" #T ">::normalizedExc"); \ + static_assert ( \ + has_normalizedNonNull>::value, \ + #Vec "<" #T ">::normalizedNonNull") + +// Confirm that the given VecN supports none of length()/normalize()/etc. +#define IMATH_STATIC_ASSERT_HAS_NONE(Vec, T) \ + static_assert (!has_length>::value, #Vec "<" #T ">::length"); \ + static_assert ( \ + !has_normalize>::value, #Vec "<" #T ">::normalize"); \ + static_assert ( \ + !has_normalizeExc>::value, #Vec "<" #T ">::normalizeExc"); \ + static_assert ( \ + !has_normalizeNonNull>::value, \ + #Vec "<" #T ">::normalizeNonNull"); \ + static_assert ( \ + !has_normalized>::value, #Vec "<" #T ">::normalized"); \ + static_assert ( \ + !has_normalizedExc>::value, #Vec "<" #T ">::normalizedExc"); \ + static_assert ( \ + !has_normalizedNonNull>::value, \ + #Vec "<" #T ">::normalizedNonNull") + +// Floating-point-like element types: length()/normalize()/etc. must be +// present. +IMATH_STATIC_ASSERT_HAS_ALL (Vec2, float); +IMATH_STATIC_ASSERT_HAS_ALL (Vec2, double); +IMATH_STATIC_ASSERT_HAS_ALL (Vec2, half); +IMATH_STATIC_ASSERT_HAS_ALL (Vec3, float); +IMATH_STATIC_ASSERT_HAS_ALL (Vec3, double); +IMATH_STATIC_ASSERT_HAS_ALL (Vec3, half); +IMATH_STATIC_ASSERT_HAS_ALL (Vec4, float); +IMATH_STATIC_ASSERT_HAS_ALL (Vec4, double); +IMATH_STATIC_ASSERT_HAS_ALL (Vec4, half); + +// Integer element types: length()/normalize()/etc. must not be present. +IMATH_STATIC_ASSERT_HAS_NONE (Vec2, short); +IMATH_STATIC_ASSERT_HAS_NONE (Vec2, int); +IMATH_STATIC_ASSERT_HAS_NONE (Vec2, int64_t); +IMATH_STATIC_ASSERT_HAS_NONE (Vec3, short); +IMATH_STATIC_ASSERT_HAS_NONE (Vec3, int); +IMATH_STATIC_ASSERT_HAS_NONE (Vec3, int64_t); +IMATH_STATIC_ASSERT_HAS_NONE (Vec4, short); +IMATH_STATIC_ASSERT_HAS_NONE (Vec4, int); +IMATH_STATIC_ASSERT_HAS_NONE (Vec4, int64_t); + +// +// An application-defined scalar class, analogous to `half`, that behaves +// like a floating-point number but is not one of the builtin +// floating-point types. An application that wants Vec2/Vec3/Vec4 of this +// type to support length()/normalize()/etc. must specialize +// Imath::is_float_like<> for it, exactly as Imath itself does for `half`. +// +class CustomFloat +{ +public: + CustomFloat () IMATH_NOEXCEPT : _v (0.0) {} + CustomFloat (double v) IMATH_NOEXCEPT : _v (v) {} + operator double () const IMATH_NOEXCEPT { return _v; } + + CustomFloat& operator+= (const CustomFloat& o) IMATH_NOEXCEPT + { + _v += o._v; + return *this; + } + CustomFloat& operator-= (const CustomFloat& o) IMATH_NOEXCEPT + { + _v -= o._v; + return *this; + } + CustomFloat& operator*= (const CustomFloat& o) IMATH_NOEXCEPT + { + _v *= o._v; + return *this; + } + CustomFloat& operator/= (const CustomFloat& o) IMATH_NOEXCEPT + { + _v /= o._v; + return *this; + } + + friend CustomFloat + operator+ (CustomFloat a, const CustomFloat& b) IMATH_NOEXCEPT + { + a += b; + return a; + } + friend CustomFloat + operator- (CustomFloat a, const CustomFloat& b) IMATH_NOEXCEPT + { + a -= b; + return a; + } + friend CustomFloat + operator* (CustomFloat a, const CustomFloat& b) IMATH_NOEXCEPT + { + a *= b; + return a; + } + friend CustomFloat + operator/ (CustomFloat a, const CustomFloat& b) IMATH_NOEXCEPT + { + a /= b; + return a; + } + friend CustomFloat operator- (const CustomFloat& a) IMATH_NOEXCEPT + { + return CustomFloat (-a._v); + } + + // Mixed CustomFloat/double overloads: without these, expressions like + // `customFloatValue * std::sqrt (x)` (which mixes CustomFloat with a + // plain double) are ambiguous, since both the implicit + // double-to-CustomFloat constructor and the implicit + // CustomFloat-to-double conversion operator are viable, equally-ranked + // conversions. + friend CustomFloat operator* (const CustomFloat& a, double b) IMATH_NOEXCEPT + { + return CustomFloat (a._v * b); + } + friend CustomFloat operator* (double a, const CustomFloat& b) IMATH_NOEXCEPT + { + return CustomFloat (a * b._v); + } + + friend bool + operator== (const CustomFloat& a, const CustomFloat& b) IMATH_NOEXCEPT + { + return a._v == b._v; + } + friend bool + operator!= (const CustomFloat& a, const CustomFloat& b) IMATH_NOEXCEPT + { + return a._v != b._v; + } + friend bool + operator< (const CustomFloat& a, const CustomFloat& b) IMATH_NOEXCEPT + { + return a._v < b._v; + } + friend bool + operator> (const CustomFloat& a, const CustomFloat& b) IMATH_NOEXCEPT + { + return a._v > b._v; + } + friend bool + operator<= (const CustomFloat& a, const CustomFloat& b) IMATH_NOEXCEPT + { + return a._v <= b._v; + } + friend bool + operator>= (const CustomFloat& a, const CustomFloat& b) IMATH_NOEXCEPT + { + return a._v >= b._v; + } + +private: + double _v; +}; + +// +// An application-defined scalar class for which length()/normalize()/etc. +// should *not* be visible, because it does not represent a floating-point +// quantity (e.g. some kind of identifier, index, or fixed-point count) and +// the application does not specialize Imath::is_float_like<> for it. +// +class CustomNonFloat +{ +public: + CustomNonFloat () IMATH_NOEXCEPT : _v (0) {} + CustomNonFloat (int v) IMATH_NOEXCEPT : _v (v) {} + operator int () const IMATH_NOEXCEPT { return _v; } + + friend CustomNonFloat + operator+ (CustomNonFloat a, const CustomNonFloat& b) IMATH_NOEXCEPT + { + return CustomNonFloat (a._v + b._v); + } + friend CustomNonFloat + operator- (CustomNonFloat a, const CustomNonFloat& b) IMATH_NOEXCEPT + { + return CustomNonFloat (a._v - b._v); + } + friend CustomNonFloat + operator* (CustomNonFloat a, const CustomNonFloat& b) IMATH_NOEXCEPT + { + return CustomNonFloat (a._v * b._v); + } + friend bool operator== ( + const CustomNonFloat& a, const CustomNonFloat& b) IMATH_NOEXCEPT + { + return a._v == b._v; + } + +private: + int _v; +}; + +} // namespace + +// Tell Imath that CustomFloat should be treated as float-like, exactly as +// an application would do for its own custom scalar type. CustomNonFloat +// is deliberately left unspecialized, so it defaults to +// std::is_floating_point::value, i.e. false. +IMATH_INTERNAL_NAMESPACE_SOURCE_ENTER +template <> struct is_float_like : public std::true_type +{}; +IMATH_INTERNAL_NAMESPACE_SOURCE_EXIT + +namespace +{ + +// Floating-point-like custom type: length()/normalize()/etc. must be +// present. +IMATH_STATIC_ASSERT_HAS_ALL (Vec2, CustomFloat); +IMATH_STATIC_ASSERT_HAS_ALL (Vec3, CustomFloat); +IMATH_STATIC_ASSERT_HAS_ALL (Vec4, CustomFloat); + +// Non-floating-point custom type: length()/normalize()/etc. must not be +// present. +IMATH_STATIC_ASSERT_HAS_NONE (Vec2, CustomNonFloat); +IMATH_STATIC_ASSERT_HAS_NONE (Vec3, CustomNonFloat); +IMATH_STATIC_ASSERT_HAS_NONE (Vec4, CustomNonFloat); + +#undef IMATH_STATIC_ASSERT_HAS_ALL +#undef IMATH_STATIC_ASSERT_HAS_NONE + +void +testCustomFloatLike () +{ + const double e = 1e-9; + + // Vec2 + { + Vec2 v (3, 4); + assert ( + IMATH_INTERNAL_NAMESPACE::equal (double (v.length ()), 5.0, e)); + assert (IMATH_INTERNAL_NAMESPACE::equal ( + double (v.normalized ().length ()), 1.0, e)); + v.normalize (); + assert ( + IMATH_INTERNAL_NAMESPACE::equal (double (v.length ()), 1.0, e)); + } + + // Vec3 + { + Vec3 v (0, 3, 4); + assert ( + IMATH_INTERNAL_NAMESPACE::equal (double (v.length ()), 5.0, e)); + assert (IMATH_INTERNAL_NAMESPACE::equal ( + double (v.normalized ().length ()), 1.0, e)); + v.normalize (); + assert ( + IMATH_INTERNAL_NAMESPACE::equal (double (v.length ()), 1.0, e)); + } + + // Vec4 + { + Vec4 v (0, 0, 3, 4); + assert ( + IMATH_INTERNAL_NAMESPACE::equal (double (v.length ()), 5.0, e)); + assert (IMATH_INTERNAL_NAMESPACE::equal ( + double (v.normalized ().length ()), 1.0, e)); + v.normalize (); + assert ( + IMATH_INTERNAL_NAMESPACE::equal (double (v.length ()), 1.0, e)); + } + + // CustomNonFloat still supports ordinary Vec operations, such as + // arithmetic and equality, that are not gated by is_float_like<>. + { + Vec3 a (1, 2, 3); + Vec3 b (4, 5, 6); + Vec3 c = a + b; + assert (int (c.x) == 5 && int (c.y) == 7 && int (c.z) == 9); + assert (a == a); + assert (!(a == b)); + } +} + +} // namespace + +namespace +{ + template void testLength2T () @@ -262,5 +590,7 @@ testVec () testLength4T (); testLength4T (); + testCustomFloatLike (); + cout << "ok\n" << endl; } diff --git a/src/pybind11/PyBindImath/PyBindImathVec.h b/src/pybind11/PyBindImath/PyBindImathVec.h index f1200143..5f3a1384 100644 --- a/src/pybind11/PyBindImath/PyBindImathVec.h +++ b/src/pybind11/PyBindImath/PyBindImathVec.h @@ -387,13 +387,13 @@ template py::class_ register_vec_fp(py::class_ c) { - return c.def("length", &Vec::length, "return the magnitude of the vector") - .def("normalize", &Vec::normalize, "destructively normalizes v and returns a reference to it") - .def("normalizeExc", &Vec::normalizeExc, "destructively normalizes V and returns a reference to it, throwing an exception if length() == 0") - .def("normalizeNonNull", &Vec::normalizeNonNull, "destructively normalizes V and returns a reference to it, faster if length() != 0") - .def("normalized", &Vec::normalized, "return a normalized copy of v") - .def("normalizedExc", &Vec::normalizedExc, "returnsa normalized copy of v, throwing an exception if length() == 0") - .def("normalizedNonNull", &Vec::normalizedNonNull, "return a normalized copy of v, faster if lngth() != 0") + return c.def("length", [](const Vec& self) { return self.length(); }, "return the magnitude of the vector") + .def("normalize", [](Vec& self) -> const Vec& { return self.normalize(); }, "destructively normalizes v and returns a reference to it") + .def("normalizeExc", [](Vec& self) -> const Vec& { return self.normalizeExc(); }, "destructively normalizes V and returns a reference to it, throwing an exception if length() == 0") + .def("normalizeNonNull", [](Vec& self) -> const Vec& { return self.normalizeNonNull(); }, "destructively normalizes V and returns a reference to it, faster if length() != 0") + .def("normalized", [](const Vec& self) { return self.normalized(); }, "return a normalized copy of v") + .def("normalizedExc", [](const Vec& self) { return self.normalizedExc(); }, "returnsa normalized copy of v, throwing an exception if length() == 0") + .def("normalizedNonNull", [](const Vec& self) { return self.normalizedNonNull(); }, "return a normalized copy of v, faster if lngth() != 0") .def("orthogonal", orthogonal, "return the vector that is perpendicular to this vector") .def("project", [](const Vec& self, const Vec& p) { // In C++/Imath it's a global function; in python it's a member: