blob: 9e45c791f4b42b0509bd2fc3149e274d36e67fd1 [file] [log] [blame]
/*
* Copyright 2006 The Android Open Source Project
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* Initial import from skia:src/core/SkMatrix.cpp
*
* Copyright 2022 Rive
*/
#include "rive/math/mat2d.hpp"
#include "rive/math/math_types.hpp"
#include <cmath>
static inline float sdot(float a, float b, float c, float d) { return a * b + c * d; }
namespace rive
{
float Mat2D::findMaxScale() const
{
if (xy() == 0 && yx() == 0)
{
return std::max(fabsf(xx()), fabsf(yy()));
}
// ignore the translation part of the matrix, just look at 2x2 portion.
// compute singular values, take largest or smallest abs value.
// [a b; b c] = A^T*A
float a = sdot(xx(), xx(), xy(), xy());
float b = sdot(xx(), yx(), yy(), xy());
float c = sdot(yx(), yx(), yy(), yy());
// eigenvalues of A^T*A are the squared singular values of A.
// characteristic equation is det((A^T*A) - l*I) = 0
// l^2 - (a + c)l + (ac-b^2)
// solve using quadratic equation (divisor is non-zero since l^2 has 1 coeff
// and roots are guaranteed to be pos and real).
float bSqd = b * b;
// if upper left 2x2 is orthogonal save some math
float result;
if (bSqd <= math::EPSILON * math::EPSILON)
{
result = std::max(a, c);
}
else
{
float aminusc = a - c;
float apluscdiv2 = (a + c) * .5f;
float x = sqrtf(aminusc * aminusc + 4 * bSqd) * .5f;
result = apluscdiv2 + x;
}
if (!std::isfinite(result))
{
result = 0;
}
// Due to the floating point inaccuracy, there might be an error in a, b, c
// calculated by sdot, further deepened by subsequent arithmetic operations
// on them. Therefore, we allow and cap the nearly-zero negative values.
result = std::max(result, 0.f);
result = sqrtf(result);
return result;
}
} // namespace rive