Comparing squared length to 1 is not sufficient to compare within range
[1 - epsilon, 1 + epsilon], as e.g. Quaternion with dot() = 1 + 1e-7
when converted to matrix has column vectors with dot() = 1 + 1e-6, which
is just above 1 + epsilon. Thus it's needed to compare sqrt(dot()) in
range [1 - epsilon, 1 + epsilon] or dot() in range [1 - 2*epsilon +
epsilon^2, 1 + 2*epsilon + epsilon^2]. Because epsilon^2 is way off
machine precision, it's omitted, thus dot() in all isNormalized()
implementations is now compared this way:
abs(dot() - 1) < 2*epsilon;
DualQuaternion and DualComplex has now only rotation() which returns
full rotation part, rotationAngle() and rotationAxis() on Quaternion and
Complex were renamed to angle() and axis().
As there is no Magnum::TypeTraits struct anymore, there is no need to
have redundant name in it. Hopefully Doxygen will handle the difference
between this and Corrade's TypeTraits.h properly.
It better visualizes the fact that neither (Dual)Complex nor
(Dual)Quaternion contains the matrix inside them, but performs (possibly
costly) conversion.
Square matrices already had that, (dual) quaternions too, making that
the default also with complex numbers. Updated the documentation to
reflect that.
It's now possible to conveniently transform 2D vectors and points with
3x3 matrices and 3D vectors/points with 4x4 matrices. Previous most
low-level solution:
Matrix4 m;
Vector3 v;
Vector3 a = (m*Vector4(v, 1.0f)).xyz();
Vector4 b = (m*Vector4(v, 0.0f)).xyz();
Another, more generalized solution for points was with Point2D/Point3D,
adding a lot of confusion (what is that class and what does .vector()?):
Vector3 a = (m*Point3D(v)).vector();
And the worst solution was with generic 2D/3D code (WTF!):
auto a = (m*typename DimensionTraits::PointType(v)).vector();
Now it is just this, similar for both dimensions:
Vector3 a = m.transformPoint(v);
Vector3 b = m.transformVector(v);
Note that transformation three-component vectors with 3x3 matrices or
four-component vectors with 4x4 matrices is easy enough so it doesn't
need any special convenience functions whatsoever:
Vector3 c = m.rotation()*v;
Overall architecture is simplififed with this change and also it's not
needed to use reinterpret_cast in matrix internals anymore, thus there
is no need for operator() and [][] works now always as expected without
any risk of GCC misoptimizations.
On the other side, constructing matrix from list of elements is not
possible anymore. You have to specify the elements as list of
column vectors, which might be less convenient to write, but it helps to
distinguish what is column and what is row:
Matrix<2, int> a(1, 2, // before
3, 4);
Matrix<2, int> a(Vector<2, int>(1, 2), // now
Vector<2, int>(3, 4));
For some matrix specializations (i.e. Matrix3 and Matrix4) it is
possible to use list-initialization instead of explicit type
specification:
Matrix<3, int>({1, 2, 3},
{4, 5, 6},
{7, 8, 9});
I didn't yet figure out how to properly implement the general
(constexpr) constructor to also take lists, so it's a bit ugly for now.
Matrix operations are now done column-wise, which should help with
future SIMD implementations, documentation is also updated accordingly.
I also removed forgotten remains of matrix/matrix operator*=(), which
can be confusing, as the multiplication is not commutative. Why it is
not present is explained in d9c900f076.