mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-02 00:17:02 +00:00
495 lines
13 KiB
C++
495 lines
13 KiB
C++
#ifndef Vectors_hpp
|
|
#define Vectors_hpp
|
|
|
|
#include "Lottie/Private/Parsing/JsonParsing.hpp"
|
|
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
|
|
namespace lottie {
|
|
|
|
struct Vector1D {
|
|
enum class InternalRepresentationType {
|
|
SingleNumber,
|
|
Array
|
|
};
|
|
|
|
explicit Vector1D(double value_) :
|
|
value(value_) {
|
|
}
|
|
|
|
explicit Vector1D(json11::Json const &json) noexcept(false) {
|
|
if (json.is_number()) {
|
|
value = json.number_value();
|
|
} else if (json.is_array()) {
|
|
if (json.array_items().empty()) {
|
|
throw LottieParsingException();
|
|
}
|
|
if (!json.array_items()[0].is_number()) {
|
|
throw LottieParsingException();
|
|
}
|
|
value = json.array_items()[0].number_value();
|
|
} else {
|
|
throw LottieParsingException();
|
|
}
|
|
}
|
|
|
|
json11::Json toJson() const {
|
|
return json11::Json(value);
|
|
}
|
|
|
|
double value;
|
|
|
|
double distanceTo(Vector1D const &to) const {
|
|
return abs(to.value - value);
|
|
}
|
|
};
|
|
|
|
double interpolate(double value, double to, double amount);
|
|
|
|
Vector1D interpolate(
|
|
Vector1D const &from,
|
|
Vector1D const &to,
|
|
double amount
|
|
);
|
|
|
|
struct Vector2D {
|
|
static Vector2D Zero() {
|
|
return Vector2D(0.0, 0.0);
|
|
}
|
|
|
|
Vector2D() :
|
|
x(0.0),
|
|
y(0.0) {
|
|
}
|
|
|
|
explicit Vector2D(double x_, double y_) :
|
|
x(x_),
|
|
y(y_) {
|
|
}
|
|
|
|
explicit Vector2D(json11::Json const &json) noexcept(false) {
|
|
x = 0.0;
|
|
y = 0.0;
|
|
|
|
if (json.is_array()) {
|
|
int index = 0;
|
|
|
|
if (json.array_items().size() > index) {
|
|
if (!json.array_items()[index].is_number()) {
|
|
throw LottieParsingException();
|
|
}
|
|
x = json.array_items()[index].number_value();
|
|
index++;
|
|
}
|
|
|
|
if (json.array_items().size() > index) {
|
|
if (!json.array_items()[index].is_number()) {
|
|
throw LottieParsingException();
|
|
}
|
|
y = json.array_items()[index].number_value();
|
|
index++;
|
|
}
|
|
} else if (json.is_object()) {
|
|
auto xAny = getAny(json.object_items(), "x");
|
|
if (xAny.is_number()) {
|
|
x = xAny.number_value();
|
|
} else if (xAny.is_array()) {
|
|
if (xAny.array_items().empty()) {
|
|
throw LottieParsingException();
|
|
}
|
|
if (!xAny.array_items()[0].is_number()) {
|
|
throw LottieParsingException();
|
|
}
|
|
x = xAny.array_items()[0].number_value();
|
|
}
|
|
|
|
auto yAny = getAny(json.object_items(), "y");
|
|
if (yAny.is_number()) {
|
|
y = yAny.number_value();
|
|
} else if (yAny.is_array()) {
|
|
if (yAny.array_items().empty()) {
|
|
throw LottieParsingException();
|
|
}
|
|
if (!yAny.array_items()[0].is_number()) {
|
|
throw LottieParsingException();
|
|
}
|
|
y = yAny.array_items()[0].number_value();
|
|
}
|
|
} else {
|
|
throw LottieParsingException();
|
|
}
|
|
}
|
|
|
|
json11::Json toJson() const {
|
|
json11::Json::object result;
|
|
|
|
result.insert(std::make_pair("x", x));
|
|
result.insert(std::make_pair("y", y));
|
|
|
|
return json11::Json(result);
|
|
}
|
|
|
|
double x;
|
|
double y;
|
|
|
|
Vector2D operator+(Vector2D const &rhs) const {
|
|
return Vector2D(x + rhs.x, y + rhs.y);
|
|
}
|
|
|
|
Vector2D operator-(Vector2D const &rhs) const {
|
|
return Vector2D(x - rhs.x, y - rhs.y);
|
|
}
|
|
|
|
Vector2D operator*(double scalar) const {
|
|
return Vector2D(x * scalar, y * scalar);
|
|
}
|
|
|
|
bool operator==(Vector2D const &rhs) const {
|
|
return x == rhs.x && y == rhs.y;
|
|
}
|
|
|
|
bool operator!=(Vector2D const &rhs) const {
|
|
return !(*this == rhs);
|
|
}
|
|
|
|
bool isZero() const {
|
|
return x == 0.0 && y == 0.0;
|
|
}
|
|
|
|
double distanceTo(Vector2D const &to) const {
|
|
auto deltaX = to.x - x;
|
|
auto deltaY = to.y - y;
|
|
return sqrt(deltaX * deltaX + deltaY * deltaY);
|
|
}
|
|
|
|
bool colinear(Vector2D const &a, Vector2D const &b) const {
|
|
double area = x * (a.y - b.y) + a.x * (b.y - y) + b.x * (y - a.y);
|
|
double accuracy = 0.05;
|
|
if (area < accuracy && area > -accuracy) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Vector2D pointOnPath(Vector2D const &to, Vector2D const &outTangent, Vector2D const &inTangent, double amount) const;
|
|
|
|
Vector2D interpolate(Vector2D const &to, double amount) const;
|
|
|
|
Vector2D interpolate(
|
|
Vector2D const &to,
|
|
Vector2D const &outTangent,
|
|
Vector2D const &inTangent,
|
|
double amount,
|
|
int maxIterations = 3,
|
|
int samples = 20,
|
|
double accuracy = 1.0
|
|
) const;
|
|
};
|
|
|
|
Vector2D interpolate(
|
|
Vector2D const &from,
|
|
Vector2D const &to,
|
|
double amount
|
|
);
|
|
|
|
struct Vector3D {
|
|
explicit Vector3D(double x_, double y_, double z_) :
|
|
x(x_),
|
|
y(y_),
|
|
z(z_) {
|
|
}
|
|
|
|
explicit Vector3D(json11::Json const &json) noexcept(false) {
|
|
if (!json.is_array()) {
|
|
throw LottieParsingException();
|
|
}
|
|
|
|
int index = 0;
|
|
|
|
x = 0.0;
|
|
y = 0.0;
|
|
z = 0.0;
|
|
|
|
if (json.array_items().size() > index) {
|
|
if (!json.array_items()[index].is_number()) {
|
|
throw LottieParsingException();
|
|
}
|
|
x = json.array_items()[index].number_value();
|
|
index++;
|
|
}
|
|
|
|
if (json.array_items().size() > index) {
|
|
if (!json.array_items()[index].is_number()) {
|
|
throw LottieParsingException();
|
|
}
|
|
y = json.array_items()[index].number_value();
|
|
index++;
|
|
}
|
|
|
|
if (json.array_items().size() > index) {
|
|
if (!json.array_items()[index].is_number()) {
|
|
throw LottieParsingException();
|
|
}
|
|
z = json.array_items()[index].number_value();
|
|
index++;
|
|
}
|
|
}
|
|
|
|
json11::Json toJson() const {
|
|
json11::Json::array result;
|
|
|
|
result.push_back(json11::Json(x));
|
|
result.push_back(json11::Json(y));
|
|
result.push_back(json11::Json(z));
|
|
|
|
return json11::Json(result);
|
|
}
|
|
|
|
double x = 0.0;
|
|
double y = 0.0;
|
|
double z = 0.0;
|
|
};
|
|
|
|
Vector3D interpolate(
|
|
Vector3D const &from,
|
|
Vector3D const &to,
|
|
double amount
|
|
);
|
|
|
|
inline double degreesToRadians(double value) {
|
|
return value * M_PI / 180.0;
|
|
}
|
|
|
|
inline double radiansToDegrees(double value) {
|
|
return value * 180.0 / M_PI;
|
|
}
|
|
|
|
struct CATransform3D {
|
|
double m11, m12, m13, m14;
|
|
double m21, m22, m23, m24;
|
|
double m31, m32, m33, m34;
|
|
double m41, m42, m43, m44;
|
|
|
|
CATransform3D(
|
|
double m11_, double m12_, double m13_, double m14_,
|
|
double m21_, double m22_, double m23_, double m24_,
|
|
double m31_, double m32_, double m33_, double m34_,
|
|
double m41_, double m42_, double m43_, double m44_
|
|
) :
|
|
m11(m11_), m12(m12_), m13(m13_), m14(m14_),
|
|
m21(m21_), m22(m22_), m23(m23_), m24(m24_),
|
|
m31(m31_), m32(m32_), m33(m33_), m34(m34_),
|
|
m41(m41_), m42(m42_), m43(m43_), m44(m44_) {
|
|
}
|
|
|
|
bool operator==(CATransform3D const &rhs) const {
|
|
return m11 == rhs.m11 && m12 == rhs.m12 && m13 == rhs.m13 && m14 == rhs.m14 &&
|
|
m21 == rhs.m21 && m22 == rhs.m22 && m23 == rhs.m23 && m24 == rhs.m24 &&
|
|
m31 == rhs.m31 && m32 == rhs.m32 && m33 == rhs.m33 && m34 == rhs.m34 &&
|
|
m41 == rhs.m41 && m42 == rhs.m42 && m43 == rhs.m43 && m44 == rhs.m44;
|
|
}
|
|
|
|
bool operator!=(CATransform3D const &rhs) const {
|
|
return !(*this == rhs);
|
|
}
|
|
|
|
inline bool isIdentity() const {
|
|
return m11 == 1.0 && m12 == 0.0 && m13 == 0.0 && m14 == 0.0 &&
|
|
m21 == 0.0 && m22 == 1.0 && m23 == 0.0 && m24 == 0.0 &&
|
|
m31 == 0.0 && m32 == 0.0 && m33 == 1.0 && m34 == 0.0 &&
|
|
m41 == 0.0 && m42 == 0.0 && m43 == 0.0 && m44 == 1.0;
|
|
}
|
|
|
|
static CATransform3D makeTranslation(double tx, double ty, double tz) {
|
|
return CATransform3D(
|
|
1, 0, 0, 0,
|
|
0, 1, 0, 0,
|
|
0, 0, 1, 0,
|
|
tx, ty, tz, 1
|
|
);
|
|
}
|
|
|
|
static CATransform3D makeScale(double sx, double sy, double sz) {
|
|
return CATransform3D(
|
|
sx, 0, 0, 0,
|
|
0, sy, 0, 0,
|
|
0, 0, sz, 0,
|
|
0, 0, 0, 1
|
|
);
|
|
}
|
|
|
|
static CATransform3D makeRotation(double radians, double x, double y, double z);
|
|
|
|
static CATransform3D makeSkew(double skew, double skewAxis) {
|
|
double mCos = cos(degreesToRadians(skewAxis));
|
|
double mSin = sin(degreesToRadians(skewAxis));
|
|
double aTan = tan(degreesToRadians(skew));
|
|
|
|
CATransform3D transform1(
|
|
mCos,
|
|
mSin,
|
|
0.0,
|
|
0.0,
|
|
-mSin,
|
|
mCos,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
1.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
1.0
|
|
);
|
|
|
|
CATransform3D transform2(
|
|
1.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
aTan,
|
|
1.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
1.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
1.0
|
|
);
|
|
|
|
CATransform3D transform3(
|
|
mCos,
|
|
-mSin,
|
|
0.0,
|
|
0.0,
|
|
mSin,
|
|
mCos,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
1.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
1.0
|
|
);
|
|
|
|
return transform3 * transform2 * transform1;
|
|
}
|
|
|
|
static CATransform3D makeTransform(
|
|
Vector2D const &anchor,
|
|
Vector2D const &position,
|
|
Vector2D const &scale,
|
|
double rotation,
|
|
std::optional<double> skew,
|
|
std::optional<double> skewAxis
|
|
) {
|
|
CATransform3D result = CATransform3D::identity();
|
|
if (skew.has_value() && skewAxis.has_value()) {
|
|
result = CATransform3D::identity().translated(position).rotated(rotation).skewed(-skew.value(), skewAxis.value()).scaled(Vector2D(scale.x * 0.01, scale.y * 0.01)).translated(Vector2D(-anchor.x, -anchor.y));
|
|
} else {
|
|
result = CATransform3D::identity().translated(position).rotated(rotation).scaled(Vector2D(scale.x * 0.01, scale.y * 0.01)).translated(Vector2D(-anchor.x, -anchor.y));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
CATransform3D rotated(double degrees) const;
|
|
|
|
CATransform3D translated(Vector2D const &translation) const;
|
|
|
|
CATransform3D scaled(Vector2D const &scale) const;
|
|
|
|
CATransform3D skewed(double skew, double skewAxis) const {
|
|
return CATransform3D::makeSkew(skew, skewAxis) * (*this);
|
|
}
|
|
|
|
static CATransform3D const &identity() {
|
|
return _identity;
|
|
}
|
|
|
|
CATransform3D operator*(CATransform3D const &b) const;
|
|
|
|
bool isInvertible() const;
|
|
|
|
CATransform3D inverted() const;
|
|
|
|
private:
|
|
static CATransform3D _identity;
|
|
};
|
|
|
|
struct CGRect {
|
|
explicit CGRect(double x_, double y_, double width_, double height_) :
|
|
x(x_), y(y_), width(width_), height(height_) {
|
|
}
|
|
|
|
double x = 0.0;
|
|
double y = 0.0;
|
|
double width = 0.0;
|
|
double height = 0.0;
|
|
|
|
static CGRect veryLarge() {
|
|
return CGRect(
|
|
-100000000.0,
|
|
-100000000.0,
|
|
200000000.0,
|
|
200000000.0
|
|
);
|
|
}
|
|
|
|
bool operator==(CGRect const &rhs) const {
|
|
return x == rhs.x && y == rhs.y && width == rhs.width && height == rhs.height;
|
|
}
|
|
|
|
bool operator!=(CGRect const &rhs) const {
|
|
return !(*this == rhs);
|
|
}
|
|
|
|
bool empty() const {
|
|
return width <= 0.0 || height <= 0.0;
|
|
}
|
|
|
|
CGRect insetBy(double dx, double dy) const {
|
|
CGRect result = *this;
|
|
|
|
result.x += dx;
|
|
result.y += dy;
|
|
result.width -= dx * 2.0;
|
|
result.height -= dy * 2.0;
|
|
|
|
return result;
|
|
}
|
|
|
|
bool intersects(CGRect const &other) const;
|
|
bool contains(CGRect const &other) const;
|
|
|
|
CGRect intersection(CGRect const &other) const;
|
|
CGRect unionWith(CGRect const &other) const;
|
|
|
|
CGRect applyingTransform(CATransform3D const &transform) const;
|
|
};
|
|
|
|
inline bool isInRangeOrEqual(double value, double from, double to) {
|
|
return from <= value && value <= to;
|
|
}
|
|
|
|
inline bool isInRange(double value, double from, double to) {
|
|
return from < value && value < to;
|
|
}
|
|
|
|
double cubicBezierInterpolate(double value, Vector2D const &P0, Vector2D const &P1, Vector2D const &P2, Vector2D const &P3);
|
|
|
|
}
|
|
|
|
#endif /* Vectors_hpp */
|