From 35e49c47e28c7d3e70bf0e7fd597d77d9a01d3d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 23 Mar 2014 19:37:31 +0100 Subject: [PATCH] doc: mention more features in math documentation. Introduce angle classes, more thoroughly explain the type conversion and do overview of component-wise functions. --- doc/matrix-vector.dox | 104 +++++++++++++++++++++++++++++++++++++----- doc/types.dox | 60 ++++++++++++++++++++++++ 2 files changed, 153 insertions(+), 11 deletions(-) diff --git a/doc/matrix-vector.dox b/doc/matrix-vector.dox index a7a3da78c..45e20bb40 100644 --- a/doc/matrix-vector.dox +++ b/doc/matrix-vector.dox @@ -83,7 +83,8 @@ Color4ub black2; // {0, 0, 0, 255} Most common and most efficient way to create vector is to pass all values to constructor, matrix is created by passing all column vectors to the -constructor. +constructor. All constructors check number of passed arguments and the errors +are catched at compile time. @code Vector3i vec(0, 1, 2); @@ -91,11 +92,9 @@ Matrix3 mat({0.0f, 1.9f, 2.2f}, {3.5f, 4.0f, 5.1f}, {6.0f, 7.3f, 8.0f}); @endcode -All constructors check number of passed arguments and the errors are catched -at compile time. -You can specify all components of vector or whole diagonal of square matrix at -once or you can create diagonal matrix from vector: +You can specify all components of vector or whole diagonal of square matrix +with single value or create diagonal matrix from vector: @code Matrix3 diag(Matrix3::Identity, 2.0f); // diagonal set to 2.0f, zeros elsewhere Vector3i fill(10); // {10, 10, 10} @@ -125,12 +124,6 @@ Math::Matrix2x3::from(mat) *= 2; // mat == { 4, 8, 12, 2, 6, 10 } Note that, unlike constructors, this function has no way to check whether the array is long enough to contain all elements, so use with caution. -You can also *explicitly* convert between data types: -@code -Vector4 floating(1.3f, 2.7f, -15.0f, 7.0f); -auto integral = Vector4i(floating); // {1, 2, -15, 7} -@endcode - @section matrix-vector-component-access Accessing matrix and vector components Column vectors of matrices and vector components can be accessed using square @@ -170,6 +163,38 @@ Vector4i bgra = swizzle<'b', 'g', 'r', 'a'>(original); // { 3, 2, -1, 4 } Math::Vector<6, Int> w10xyz = swizzle<'w', '1', '0', 'x', 'y', 'z'>(original); // { 4, 1, 0, -1, 2, 3 } @endcode +@section matrix-vector-conversion Converting between different underlying types + +All vector, matrix and other classes in @ref Math namespace and also +@ref Color3 and @ref Color4 classes are able to be constructed from type with +different underlying type (e.g. convert between integer and floating-point or +betweeen @ref Float and @ref Double). Unlike with plain C++ data types, the +conversion is done via *explicit* constructor. That might sound inconvenient, +but doing the conversion explicitly avoids common issues like precision loss +(or, on the other hand, doing computations in unnecessarily high precision). + +To further emphasise the intent of conversion (so it doesn't look like accident +or typo), you are encouraged to use `auto b = Type{a}` instead of `Type b{a}`. +@code +Vector3 a{2.2f, 0.25f, -5.1f}; +//Vector3i b = a; // error, implicit conversion not allowed +auto c = Vector3i{a}; // {2, 0, -5} +auto d = Vector3d{a}; // {2.2, 0.25, -5.1} +@endcode + +For normalizing and denormalizing there are @ref Math::normalize() and +@ref Math::denormalize() functions: +@code +Color3 a{0.8f, 1.0f, 0.3f}; +auto b = Math::denormalize(a); // {204, 255, 76} + +Color3ub c{64, 127, 89}; +auto d = Math::normalize(c); // {0.251, 0.498, 0.349} +@endcode + +See @ref matrix-vector-componentwise "below" for more information about other +available component-wise operations. + @section matrix-vector-operations Operations with matrices and vectors Vectors can be added, subtracted, negated and multiplied or divided with @@ -230,6 +255,63 @@ Math::RectangularMatrix<4, 1, Float> d; Matrix4x3 e = b*d; @endcode +@section matrix-vector-componentwise Component-wise and inter-vector operations + +As shown above, vectors can be added and multiplied component-wise using the +`+` or `*` operator. You can use @ref Vector::sum() "sum()" and +@ref Vector::product() "product()" for sum or product of components in one +vector: +@code +Float a = Vector3{1.5f, 0.3f, 8.0f}.sum(); // 8.8f +Int b = Vector3i{32, -5, 7}.product() // 1120 +@endcode + +Component-wise minimum and maximum of two vectors can be done using +@ref Math::min(), @ref Math::max() or @ref Math::minmax(), similarly with +@ref Vector::min() "min()", @ref Vector::max() "max()" and +@ref Vector2::minmax() "minmax()" for components in one vector. +@code +Vector3i a{-5, 7, 24}; +Vector3i b{8, -2, 12}; + +Vector3i min = Math::min(a, b); // {-5, -2, 12} +Int max = a.max(); // 24 +@endcode + +The vectors can be also compared component-wise, the result is returned in +@ref Math::BoolVector class: +@code +BoolVector<3> largerOrEqual = a >= b; // {false, true, true} +bool anySmaller = (a < b).any(); // true +bool allLarger = (a > b).all(); // false +@endcode + +There are also function for component-wise rounding, sign operations, square +root, various interpolation and (de)normalization functionality: +@code +Vector3 a{5.5f, -0.3f, 75.0f}; +Vector3 b = Math::round(a); // {5.0f, 0.0f, 75.0f} +Vector3 c = Math::abs(a); // {5.5f, -0.3f, 75.0f} +Vector3 d = Math::clamp(a, -0.2f, 55.0f); // {5.5f, -0.2f, 55.0f} +@endcode + +Component-wise functions are implemented only for vectors and not for matrices +to keep the math library in sane and maintainable size. Instead, you can +reinterpret the matrix as vector and do the operation on it (and vice versa): +@code +Matrix3x2 mat; +Math::Vector<6, Float> vec = mat.toVector(); +// ... +mat = Matrix3x2::fromVector(vec); +@endcode + +Note that all component-wise functions in Math namespace work also for scalars: +@code +std::pair minmax = Math::minmax(24, -5); // -5, 24 +Int a = Math::lerp(0, 360, 0.75f); // 270 +auto b = Math::denormalize(0.89f); // 226 +@endcode + @section matrix-vector-column-major Matrices are column-major and vectors are columns OpenGL matrices are column-major, thus it is reasonable to have matrices in diff --git a/doc/types.dox b/doc/types.dox index fb3a8a0c5..027c89e66 100644 --- a/doc/types.dox +++ b/doc/types.dox @@ -87,6 +87,66 @@ underlying type. Any super- or sub-class of the same size and underlying type can be used equivalently (e.g. @ref Math::Vector or @ref Color3 instead of @ref Vector3). +@section types-binary Binary representation + +Scalar types with GLSL equivalent are verified to be exactly the same as +corresponding `GL*` types. Matrix and vector classes have the same binary +representation as corresponding array of numeric values without any additional +data or padding (e.g. `sizeof(Vector3i) == sizeof(Int[3])`), all matrices are +stored in column-major order. + +This means that all scalar, matrix and vector types can be used directly for +filling GPU buffers and textures without any need for data extraction or +conversion. For convenience all vector and matrix classes provide +@ref Math::RectangularMatrix::data() "data()" function, which returns pointer +to the internal data array. + +@section types-special Special types + +%Magnum has special type for strongly-typed representation of angles, namely +the @ref Deg and @ref Rad classes (or @ref Degd and @ref Radd with @ref Double +as underlying type). Their only purpose is to avoid common degree-vs-radian +bugs (i.e. entering degree value where radians should be) and make the +conversion between these two representations easier. They are just a tiny +`inline` `constexpr` wrapper around the native type and they support all +meaningful numeric operations, so using them won't have any performance or +usability impact in practice. + +These classes are *not* implicitly constructible or convertible from/to +@ref Float or @ref Double, you have to either construct/convert them explicitly +or use custom `_degf`/`_deg` and `_radf`/`_rad` literals: +@code +//Deg a = 60.0f // error, no implicit conversion from Float +Deg a = 60.0_degf; // okay + +Float b = 3.2831853f; +auto tau = Rad{b} + 3.0_radf; +Radd pi = 3.141592653589793_rad; + +//Double c = pi; // error, no implicit conversion to Double +auto c = Double{pi}; // okay +@endcode + +They can be implicitly converted to each other, but conversion to different +underlying type is *explicit* to avoid precision loss (or, on the other hand, +unnecessarily high precision) during computations: +@code +Rad d = 60.0_degf; // 1.0471976f +auto e = Degd{pi}; // 180.0 + +//Rad f = pi; // error, no implicit conversion of underlying types +auto f = Rad{pi}; // 3.141592654f +@endcode + +These classes are used exclusively in all functions taking and returning angles +-- trigonometry, angle computation, rotating transformation etc. Thanks to +implicit conversion you can seamlessly use either radians or degrees without +any need to care about what input the function expects: +@code +Float a = Math::sin(1.32457_radf); +Complex b = Complex::rotation(60.0_degf); +@endcode + @section types-other Other types Other types, which don't have their GLSL equivalent, are: