Feature guide » Math type system

Type aliases, naming and compatibility with OpenGL, Vulkan and GLSL types.

Magnum defines a variety of scalar, vector and matrix types. Most of the functionality is implemented using template classes in the Math library, with the most common variants brought as typedefs into the root Magnum namespace.

Builtin types

Magnum provides its own typedefs for builtin integral and floating-point arithmetic types to ensure portability, maintain consistency and reduce confusion. E.g., the Int typedef is guaranteed to always be 32-bit and Magnum's own code and documentation prefers to use it over a wild mixture of std::int32_t, int, GLint or ALint that all refer to the same type.

Magnum typeSizeEquivalent GLSL type
UnsignedByte8bit unsigned(none)
Byte8bit signed(none)
UnsignedShort16bit unsigned(none)
Short16bit signed(none)
UnsignedInt32bit unsigneduint
Int32bit signedint
UnsignedLong64bit unsigned(none)
Long64bit signed(none)
Half16bit(none)
Float32bitfloat
Double64bitdouble

Types not meant to be used in arithmetic (such as bool or std::size_t) or types which have no use in GPU computations (such as long double) have no typedefs.

Types from the above table are then used to define other types. All following types are aliases of corresponding types in Math namespace. No suffix after type name means Float underlying type, h means Half, d is Double, ub UnsignedByte, b Byte, us UnsignedShort, s Short, ui UnsignedInt and i is Int.

Matrix/vector types

Magnum vector typeEquivalent GLSL type
BitVector2, BitVector3, BitVector4bvec2, bvec3, bvec4
Vector2, Vector3, Color3, Vector4, Color4vec2, vec3, vec4
Vector2h, Vector3h, Color3h, Vector4h, Color4h(none)
Vector2d, Vector3d, Vector4ddvec2, dvec3, dvec4
Vector2ub, Vector3ub, Vector4ub, Color3ub, Color4ub(none)
Vector2b, Vector3b, Vector4b(none)
Vector2us, Vector3us, Vector4us, Color3us, Color4us(none)
Vector2s, Vector3s, Vector4s(none)
Vector2ui, Vector3ui, Vector4uiuvec2, uvec3, uvec4
Vector2i, Vector3i, Vector4iivec2, ivec3, ivec4
Magnum matrix typeEquivalent GLSL type
Matrix2x2 or Matrix2x2dmat2 / mat2x2 or dmat2 / dmat2x2
Matrix2x2h, Matrix2x2b, Matrix2x2s(none)
Matrix3 / Matrix3x3 or Matrix3d / Matrix3x3dmat3 / mat3x3 or dmat3 / dmat3x3
Matrix3x3h, Matrix3x3b, Matrix3x3s(none)
Matrix4 / Matrix4x4 or Matrix4d / Matrix4x4dmat4 / mat4x4 or dmat4 / dmat4x4
Matrix4x4h, Matrix4x4b, Matrix4x4s(none)
Matrix2x3 or Matrix2x3dmat2x3 or dmat2x3
Matrix2x3h, Matrix2x3b, Matrix2x3s(none)
Matrix3x2 or Matrix3x2dmat3x2 or dmat3x2
Matrix3x2h, Matrix3x2b, Matrix3x2s(none)
Matrix2x4 or Matrix2x4dmat2x4 or dmat2x4
Matrix2x4h, Matrix2x4b, Matrix2x4s(none)
Matrix4x2 or Matrix4x2dmat4x2 or dmat4x2
Matrix4x2h, Matrix4x2b, Matrix4x2s(none)
Matrix3x4 or Matrix3x4dmat3x4 or dmat3x4
Matrix3x4h, Matrix3x4b, Matrix3x4s(none)
Matrix4x3 or Matrix4x3dmat4x3 or dmat4x3
Matrix4x3h, Matrix4x3b, Matrix4x3s(none)

Any super- or sub-class of the same size and underlying type can be used equivalently (e.g. Math::Vector<Float> or Color3 instead of Vector3).

For easier entering of (s)RGB colors in hexadecimal format there are _srgb / _srgbf, _srgba / _srgbaf, _rgb / _rgbf and _rgba / _rgbaf literals in the Math::Literals namespace. See their documentation for more information about the differences.

using namespace Math::Literals;

Color3 a = 0x33b27f_srgbf;      // {0.0331048f, 0.445201f, 0.212231f}
Color4ub b = 0x33b27fcc_rgba;   // {0x33, 0xb2, 0x7f, 0xcc}

Binary representation

Scalar types with a GLSL equivalent are guaranteed to have exactly the same binary representation. Consequently, matrix and vector classes also 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 a data() function, which returns a pointer to the internal data array.

Half-precision arithmetic

The Half type represents half-precision floating point values. The sole purpose of the type is to make creation, conversion and visualization of half-float values easier. By design it doesn't support any arithmetic operations as not all CPU architecture have native support for half-floats and thus the operations would be done faster in a regular single-precision Float. The class provides explicit constructors and conversion operators from/to Float and UnsignedShort and you can also use the _h literal that is provided in the Math::Literals namespace:

using namespace Math::Literals;

Half a = 3.5_h;         // 0x4300 internally

Half-precision vector and matrix types such as Vector3h or Matrix3x3h work similarly — you can construct them and convert them from/to other types, but can't perform any arithmetic.

Angle types

Magnum has a special type for strongly-typed representation of angles, namely the Deg and Rad classes (or Degd / Degh and Radd / Radh with Double / Half as underlying type). Their purpose is to avoid common degree-vs-radian bugs (i.e. entering a degree value where radians should be and vice versa) and make the conversion between these two representations easier. They are just a tiny constexpr wrapper around the native type and they support all meaningful numeric operations. The wrapper API may have a slight overhead on debug builds, but the safety benefits outweight that in most practical use cases.

These classes are not implicitly constructible or convertible from/to Float or Double, you have to either construct/convert them explicitly or use custom _degf / _deg and _radf / _rad literals that are provided in the Math::Literals namespace:

using namespace Math::Literals;

//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

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:

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

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:

Float a = Math::sin(1.32457_radf);
Complex b = Complex::rotation(60.0_degf);

Time types

Similarly to Deg and Rad, there's Nanoseconds and Seconds for strongly-typed representation of time values. The Nanoseconds is a 64-bit integer type, giving the best possible precision over a range of ±292 years, while Seconds is a 32-bit floating-point type that should be sufficient for most practical uses where neither large precision nor a large range is needed, such as animation keyframe timing. As with the angle types, they're not implicitly constructible from their underlying representation, instead you can construct them explicitly or use the _sec, _msec, _usec and _nsec convenience literals that are provided in the Math::Literals namespace. The time types are similar in spirit to std::chrono type definitions, but without a dependency on STL. An opt-in conversion is available if you include Magnum/Math/TimeStl.h.

#include <Magnum/Math/TimeStl.h>



using namespace Math::Literals;

Nanoseconds currentFrameTime{std::chrono::steady_clock::now()};

if(currentFrameTime - previousFrameTime < 16.667_msec)
    stillCanDoSomething();

Other types

Other types, which don't have their GLSL equivalent, are:

These types can be used in GLSL either by extracting values from their underlying structure or converting them to types supported by GLSL (e.g. quaternion to matrix).

For your convenience, there is also a structure with often used constants — Constants, or Constantsd for the double-precision variants.

Initialization

Vectors, general matrices and range types are by default zero-initialized, transformation types (square matrices, (dual) complex numbers and quaternions) are set to identity transformation. It is possible to initialize the instances differently using so-called tags or use the tag to make the choice appear explicit:

  • Math::ZeroInit zero-initializes the contents (works for all types).
  • Math::IdentityInit initializes the contents to identity transformation (works only for transformation types, where it is also the default).
  • NoInit leaves the contents uninitialized (useful if you will overwrite the contents anyway, works for all types).

Example:

/* These are equivalent */
Vector3 a1;
Vector3 a2{Math::ZeroInit};

/* These too */
Quaternion q1;
Quaternion q2{Math::IdentityInit};

/* Avoid unnecessary initialization if is overwritten anyway */
Matrix4 projection{NoInit};
if(orthographic)
    projection = Matrix4::orthographicProjection({4.0f, 3.0f}, 0.1f, 100.0f);
else
    projection = Matrix4::perspectiveProjection(35.0_degf, 1.33f, 0.1f, 100.0f);

Integration with types from the STL and 3rd party APIs

To simplify the workflow when interacting with 3rd party APIs, all Magnum math types can be made explicitly convertible to and from types coming from the STL or external libraries. Currently, various Magnum libraries provide these conversions, see documentation of each header for details: