matterbridge/vendor/github.com/Benau/go_rlottie/vector_vmatrix.cpp

685 lines
18 KiB
C++

/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "vector_vmatrix.h"
#include "vector_vglobal.h"
#include <cassert>
#include <cmath>
V_BEGIN_NAMESPACE
/* m11 m21 mtx
* m12 m22 mty
* m13 m23 m33
*/
inline float VMatrix::determinant() const
{
return m11 * (m33 * m22 - mty * m23) - m21 * (m33 * m12 - mty * m13) +
mtx * (m23 * m12 - m22 * m13);
}
bool VMatrix::isAffine() const
{
return type() < MatrixType::Project;
}
bool VMatrix::isIdentity() const
{
return type() == MatrixType::None;
}
bool VMatrix::isInvertible() const
{
return !vIsZero(determinant());
}
bool VMatrix::isScaling() const
{
return type() >= MatrixType::Scale;
}
bool VMatrix::isRotating() const
{
return type() >= MatrixType::Rotate;
}
bool VMatrix::isTranslating() const
{
return type() >= MatrixType::Translate;
}
VMatrix &VMatrix::operator*=(float num)
{
if (num == 1.) return *this;
m11 *= num;
m12 *= num;
m13 *= num;
m21 *= num;
m22 *= num;
m23 *= num;
mtx *= num;
mty *= num;
m33 *= num;
if (dirty < MatrixType::Scale) dirty = MatrixType::Scale;
return *this;
}
VMatrix &VMatrix::operator/=(float div)
{
if (div == 0) return *this;
div = 1 / div;
return operator*=(div);
}
VMatrix::MatrixType VMatrix::type() const
{
if (dirty == MatrixType::None || dirty < mType) return mType;
switch (dirty) {
case MatrixType::Project:
if (!vIsZero(m13) || !vIsZero(m23) || !vIsZero(m33 - 1)) {
mType = MatrixType::Project;
break;
}
VECTOR_FALLTHROUGH
case MatrixType::Shear:
case MatrixType::Rotate:
if (!vIsZero(m12) || !vIsZero(m21)) {
const float dot = m11 * m12 + m21 * m22;
if (vIsZero(dot))
mType = MatrixType::Rotate;
else
mType = MatrixType::Shear;
break;
}
VECTOR_FALLTHROUGH
case MatrixType::Scale:
if (!vIsZero(m11 - 1) || !vIsZero(m22 - 1)) {
mType = MatrixType::Scale;
break;
}
VECTOR_FALLTHROUGH
case MatrixType::Translate:
if (!vIsZero(mtx) || !vIsZero(mty)) {
mType = MatrixType::Translate;
break;
}
VECTOR_FALLTHROUGH
case MatrixType::None:
mType = MatrixType::None;
break;
}
dirty = MatrixType::None;
return mType;
}
VMatrix &VMatrix::translate(float dx, float dy)
{
if (dx == 0 && dy == 0) return *this;
switch (type()) {
case MatrixType::None:
mtx = dx;
mty = dy;
break;
case MatrixType::Translate:
mtx += dx;
mty += dy;
break;
case MatrixType::Scale:
mtx += dx * m11;
mty += dy * m22;
break;
case MatrixType::Project:
m33 += dx * m13 + dy * m23;
VECTOR_FALLTHROUGH
case MatrixType::Shear:
case MatrixType::Rotate:
mtx += dx * m11 + dy * m21;
mty += dy * m22 + dx * m12;
break;
}
if (dirty < MatrixType::Translate) dirty = MatrixType::Translate;
return *this;
}
VMatrix &VMatrix::scale(float sx, float sy)
{
if (sx == 1 && sy == 1) return *this;
switch (type()) {
case MatrixType::None:
case MatrixType::Translate:
m11 = sx;
m22 = sy;
break;
case MatrixType::Project:
m13 *= sx;
m23 *= sy;
VECTOR_FALLTHROUGH
case MatrixType::Rotate:
case MatrixType::Shear:
m12 *= sx;
m21 *= sy;
VECTOR_FALLTHROUGH
case MatrixType::Scale:
m11 *= sx;
m22 *= sy;
break;
}
if (dirty < MatrixType::Scale) dirty = MatrixType::Scale;
return *this;
}
VMatrix &VMatrix::shear(float sh, float sv)
{
if (sh == 0 && sv == 0) return *this;
switch (type()) {
case MatrixType::None:
case MatrixType::Translate:
m12 = sv;
m21 = sh;
break;
case MatrixType::Scale:
m12 = sv * m22;
m21 = sh * m11;
break;
case MatrixType::Project: {
float tm13 = sv * m23;
float tm23 = sh * m13;
m13 += tm13;
m23 += tm23;
VECTOR_FALLTHROUGH
}
case MatrixType::Rotate:
case MatrixType::Shear: {
float tm11 = sv * m21;
float tm22 = sh * m12;
float tm12 = sv * m22;
float tm21 = sh * m11;
m11 += tm11;
m12 += tm12;
m21 += tm21;
m22 += tm22;
break;
}
}
if (dirty < MatrixType::Shear) dirty = MatrixType::Shear;
return *this;
}
static const float deg2rad = float(0.017453292519943295769); // pi/180
static const float inv_dist_to_plane = 1. / 1024.;
VMatrix &VMatrix::rotate(float a, Axis axis)
{
if (a == 0) return *this;
float sina = 0;
float cosa = 0;
if (a == 90. || a == -270.)
sina = 1.;
else if (a == 270. || a == -90.)
sina = -1.;
else if (a == 180.)
cosa = -1.;
else {
float b = deg2rad * a; // convert to radians
sina = std::sin(b); // fast and convenient
cosa = std::cos(b);
}
if (axis == Axis::Z) {
switch (type()) {
case MatrixType::None:
case MatrixType::Translate:
m11 = cosa;
m12 = sina;
m21 = -sina;
m22 = cosa;
break;
case MatrixType::Scale: {
float tm11 = cosa * m11;
float tm12 = sina * m22;
float tm21 = -sina * m11;
float tm22 = cosa * m22;
m11 = tm11;
m12 = tm12;
m21 = tm21;
m22 = tm22;
break;
}
case MatrixType::Project: {
float tm13 = cosa * m13 + sina * m23;
float tm23 = -sina * m13 + cosa * m23;
m13 = tm13;
m23 = tm23;
VECTOR_FALLTHROUGH
}
case MatrixType::Rotate:
case MatrixType::Shear: {
float tm11 = cosa * m11 + sina * m21;
float tm12 = cosa * m12 + sina * m22;
float tm21 = -sina * m11 + cosa * m21;
float tm22 = -sina * m12 + cosa * m22;
m11 = tm11;
m12 = tm12;
m21 = tm21;
m22 = tm22;
break;
}
}
if (dirty < MatrixType::Rotate) dirty = MatrixType::Rotate;
} else {
VMatrix result;
if (axis == Axis::Y) {
result.m11 = cosa;
result.m13 = -sina * inv_dist_to_plane;
} else {
result.m22 = cosa;
result.m23 = -sina * inv_dist_to_plane;
}
result.mType = MatrixType::Project;
*this = result * *this;
}
return *this;
}
VMatrix VMatrix::operator*(const VMatrix &m) const
{
const MatrixType otherType = m.type();
if (otherType == MatrixType::None) return *this;
const MatrixType thisType = type();
if (thisType == MatrixType::None) return m;
VMatrix t;
MatrixType type = vMax(thisType, otherType);
switch (type) {
case MatrixType::None:
break;
case MatrixType::Translate:
t.mtx = mtx + m.mtx;
t.mty += mty + m.mty;
break;
case MatrixType::Scale: {
float m11v = m11 * m.m11;
float m22v = m22 * m.m22;
float m31v = mtx * m.m11 + m.mtx;
float m32v = mty * m.m22 + m.mty;
t.m11 = m11v;
t.m22 = m22v;
t.mtx = m31v;
t.mty = m32v;
break;
}
case MatrixType::Rotate:
case MatrixType::Shear: {
float m11v = m11 * m.m11 + m12 * m.m21;
float m12v = m11 * m.m12 + m12 * m.m22;
float m21v = m21 * m.m11 + m22 * m.m21;
float m22v = m21 * m.m12 + m22 * m.m22;
float m31v = mtx * m.m11 + mty * m.m21 + m.mtx;
float m32v = mtx * m.m12 + mty * m.m22 + m.mty;
t.m11 = m11v;
t.m12 = m12v;
t.m21 = m21v;
t.m22 = m22v;
t.mtx = m31v;
t.mty = m32v;
break;
}
case MatrixType::Project: {
float m11v = m11 * m.m11 + m12 * m.m21 + m13 * m.mtx;
float m12v = m11 * m.m12 + m12 * m.m22 + m13 * m.mty;
float m13v = m11 * m.m13 + m12 * m.m23 + m13 * m.m33;
float m21v = m21 * m.m11 + m22 * m.m21 + m23 * m.mtx;
float m22v = m21 * m.m12 + m22 * m.m22 + m23 * m.mty;
float m23v = m21 * m.m13 + m22 * m.m23 + m23 * m.m33;
float m31v = mtx * m.m11 + mty * m.m21 + m33 * m.mtx;
float m32v = mtx * m.m12 + mty * m.m22 + m33 * m.mty;
float m33v = mtx * m.m13 + mty * m.m23 + m33 * m.m33;
t.m11 = m11v;
t.m12 = m12v;
t.m13 = m13v;
t.m21 = m21v;
t.m22 = m22v;
t.m23 = m23v;
t.mtx = m31v;
t.mty = m32v;
t.m33 = m33v;
}
}
t.dirty = type;
t.mType = type;
return t;
}
VMatrix &VMatrix::operator*=(const VMatrix &o)
{
const MatrixType otherType = o.type();
if (otherType == MatrixType::None) return *this;
const MatrixType thisType = type();
if (thisType == MatrixType::None) return operator=(o);
MatrixType t = vMax(thisType, otherType);
switch (t) {
case MatrixType::None:
break;
case MatrixType::Translate:
mtx += o.mtx;
mty += o.mty;
break;
case MatrixType::Scale: {
float m11v = m11 * o.m11;
float m22v = m22 * o.m22;
float m31v = mtx * o.m11 + o.mtx;
float m32v = mty * o.m22 + o.mty;
m11 = m11v;
m22 = m22v;
mtx = m31v;
mty = m32v;
break;
}
case MatrixType::Rotate:
case MatrixType::Shear: {
float m11v = m11 * o.m11 + m12 * o.m21;
float m12v = m11 * o.m12 + m12 * o.m22;
float m21v = m21 * o.m11 + m22 * o.m21;
float m22v = m21 * o.m12 + m22 * o.m22;
float m31v = mtx * o.m11 + mty * o.m21 + o.mtx;
float m32v = mtx * o.m12 + mty * o.m22 + o.mty;
m11 = m11v;
m12 = m12v;
m21 = m21v;
m22 = m22v;
mtx = m31v;
mty = m32v;
break;
}
case MatrixType::Project: {
float m11v = m11 * o.m11 + m12 * o.m21 + m13 * o.mtx;
float m12v = m11 * o.m12 + m12 * o.m22 + m13 * o.mty;
float m13v = m11 * o.m13 + m12 * o.m23 + m13 * o.m33;
float m21v = m21 * o.m11 + m22 * o.m21 + m23 * o.mtx;
float m22v = m21 * o.m12 + m22 * o.m22 + m23 * o.mty;
float m23v = m21 * o.m13 + m22 * o.m23 + m23 * o.m33;
float m31v = mtx * o.m11 + mty * o.m21 + m33 * o.mtx;
float m32v = mtx * o.m12 + mty * o.m22 + m33 * o.mty;
float m33v = mtx * o.m13 + mty * o.m23 + m33 * o.m33;
m11 = m11v;
m12 = m12v;
m13 = m13v;
m21 = m21v;
m22 = m22v;
m23 = m23v;
mtx = m31v;
mty = m32v;
m33 = m33v;
}
}
dirty = t;
mType = t;
return *this;
}
VMatrix VMatrix::adjoint() const
{
float h11, h12, h13, h21, h22, h23, h31, h32, h33;
h11 = m22 * m33 - m23 * mty;
h21 = m23 * mtx - m21 * m33;
h31 = m21 * mty - m22 * mtx;
h12 = m13 * mty - m12 * m33;
h22 = m11 * m33 - m13 * mtx;
h32 = m12 * mtx - m11 * mty;
h13 = m12 * m23 - m13 * m22;
h23 = m13 * m21 - m11 * m23;
h33 = m11 * m22 - m12 * m21;
VMatrix res;
res.m11 = h11;
res.m12 = h12;
res.m13 = h13;
res.m21 = h21;
res.m22 = h22;
res.m23 = h23;
res.mtx = h31;
res.mty = h32;
res.m33 = h33;
res.mType = MatrixType::None;
res.dirty = MatrixType::Project;
return res;
}
VMatrix VMatrix::inverted(bool *invertible) const
{
VMatrix invert;
bool inv = true;
switch (type()) {
case MatrixType::None:
break;
case MatrixType::Translate:
invert.mtx = -mtx;
invert.mty = -mty;
break;
case MatrixType::Scale:
inv = !vIsZero(m11);
inv &= !vIsZero(m22);
if (inv) {
invert.m11 = 1.0f / m11;
invert.m22 = 1.0f / m22;
invert.mtx = -mtx * invert.m11;
invert.mty = -mty * invert.m22;
}
break;
default:
// general case
float det = determinant();
inv = !vIsZero(det);
if (inv) invert = (adjoint() /= det);
// TODO Test above line
break;
}
if (invertible) *invertible = inv;
if (inv) {
// inverting doesn't change the type
invert.mType = mType;
invert.dirty = dirty;
}
return invert;
}
bool VMatrix::operator==(const VMatrix &o) const
{
return fuzzyCompare(o);
}
bool VMatrix::operator!=(const VMatrix &o) const
{
return !operator==(o);
}
bool VMatrix::fuzzyCompare(const VMatrix &o) const
{
return vCompare(m11, o.m11) && vCompare(m12, o.m12) &&
vCompare(m21, o.m21) && vCompare(m22, o.m22) &&
vCompare(mtx, o.mtx) && vCompare(mty, o.mty);
}
#define V_NEAR_CLIP 0.000001f
#ifdef MAP
#undef MAP
#endif
#define MAP(x, y, nx, ny) \
do { \
float FX_ = x; \
float FY_ = y; \
switch (t) { \
case MatrixType::None: \
nx = FX_; \
ny = FY_; \
break; \
case MatrixType::Translate: \
nx = FX_ + mtx; \
ny = FY_ + mty; \
break; \
case MatrixType::Scale: \
nx = m11 * FX_ + mtx; \
ny = m22 * FY_ + mty; \
break; \
case MatrixType::Rotate: \
case MatrixType::Shear: \
case MatrixType::Project: \
nx = m11 * FX_ + m21 * FY_ + mtx; \
ny = m12 * FX_ + m22 * FY_ + mty; \
if (t == MatrixType::Project) { \
float w = (m13 * FX_ + m23 * FY_ + m33); \
if (w < V_NEAR_CLIP) w = V_NEAR_CLIP; \
w = 1. / w; \
nx *= w; \
ny *= w; \
} \
} \
} while (0)
VRect VMatrix::map(const VRect &rect) const
{
VMatrix::MatrixType t = type();
if (t <= MatrixType::Translate)
return rect.translated(std::lround(mtx), std::lround(mty));
if (t <= MatrixType::Scale) {
int x = std::lround(m11 * rect.x() + mtx);
int y = std::lround(m22 * rect.y() + mty);
int w = std::lround(m11 * rect.width());
int h = std::lround(m22 * rect.height());
if (w < 0) {
w = -w;
x -= w;
}
if (h < 0) {
h = -h;
y -= h;
}
return {x, y, w, h};
} else if (t < MatrixType::Project) {
// see mapToPolygon for explanations of the algorithm.
float x = 0, y = 0;
MAP(rect.left(), rect.top(), x, y);
float xmin = x;
float ymin = y;
float xmax = x;
float ymax = y;
MAP(rect.right() + 1, rect.top(), x, y);
xmin = vMin(xmin, x);
ymin = vMin(ymin, y);
xmax = vMax(xmax, x);
ymax = vMax(ymax, y);
MAP(rect.right() + 1, rect.bottom() + 1, x, y);
xmin = vMin(xmin, x);
ymin = vMin(ymin, y);
xmax = vMax(xmax, x);
ymax = vMax(ymax, y);
MAP(rect.left(), rect.bottom() + 1, x, y);
xmin = vMin(xmin, x);
ymin = vMin(ymin, y);
xmax = vMax(xmax, x);
ymax = vMax(ymax, y);
return VRect(std::lround(xmin), std::lround(ymin),
std::lround(xmax) - std::lround(xmin),
std::lround(ymax) - std::lround(ymin));
} else {
// Not supported
assert(0);
return {};
}
}
VPointF VMatrix::map(const VPointF &p) const
{
float fx = p.x();
float fy = p.y();
float x = 0, y = 0;
VMatrix::MatrixType t = type();
switch (t) {
case MatrixType::None:
x = fx;
y = fy;
break;
case MatrixType::Translate:
x = fx + mtx;
y = fy + mty;
break;
case MatrixType::Scale:
x = m11 * fx + mtx;
y = m22 * fy + mty;
break;
case MatrixType::Rotate:
case MatrixType::Shear:
case MatrixType::Project:
x = m11 * fx + m21 * fy + mtx;
y = m12 * fx + m22 * fy + mty;
if (t == MatrixType::Project) {
float w = 1.0f / (m13 * fx + m23 * fy + m33);
x *= w;
y *= w;
}
}
return {x, y};
}
V_END_NAMESPACE