Refactoring

This commit is contained in:
Isaac 2024-05-19 12:27:24 +04:00
parent 62d58b3bdd
commit 1f1509a64c
25 changed files with 522 additions and 349 deletions

View File

@ -81,7 +81,7 @@ public:
virtual void setAlpha(float alpha) = 0;
virtual void concatenate(lottie::Transform3D const &transform) = 0;
virtual void concatenate(lottie::Transform2D const &transform) = 0;
virtual void draw(std::shared_ptr<Canvas> const &other, lottie::CGRect const &rect) = 0;
};

View File

@ -40,7 +40,7 @@ public:
virtual void fill(lottie::CGRect const &rect, lottie::Color const &fillColor) override;
virtual void setBlendMode(BlendMode blendMode) override;
virtual void setAlpha(float alpha) override;
virtual void concatenate(lottie::Transform3D const &transform) override;
virtual void concatenate(lottie::Transform2D const &transform) override;
virtual std::shared_ptr<Image> makeImage() const;
virtual void draw(std::shared_ptr<Canvas> const &other, lottie::CGRect const &rect) override;

View File

@ -509,7 +509,7 @@ void CanvasImpl::setAlpha(float alpha) {
CGContextSetAlpha(_context, alpha);
}
void CanvasImpl::concatenate(lottie::Transform3D const &transform) {
void CanvasImpl::concatenate(lottie::Transform2D const &transform) {
CGContextConcatCTM(_context, CATransform3DGetAffineTransform(nativeTransform(transform)));
}

View File

@ -30,7 +30,7 @@ public:
virtual void setAlpha(float alpha) override;
virtual void concatenate(lottie::Transform3D const &transform) override;
virtual void concatenate(lottie::Transform2D const &transform) override;
virtual void draw(std::shared_ptr<Canvas> const &other, lottie::CGRect const &rect) override;
@ -39,7 +39,7 @@ public:
private:
float _width = 0.0f;
float _height = 0.0f;
lottie::Transform3D _transform;
lottie::Transform2D _transform;
};
}

View File

@ -12,7 +12,7 @@ void addEnumeratedPath(CanvasPathEnumerator const &enumeratePath) {
}
NullCanvasImpl::NullCanvasImpl(int width, int height) :
_width(width), _height(height), _transform(lottie::Transform3D::identity()) {
_width(width), _height(height), _transform(lottie::Transform2D::identity()) {
}
NullCanvasImpl::~NullCanvasImpl() {
@ -69,7 +69,7 @@ void NullCanvasImpl::setBlendMode(BlendMode blendMode) {
void NullCanvasImpl::setAlpha(float alpha) {
}
void NullCanvasImpl::concatenate(lottie::Transform3D const &transform) {
void NullCanvasImpl::concatenate(lottie::Transform2D const &transform) {
}
void NullCanvasImpl::draw(std::shared_ptr<Canvas> const &other, lottie::CGRect const &rect) {

View File

@ -15,17 +15,17 @@ static constexpr float minGlobalRectCalculationSize = 200.0f;
struct TransformedPath {
lottie::BezierPath path;
lottie::Transform3D transform;
lottie::Transform2D transform;
TransformedPath(lottie::BezierPath const &path_, lottie::Transform3D const &transform_) :
TransformedPath(lottie::BezierPath const &path_, lottie::Transform2D const &transform_) :
path(path_),
transform(transform_) {
}
};
static lottie::CGRect collectPathBoundingBoxes(std::shared_ptr<lottie::RenderTreeNodeContentItem> item, size_t subItemLimit, lottie::Transform3D const &parentTransform, bool skipApplyTransform, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) {
static lottie::CGRect collectPathBoundingBoxes(std::shared_ptr<lottie::RenderTreeNodeContentItem> item, size_t subItemLimit, lottie::Transform2D const &parentTransform, bool skipApplyTransform, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) {
//TODO:remove skipApplyTransform
lottie::Transform3D effectiveTransform = parentTransform;
lottie::Transform2D effectiveTransform = parentTransform;
if (!skipApplyTransform && item->isGroup) {
effectiveTransform = item->transform * effectiveTransform;
}
@ -56,9 +56,9 @@ static lottie::CGRect collectPathBoundingBoxes(std::shared_ptr<lottie::RenderTre
return boundingBox;
}
static void enumeratePaths(std::shared_ptr<lottie::RenderTreeNodeContentItem> item, size_t subItemLimit, lottie::Transform3D const &parentTransform, bool skipApplyTransform, std::function<void(lottie::BezierPath const &path, lottie::Transform3D const &transform)> const &onPath) {
static void enumeratePaths(std::shared_ptr<lottie::RenderTreeNodeContentItem> item, size_t subItemLimit, lottie::Transform2D const &parentTransform, bool skipApplyTransform, std::function<void(lottie::BezierPath const &path, lottie::Transform2D const &transform)> const &onPath) {
//TODO:remove skipApplyTransform
lottie::Transform3D effectiveTransform = parentTransform;
lottie::Transform2D effectiveTransform = parentTransform;
if (!skipApplyTransform && item->isGroup) {
effectiveTransform = item->transform * effectiveTransform;
}
@ -80,14 +80,14 @@ static void enumeratePaths(std::shared_ptr<lottie::RenderTreeNodeContentItem> it
namespace lottie {
static std::optional<CGRect> getRenderContentItemGlobalRect(std::shared_ptr<RenderTreeNodeContentItem> const &contentItem, lottie::Vector2D const &globalSize, lottie::Transform3D const &parentTransform, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) {
static std::optional<CGRect> getRenderContentItemGlobalRect(std::shared_ptr<RenderTreeNodeContentItem> const &contentItem, lottie::Vector2D const &globalSize, lottie::Transform2D const &parentTransform, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) {
auto currentTransform = parentTransform;
Transform3D localTransform = contentItem->transform;
Transform2D localTransform = contentItem->transform;
currentTransform = localTransform * currentTransform;
std::optional<CGRect> globalRect;
for (const auto &shadingVariant : contentItem->shadings) {
lottie::CGRect shapeBounds = collectPathBoundingBoxes(contentItem, shadingVariant->subItemLimit, lottie::Transform3D::identity(), true, bezierPathsBoundingBoxContext);
lottie::CGRect shapeBounds = collectPathBoundingBoxes(contentItem, shadingVariant->subItemLimit, lottie::Transform2D::identity(), true, bezierPathsBoundingBoxContext);
if (shadingVariant->stroke) {
shapeBounds = shapeBounds.insetBy(-shadingVariant->stroke->lineWidth / 2.0, -shadingVariant->stroke->lineWidth / 2.0);
@ -128,13 +128,13 @@ static std::optional<CGRect> getRenderContentItemGlobalRect(std::shared_ptr<Rend
}
}
static std::optional<CGRect> getRenderNodeGlobalRect(std::shared_ptr<RenderTreeNode> const &node, lottie::Vector2D const &globalSize, lottie::Transform3D const &parentTransform, bool isInvertedMatte, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) {
static std::optional<CGRect> getRenderNodeGlobalRect(std::shared_ptr<RenderTreeNode> const &node, lottie::Vector2D const &globalSize, lottie::Transform2D const &parentTransform, bool isInvertedMatte, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) {
if (node->isHidden() || node->alpha() < minVisibleAlpha) {
return std::nullopt;
}
auto currentTransform = parentTransform;
Transform3D localTransform = node->transform();
Transform2D localTransform = node->transform();
currentTransform = localTransform * currentTransform;
std::optional<CGRect> globalRect;
@ -179,9 +179,9 @@ static std::optional<CGRect> getRenderNodeGlobalRect(std::shared_ptr<RenderTreeN
namespace {
static void drawLottieContentItem(std::shared_ptr<lottieRendering::Canvas> const &parentContext, std::shared_ptr<lottie::RenderTreeNodeContentItem> item, float parentAlpha, lottie::Vector2D const &globalSize, lottie::Transform3D const &parentTransform, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) {
static void drawLottieContentItem(std::shared_ptr<lottieRendering::Canvas> const &parentContext, std::shared_ptr<lottie::RenderTreeNodeContentItem> item, float parentAlpha, lottie::Vector2D const &globalSize, lottie::Transform2D const &parentTransform, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) {
auto currentTransform = parentTransform;
lottie::Transform3D localTransform = item->transform;
lottie::Transform2D localTransform = item->transform;
currentTransform = localTransform * currentTransform;
float normalizedOpacity = item->alpha;
@ -215,7 +215,7 @@ static void drawLottieContentItem(std::shared_ptr<lottieRendering::Canvas> const
tempContext = tempContextValue;
currentContext = &tempContext;
(*currentContext)->concatenate(lottie::Transform3D::identity().translated(lottie::Vector2D(-globalRect->x, -globalRect->y)));
(*currentContext)->concatenate(lottie::Transform2D::identity().translated(lottie::Vector2D(-globalRect->x, -globalRect->y)));
(*currentContext)->saveState();
(*currentContext)->concatenate(currentTransform);
@ -268,7 +268,7 @@ static void drawLottieContentItem(std::shared_ptr<lottieRendering::Canvas> const
};
} else {
iteratePaths = [&](std::function<void(lottieRendering::PathCommand const &)> iterate) {
enumeratePaths(item, shading->subItemLimit, lottie::Transform3D::identity(), true, [&](lottie::BezierPath const &sourcePath, lottie::Transform3D const &transform) {
enumeratePaths(item, shading->subItemLimit, lottie::Transform2D::identity(), true, [&](lottie::BezierPath const &sourcePath, lottie::Transform2D const &transform) {
auto path = sourcePath.copyUsingTransform(transform);
lottieRendering::PathCommand pathCommand;
@ -456,7 +456,7 @@ static void drawLottieContentItem(std::shared_ptr<lottieRendering::Canvas> const
parentContext->restoreState();
}
static void renderLottieRenderNode(std::shared_ptr<lottie::RenderTreeNode> node, std::shared_ptr<lottieRendering::Canvas> const &parentContext, lottie::Vector2D const &globalSize, lottie::Transform3D const &parentTransform, float parentAlpha, bool isInvertedMatte, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) {
static void renderLottieRenderNode(std::shared_ptr<lottie::RenderTreeNode> node, std::shared_ptr<lottieRendering::Canvas> const &parentContext, lottie::Vector2D const &globalSize, lottie::Transform2D const &parentTransform, float parentAlpha, bool isInvertedMatte, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) {
float normalizedOpacity = node->alpha();
float layerAlpha = ((float)normalizedOpacity) * parentAlpha;
@ -465,7 +465,7 @@ static void renderLottieRenderNode(std::shared_ptr<lottie::RenderTreeNode> node,
}
auto currentTransform = parentTransform;
lottie::Transform3D localTransform = node->transform();
lottie::Transform2D localTransform = node->transform();
currentTransform = localTransform * currentTransform;
std::shared_ptr<lottieRendering::Canvas> maskContext;
@ -507,7 +507,7 @@ static void renderLottieRenderNode(std::shared_ptr<lottie::RenderTreeNode> node,
if ((node->mask() && !node->mask()->isHidden() && node->mask()->alpha() >= minVisibleAlpha) || masksToBounds) {
auto maskBackingStorage = parentContext->makeLayer((int)(globalRect->width), (int)(globalRect->height));
maskBackingStorage->concatenate(lottie::Transform3D::identity().translated(lottie::Vector2D(-globalRect->x, -globalRect->y)));
maskBackingStorage->concatenate(lottie::Transform2D::identity().translated(lottie::Vector2D(-globalRect->x, -globalRect->y)));
maskBackingStorage->concatenate(currentTransform);
if (masksToBounds) {
@ -524,7 +524,7 @@ static void renderLottieRenderNode(std::shared_ptr<lottie::RenderTreeNode> node,
tempContext = tempContextValue;
currentContext = tempContextValue;
currentContext->concatenate(lottie::Transform3D::identity().translated(lottie::Vector2D(-globalRect->x, -globalRect->y)));
currentContext->concatenate(lottie::Transform2D::identity().translated(lottie::Vector2D(-globalRect->x, -globalRect->y)));
currentContext->saveState();
currentContext->concatenate(currentTransform);
@ -602,13 +602,13 @@ CGRect getPathNativeBoundingBox(CGPathRef _Nonnull path) {
return nil;
}
lottie::Transform3D rootTransform = lottie::Transform3D::identity().scaled(lottie::Vector2D(size.width / (float)animation.size.width, size.height / (float)animation.size.height));
lottie::Transform2D rootTransform = lottie::Transform2D::identity().scaled(lottie::Vector2D(size.width / (float)animation.size.width, size.height / (float)animation.size.height));
if (useReferenceRendering) {
auto context = std::make_shared<lottieRendering::CanvasImpl>((int)size.width, (int)size.height);
CGPoint scale = CGPointMake(size.width / (CGFloat)animation.size.width, size.height / (CGFloat)animation.size.height);
context->concatenate(lottie::Transform3D::makeScale(scale.x, scale.y, 1.0));
context->concatenate(lottie::Transform2D::makeScale(scale.x, scale.y));
renderLottieRenderNode(renderNode, context, lottie::Vector2D(context->width(), context->height()), rootTransform, 1.0, false, *_bezierPathsBoundingBoxContext.get());
@ -620,7 +620,7 @@ CGRect getPathNativeBoundingBox(CGPathRef _Nonnull path) {
auto context = std::make_shared<lottieRendering::NullCanvasImpl>((int)size.width, (int)size.height);
CGPoint scale = CGPointMake(size.width / (CGFloat)animation.size.width, size.height / (CGFloat)animation.size.height);
context->concatenate(lottie::Transform3D::makeScale(scale.x, scale.y, 1.0));
context->concatenate(lottie::Transform2D::makeScale(scale.x, scale.y));
renderLottieRenderNode(renderNode, context, lottie::Vector2D(context->width(), context->height()), rootTransform, 1.0, false, *_bezierPathsBoundingBoxContext.get());

View File

@ -32,7 +32,7 @@ public:
virtual void setAlpha(float alpha) override;
virtual void concatenate(lottie::Transform3D const &transform) override;
virtual void concatenate(lottie::Transform2D const &transform) override;
virtual void draw(std::shared_ptr<Canvas> const &other, lottie::CGRect const &rect) override;
@ -52,8 +52,8 @@ private:
std::unique_ptr<tvg::SwCanvas> _canvas;
float _alpha = 1.0;
lottie::Transform3D _transform;
std::vector<lottie::Transform3D> _stateStack;
lottie::Transform2D _transform;
std::vector<lottie::Transform2D> _stateStack;
int _bytesPerRow = 0;
uint32_t *_backingData = nullptr;
int _statsNumStrokes = 0;

View File

@ -27,7 +27,7 @@ void tvgPath(CanvasPathEnumerator const &enumeratePath, tvg::Shape *shape) {
});
}
tvg::Matrix tvgTransform(lottie::Transform3D const &transform) {
tvg::Matrix tvgTransform(lottie::Transform2D const &transform) {
CGAffineTransform affineTransform = CATransform3DGetAffineTransform(lottie::nativeTransform(transform));
tvg::Matrix result;
result.e11 = affineTransform.a;
@ -45,7 +45,7 @@ tvg::Matrix tvgTransform(lottie::Transform3D const &transform) {
}
ThorVGCanvasImpl::ThorVGCanvasImpl(int width, int height) :
_width(width), _height(height), _transform(lottie::Transform3D::identity()) {
_width(width), _height(height), _transform(lottie::Transform2D::identity()) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
tvg::Initializer::init(0);
@ -259,7 +259,7 @@ void ThorVGCanvasImpl::setAlpha(float alpha) {
_alpha = alpha;
}
void ThorVGCanvasImpl::concatenate(lottie::Transform3D const &transform) {
void ThorVGCanvasImpl::concatenate(lottie::Transform2D const &transform) {
_transform = transform * _transform;
/*_canvas->concat(SkM44(
transform.m11, transform.m21, transform.m31, transform.m41,

View File

@ -122,7 +122,7 @@ public:
std::optional<bool> const &closed() const;
void setClosed(std::optional<bool> const &closed);
std::shared_ptr<CGPath> cgPath() const;
BezierPath copyUsingTransform(Transform3D const &transform) const;
BezierPath copyUsingTransform(Transform2D const &transform) const;
public:
BezierPath(std::shared_ptr<BezierPathContents> contents);

View File

@ -57,7 +57,7 @@ public:
virtual bool empty() const = 0;
virtual std::shared_ptr<CGPath> copyUsingTransform(Transform3D const &transform) const = 0;
virtual std::shared_ptr<CGPath> copyUsingTransform(Transform2D const &transform) const = 0;
virtual void addLineTo(Vector2D const &point) = 0;
virtual void addCurveTo(Vector2D const &point, Vector2D const &control1, Vector2D const &control2) = 0;
@ -71,7 +71,7 @@ public:
virtual bool isEqual(CGPath *other) const = 0;
};
Vector2D transformVector(Vector2D const &v, Transform3D const &m);
Vector2D transformVector(Vector2D const &v, Transform2D const &m);
}

View File

@ -22,7 +22,7 @@ public:
virtual bool empty() const override;
virtual std::shared_ptr<CGPath> copyUsingTransform(Transform3D const &transform) const override;
virtual std::shared_ptr<CGPath> copyUsingTransform(Transform2D const &transform) const override;
virtual void addLineTo(Vector2D const &point) override;
virtual void addCurveTo(Vector2D const &point, Vector2D const &control1, Vector2D const &control2) override;

View File

@ -64,7 +64,7 @@ public:
return CurveVertex(point + translation, inTangent + translation, outTangent + translation, false);
}
CurveVertex transformed(Transform3D const &transform) const {
CurveVertex transformed(Transform2D const &transform) const {
return CurveVertex(transformVector(point, transform), transformVector(inTangent, transform), transformVector(outTangent, transform), false);
}

View File

@ -364,7 +364,7 @@ public:
public:
bool isGroup = false;
Transform3D transform = Transform3D::identity();
Transform2D transform = Transform2D::identity();
float alpha = 0.0;
std::optional<TrimParams> trimParams;
std::shared_ptr<RenderTreeNodeContentPath> path;
@ -392,7 +392,7 @@ class RenderTreeNode {
public:
RenderTreeNode(
Vector2D size_,
Transform3D transform_,
Transform2D transform_,
float alpha_,
bool masksToBounds_,
bool isHidden_,
@ -421,7 +421,7 @@ public:
return _size;
}
Transform3D const &transform() const {
Transform2D const &transform() const {
return _transform;
}
@ -451,7 +451,7 @@ public:
public:
Vector2D _size;
Transform3D _transform = Transform3D::identity();
Transform2D _transform = Transform2D::identity();
float _alpha = 1.0f;
bool _masksToBounds = false;
bool _isHidden = false;

View File

@ -8,6 +8,8 @@
#include <LottieCpp/lottiejson11.hpp>
#import <simd/simd.h>
namespace lottie {
struct Vector1D {
@ -148,167 +150,64 @@ inline float radiansToDegrees(float value) {
return value * 180.0f / M_PI;
}
struct Transform3D {
float m11, m12, m13, m14;
float m21, m22, m23, m24;
float m31, m32, m33, m34;
float m41, m42, m43, m44;
Transform3D(
float m11_, float m12_, float m13_, float m14_,
float m21_, float m22_, float m23_, float m24_,
float m31_, float m32_, float m33_, float m34_,
float m41_, float m42_, float m43_, float 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_) {
struct Transform2D {
static Transform2D const &identity() {
return _identity;
}
bool operator==(Transform3D 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;
explicit Transform2D(simd_float3x3 const &rows_) :
_rows(rows_) {
}
bool operator!=(Transform3D const &rhs) const {
return !(*this == rhs);
Transform2D operator*(Transform2D const &other) const {
return Transform2D(simd_mul(other._rows, _rows));
}
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;
bool isInvertible() const {
return simd_determinant(_rows) > 0.00000001;
}
static Transform3D makeTranslation(float tx, float ty, float tz) {
return Transform3D(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
tx, ty, tz, 1
);
Transform2D inverted() const {
return Transform2D(simd_inverse(_rows));
}
static Transform3D makeScale(float sx, float sy, float sz) {
return Transform3D(
sx, 0, 0, 0,
0, sy, 0, 0,
0, 0, sz, 0,
0, 0, 0, 1
);
bool isIdentity() const {
return (*this) == identity();
}
static Transform3D makeRotation(float radians, float x, float y, float z);
static Transform3D makeSkew(float skew, float skewAxis) {
float mCos = cos(degreesToRadians(skewAxis));
float mSin = sin(degreesToRadians(skewAxis));
float aTan = tan(degreesToRadians(skew));
Transform3D 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
);
Transform3D 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
);
Transform3D 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 Transform3D makeTransform(
static Transform2D makeTranslation(float tx, float ty);
static Transform2D makeScale(float sx, float sy);
static Transform2D makeRotation(float radians);
static Transform2D makeSkew(float skew, float skewAxis);
static Transform2D makeTransform(
Vector2D const &anchor,
Vector2D const &position,
Vector2D const &scale,
float rotation,
std::optional<float> skew,
std::optional<float> skewAxis
) {
Transform3D result = Transform3D::identity();
if (skew.has_value() && skewAxis.has_value()) {
result = Transform3D::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 = Transform3D::identity().translated(position).rotated(rotation).scaled(Vector2D(scale.x * 0.01, scale.y * 0.01)).translated(Vector2D(-anchor.x, -anchor.y));
}
return result;
);
Transform2D rotated(float degrees) const;
Transform2D translated(Vector2D const &translation) const;
Transform2D scaled(Vector2D const &scale) const;
Transform2D skewed(float skew, float skewAxis) const;
bool operator==(Transform2D const &rhs) const {
return simd_equal(_rows, rhs._rows);
}
Transform3D rotated(float degrees) const;
Transform3D translated(Vector2D const &translation) const;
Transform3D scaled(Vector2D const &scale) const;
Transform3D skewed(float skew, float skewAxis) const {
return Transform3D::makeSkew(skew, skewAxis) * (*this);
bool operator!=(Transform2D const &rhs) const {
return !((*this) == rhs);
}
static Transform3D const &identity() {
return _identity;
simd_float3x3 const &rows() const {
return _rows;
}
Transform3D operator*(Transform3D const &b) const;
bool isInvertible() const;
Transform3D inverted() const;
private:
static Transform3D _identity;
static Transform2D _identity;
simd_float3x3 _rows;
};
struct CGRect {
@ -359,7 +258,7 @@ struct CGRect {
CGRect intersection(CGRect const &other) const;
CGRect unionWith(CGRect const &other) const;
CGRect applyingTransform(Transform3D const &transform) const;
CGRect applyingTransform(Transform2D const &transform) const;
};
inline bool isInRangeOrEqual(float value, float from, float to) {

View File

@ -7,8 +7,8 @@
namespace lottie {
::CATransform3D nativeTransform(Transform3D const &value);
Transform3D fromNativeTransform(::CATransform3D const &value);
::CATransform3D nativeTransform(Transform2D const &value);
Transform2D fromNativeTransform(::CATransform3D const &value);
}

View File

@ -122,7 +122,7 @@ public:
std::vector<std::shared_ptr<RenderTreeNode>> renderTreeValue;
auto renderTreeContentItem = std::make_shared<RenderTreeNode>(
Vector2D(0.0, 0.0),
Transform3D::identity(),
Transform2D::identity(),
1.0,
false,
false,
@ -136,7 +136,7 @@ public:
_contentsTreeNode = std::make_shared<RenderTreeNode>(
Vector2D(0.0, 0.0),
Transform3D::identity(),
Transform2D::identity(),
1.0,
false,
false,
@ -159,7 +159,7 @@ public:
_renderTreeNode = std::make_shared<RenderTreeNode>(
Vector2D(0.0, 0.0),
Transform3D::identity(),
Transform2D::identity(),
1.0,
false,
false,

View File

@ -492,9 +492,9 @@ public:
struct TransformedPath {
BezierPath path;
Transform3D transform;
Transform2D transform;
TransformedPath(BezierPath const &path_, Transform3D const &transform_) :
TransformedPath(BezierPath const &path_, Transform2D const &transform_) :
path(path_),
transform(transform_) {
}
@ -853,13 +853,13 @@ public:
_opacityValue = 1.0;
}
_transformValue = Transform3D::identity().translated(Vector2D(positionValue.x, positionValue.y)).rotated(rotationValue).skewed(-skewValue, skewAxisValue).scaled(Vector2D(scaleValue.x * 0.01, scaleValue.y * 0.01)).translated(Vector2D(-anchorValue.x, -anchorValue.y));
_transformValue = Transform2D::identity().translated(Vector2D(positionValue.x, positionValue.y)).rotated(rotationValue).skewed(-skewValue, skewAxisValue).scaled(Vector2D(scaleValue.x * 0.01, scaleValue.y * 0.01)).translated(Vector2D(-anchorValue.x, -anchorValue.y));
hasValidData = true;
}
}
Transform3D const &transform() {
Transform2D const &transform() {
return _transformValue;
}
@ -878,7 +878,7 @@ public:
std::unique_ptr<KeyframeInterpolator<Vector1D>> _skewAxis;
std::unique_ptr<KeyframeInterpolator<Vector1D>> _opacity;
Transform3D _transformValue = Transform3D::identity();
Transform2D _transformValue = Transform2D::identity();
float _opacityValue = 1.0;
};
@ -910,11 +910,11 @@ public:
std::shared_ptr<RenderTreeNodeContentItem> _contentItem;
private:
std::vector<TransformedPath> collectPaths(size_t subItemLimit, Transform3D const &parentTransform, bool skipApplyTransform) {
std::vector<TransformedPath> collectPaths(size_t subItemLimit, Transform2D const &parentTransform, bool skipApplyTransform) {
std::vector<TransformedPath> mappedPaths;
//TODO:remove skipApplyTransform
Transform3D effectiveTransform = parentTransform;
Transform2D effectiveTransform = parentTransform;
if (!skipApplyTransform && isGroup && transform) {
effectiveTransform = transform->transform() * effectiveTransform;
}
@ -942,7 +942,7 @@ public:
}
CompoundBezierPath trimmedPath = trimCompoundPath(tempPath, currentTrim->start, currentTrim->end, currentTrim->offset, currentTrim->type);
for (auto &path : trimmedPath.paths) {
mappedPaths.emplace_back(path, Transform3D::identity());
mappedPaths.emplace_back(path, Transform2D::identity());
}
} else {
for (auto &path : subItemPaths) {
@ -1061,7 +1061,7 @@ public:
}
void updateContents(std::optional<TrimParams> parentTrim) {
Transform3D containerTransform = Transform3D::identity();
Transform2D containerTransform = Transform2D::identity();
float containerOpacity = 1.0;
if (transform) {
containerTransform = transform->transform();
@ -1089,7 +1089,7 @@ public:
if (parentTrim) {
CompoundBezierPath compoundPath;
auto paths = collectPaths(shadingVariant.subItemLimit, Transform3D::identity(), true);
auto paths = collectPaths(shadingVariant.subItemLimit, Transform2D::identity(), true);
for (const auto &path : paths) {
compoundPath.appendPath(path.path.copyUsingTransform(path.transform));
}
@ -1104,7 +1104,7 @@ public:
} else {
if (hasTrims()) {
CompoundBezierPath compoundPath;
auto paths = collectPaths(shadingVariant.subItemLimit, Transform3D::identity(), true);
auto paths = collectPaths(shadingVariant.subItemLimit, Transform2D::identity(), true);
for (const auto &path : paths) {
compoundPath.appendPath(path.path.copyUsingTransform(path.transform));
}
@ -1324,7 +1324,7 @@ std::shared_ptr<RenderTreeNode> ShapeCompositionLayer::renderTreeNode(BezierPath
if (!_renderTreeNode) {
_contentRenderTreeNode = std::make_shared<RenderTreeNode>(
Vector2D(0.0, 0.0),
Transform3D::identity(),
Transform2D::identity(),
1.0,
false,
false,
@ -1349,7 +1349,7 @@ std::shared_ptr<RenderTreeNode> ShapeCompositionLayer::renderTreeNode(BezierPath
_renderTreeNode = std::make_shared<RenderTreeNode>(
Vector2D(0.0, 0.0),
Transform3D::identity(),
Transform2D::identity(),
1.0,
false,
false,

View File

@ -238,7 +238,7 @@ public:
}
_renderTreeNode = std::make_shared<RenderTreeNode>(
size(),
Transform3D::identity(),
Transform2D::identity(),
1.0,
false,
false,

View File

@ -157,7 +157,7 @@ public:
Vector3D anchor = _transformProperties->anchor()->value();
Vector3D scale = _transformProperties->scale()->value();
_localTransform = Transform3D::makeTransform(
_localTransform = Transform2D::makeTransform(
Vector2D(anchor.x, anchor.y),
position,
Vector2D(scale.x, scale.y),
@ -181,7 +181,7 @@ public:
return _opacity;
}
Transform3D const &globalTransform() {
Transform2D const &globalTransform() {
return _globalTransform;
}
@ -191,8 +191,8 @@ private:
std::shared_ptr<LayerTransformProperties> _transformProperties;
float _opacity = 1.0;
Transform3D _localTransform = Transform3D::identity();
Transform3D _globalTransform = Transform3D::identity();
Transform2D _localTransform = Transform2D::identity();
Transform2D _globalTransform = Transform2D::identity();
public:
virtual LayerTransformNode *asLayerTransformNode() override {

View File

@ -93,7 +93,7 @@ public:
return _childKeypaths;
}
Transform3D caTransform() {
Transform2D caTransform() {
Vector2D anchor = Vector2D::Zero();
if (_anchor) {
auto anchor3d = _anchor->value();
@ -126,7 +126,7 @@ public:
skewAxis = _skewAxis->value().value;
}
return Transform3D::makeTransform(
return Transform2D::makeTransform(
anchor,
position,
scale,
@ -212,16 +212,16 @@ public:
return _parentTextNode;
}
Transform3D xform() {
Transform2D xform() {
if (_xform.has_value()) {
return _xform.value();
} else if (_parentTextNode) {
return _parentTextNode->xform();
} else {
return Transform3D::identity();
return Transform2D::identity();
}
}
void setXform(Transform3D const &xform) {
void setXform(Transform2D const &xform) {
_xform = xform;
}
@ -312,7 +312,7 @@ private:
std::shared_ptr<CGPath> _outputPath;
std::optional<Transform3D> _xform;
std::optional<Transform2D> _xform;
std::optional<float> _opacity;
std::optional<Color> _strokeColor;
std::optional<Color> _fillColor;

View File

@ -486,8 +486,8 @@ std::shared_ptr<CGPath> BezierPath::cgPath() const {
return _contents->cgPath();
}
BezierPath BezierPath::copyUsingTransform(Transform3D const &transform) const {
if (transform == Transform3D::identity()) {
BezierPath BezierPath::copyUsingTransform(Transform2D const &transform) const {
if (transform == Transform2D::identity()) {
return (*this);
}
BezierPath result;

View File

@ -64,10 +64,10 @@ public:
_size = size;
}
Transform3D const &transform() const {
Transform2D const &transform() const {
return _transform;
}
void setTransform(Transform3D const &transform) {
void setTransform(Transform2D const &transform) {
_transform = transform;
}
@ -117,7 +117,7 @@ private:
bool _isHidden = false;
float _opacity = 1.0;
Vector2D _size = Vector2D(0.0, 0.0);
Transform3D _transform = Transform3D::identity();
Transform2D _transform = Transform2D::identity();
std::shared_ptr<CALayer> _mask;
bool _masksToBounds = false;
std::optional<BlendMode> _compositingFilter;

View File

@ -35,11 +35,10 @@ void addPointToBoundingRect(bool *isFirst, CGRect *rect, Vector2D const *point)
}
Vector2D transformVector(Vector2D const &v, Transform3D const &m) {
return Vector2D(
m.m11 * v.x + m.m21 * v.y + m.m41 * 1.0,
m.m12 * v.x + m.m22 * v.y + m.m42 * 1.0
);
Vector2D transformVector(Vector2D const &v, Transform2D const &m) {
float transformedX = m.rows().columns[0][0] * v.x + m.rows().columns[1][0] * v.y + m.rows().columns[2][0] * 1.0f;
float transformedY = m.rows().columns[0][1] * v.x + m.rows().columns[1][1] * v.y + m.rows().columns[2][1] * 1.0f;
return Vector2D(transformedX, transformedY);
}
class CGPathImpl: public CGPath {
@ -51,7 +50,7 @@ public:
virtual bool empty() const override;
virtual std::shared_ptr<CGPath> copyUsingTransform(Transform3D const &transform) const override;
virtual std::shared_ptr<CGPath> copyUsingTransform(Transform2D const &transform) const override;
virtual void addLineTo(Vector2D const &point) override;
virtual void addCurveTo(Vector2D const &point, Vector2D const &control1, Vector2D const &control2) override;
@ -108,10 +107,10 @@ bool CGPathImpl::empty() const {
return _items.empty();
}
std::shared_ptr<CGPath> CGPathImpl::copyUsingTransform(Transform3D const &transform) const {
std::shared_ptr<CGPath> CGPathImpl::copyUsingTransform(Transform2D const &transform) const {
auto result = std::make_shared<CGPathImpl>();
if (transform == Transform3D::identity()) {
if (transform == Transform2D::identity()) {
result->_items = _items;
return result;
}

View File

@ -92,29 +92,12 @@ bool CGPathCocoaImpl::empty() const {
return CGPathIsEmpty(_path);
}
std::shared_ptr<CGPath> CGPathCocoaImpl::copyUsingTransform(Transform3D const &transform) const {
::CATransform3D nativeTransform;
nativeTransform.m11 = transform.m11;
nativeTransform.m12 = transform.m12;
nativeTransform.m13 = transform.m13;
nativeTransform.m14 = transform.m14;
nativeTransform.m21 = transform.m21;
nativeTransform.m22 = transform.m22;
nativeTransform.m23 = transform.m23;
nativeTransform.m24 = transform.m24;
nativeTransform.m31 = transform.m31;
nativeTransform.m32 = transform.m32;
nativeTransform.m33 = transform.m33;
nativeTransform.m34 = transform.m34;
nativeTransform.m41 = transform.m41;
nativeTransform.m42 = transform.m42;
nativeTransform.m43 = transform.m43;
nativeTransform.m44 = transform.m44;
auto affineTransform = CATransform3DGetAffineTransform(nativeTransform);
std::shared_ptr<CGPath> CGPathCocoaImpl::copyUsingTransform(Transform2D const &transform) const {
CGAffineTransform affineTransform = CGAffineTransformMake(
transform.rows().columns[0][0], transform.rows().columns[0][1],
transform.rows().columns[1][0], transform.rows().columns[1][1],
transform.rows().columns[2][0], transform.rows().columns[2][1]
);
CGPathRef resultPath = CGPathCreateCopyByTransformingPath(_path, &affineTransform);
if (resultPath == nil) {

View File

@ -8,10 +8,261 @@
#import <QuartzCore/QuartzCore.h>
#import <simd/simd.h>
namespace lottie {
/*explicit Transform2D(Transform3D const &t) {
CGAffineTransform at = CATransform3DGetAffineTransform(nativeTransform(t));
_rows.columns[0] = simd_make_float3(at.a, at.b, 0.0);
_rows.columns[1] = simd_make_float3(at.c, at.d, 0.0);
_rows.columns[2] = simd_make_float3(at.tx, at.ty, 1.0);
}
Transform3D transform3D() {
CGAffineTransform at = CGAffineTransformMake(
_rows.columns[0][0], _rows.columns[0][1],
_rows.columns[1][0], _rows.columns[1][1],
_rows.columns[2][0], _rows.columns[2][1]
);
return fromNativeTransform(CATransform3DMakeAffineTransform(at));
}*/
/*struct Transform3D {
float m11, m12, m13, m14;
float m21, m22, m23, m24;
float m31, m32, m33, m34;
float m41, m42, m43, m44;
Transform3D(
float m11_, float m12_, float m13_, float m14_,
float m21_, float m22_, float m23_, float m24_,
float m31_, float m32_, float m33_, float m34_,
float m41_, float m42_, float m43_, float 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==(Transform3D 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!=(Transform3D 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 Transform3D makeTranslation(float tx, float ty, float tz) {
return Transform3D(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
tx, ty, tz, 1
);
}
static Transform3D makeScale(float sx, float sy, float sz) {
return Transform3D(
sx, 0, 0, 0,
0, sy, 0, 0,
0, 0, sz, 0,
0, 0, 0, 1
);
}
static Transform3D makeRotation(float radians);
static Transform3D makeSkew(float skew, float skewAxis) {
float mCos = cos(degreesToRadians(skewAxis));
float mSin = sin(degreesToRadians(skewAxis));
float aTan = tan(degreesToRadians(skew));
Transform3D 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
);
Transform3D 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
);
Transform3D 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 Transform3D makeTransform(
Vector2D const &anchor,
Vector2D const &position,
Vector2D const &scale,
float rotation,
std::optional<float> skew,
std::optional<float> skewAxis
) {
Transform3D result = Transform3D::identity();
if (skew.has_value() && skewAxis.has_value()) {
result = Transform3D::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 = Transform3D::identity().translated(position).rotated(rotation).scaled(Vector2D(scale.x * 0.01, scale.y * 0.01)).translated(Vector2D(-anchor.x, -anchor.y));
}
return result;
}
Transform3D rotated(float degrees) const;
Transform3D translated(Vector2D const &translation) const;
Transform3D scaled(Vector2D const &scale) const;
Transform3D skewed(float skew, float skewAxis) const {
return Transform3D::makeSkew(skew, skewAxis) * (*this);
}
static Transform3D identity() {
return Transform3D(
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
);
}
Transform3D operator*(Transform3D const &b) const;
};*/
/*Transform2D t2d(Transform3D const &testMatrix) {
::CATransform3D nativeTest;
nativeTest.m11 = testMatrix.m11;
nativeTest.m12 = testMatrix.m12;
nativeTest.m13 = testMatrix.m13;
nativeTest.m14 = testMatrix.m14;
nativeTest.m21 = testMatrix.m21;
nativeTest.m22 = testMatrix.m22;
nativeTest.m23 = testMatrix.m23;
nativeTest.m24 = testMatrix.m24;
nativeTest.m31 = testMatrix.m31;
nativeTest.m32 = testMatrix.m32;
nativeTest.m33 = testMatrix.m33;
nativeTest.m34 = testMatrix.m34;
nativeTest.m41 = testMatrix.m41;
nativeTest.m42 = testMatrix.m42;
nativeTest.m43 = testMatrix.m43;
nativeTest.m44 = testMatrix.m44;
CGAffineTransform at = CATransform3DGetAffineTransform(nativeTest);
Transform2D result = Transform2D::identity();
simd_float3x3 *rows = (simd_float3x3 *)&result.rows();
rows->columns[0] = simd_make_float3(at.a, at.b, 0.0);
rows->columns[1] = simd_make_float3(at.c, at.d, 0.0);
rows->columns[2] = simd_make_float3(at.tx, at.ty, 1.0);
return result;
}
Transform3D t3d(Transform2D const &t) {
CGAffineTransform at = CGAffineTransformMake(
t.rows().columns[0][0], t.rows().columns[0][1],
t.rows().columns[1][0], t.rows().columns[1][1],
t.rows().columns[2][0], t.rows().columns[2][1]
);
::CATransform3D value = CATransform3DMakeAffineTransform(at);
Transform3D result = Transform3D::identity();
result.m11 = value.m11;
result.m12 = value.m12;
result.m13 = value.m13;
result.m14 = value.m14;
result.m21 = value.m21;
result.m22 = value.m22;
result.m23 = value.m23;
result.m24 = value.m24;
result.m31 = value.m31;
result.m32 = value.m32;
result.m33 = value.m33;
result.m34 = value.m34;
result.m41 = value.m41;
result.m42 = value.m42;
result.m43 = value.m43;
result.m44 = value.m44;
return result;
}
Transform3D Transform3D::operator*(Transform3D const &b) const {
if (isIdentity()) {
return b;
}
if (b.isIdentity()) {
return *this;
}
return t3d((t2d(*this) * t2d(b)));
}*/
Vector1D::Vector1D(lottiejson11::Json const &json) noexcept(false) {
if (json.is_number()) {
value = json.number_value();
@ -140,13 +391,108 @@ lottiejson11::Json Vector3D::toJson() const {
return lottiejson11::Json(result);
}
Transform3D Transform3D::_identity = Transform3D(
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0
Transform2D Transform2D::_identity = Transform2D(
simd_float3x3({
simd_make_float3(1.0f, 0.0f, 0.0f),
simd_make_float3(0.0f, 1.0f, 0.0f),
simd_make_float3(0.0f, 0.0f, 1.0f)
})
);
Transform2D Transform2D::makeTranslation(float tx, float ty) {
return Transform2D(simd_float3x3({
simd_make_float3(1.0f, 0.0f, 0.0f),
simd_make_float3(0.0f, 1.0f, 0.0f),
simd_make_float3(tx, ty, 1.0f)
}));
}
Transform2D Transform2D::makeScale(float sx, float sy) {
return Transform2D(simd_float3x3({
simd_make_float3(sx, 0.0f, 0.0f),
simd_make_float3(0.0f, sy, 0.0f),
simd_make_float3(0.0f, 0.0f, 1.0f)
}));
}
Transform2D Transform2D::makeRotation(float radians) {
float c = cos(radians);
float s = sin(radians);
return Transform2D(simd_float3x3({
simd_make_float3(c, s, 0.0f),
simd_make_float3(-s, c, 0.0f),
simd_make_float3(0.0f, 0.0f, 1.0f)
}));
}
Transform2D Transform2D::makeSkew(float skew, float skewAxis) {
if (std::abs(skew) <= FLT_EPSILON && std::abs(skewAxis) <= FLT_EPSILON) {
return Transform2D::identity();
}
float mCos = cos(degreesToRadians(skewAxis));
float mSin = sin(degreesToRadians(skewAxis));
float aTan = tan(degreesToRadians(skew));
simd_float3x3 simd1 = simd_float3x3({
simd_make_float3(mCos, -mSin, 0.0),
simd_make_float3(mSin, mCos, 0.0),
simd_make_float3(0.0, 0.0, 1.0)
});
simd_float3x3 simd2 = simd_float3x3({
simd_make_float3(1.0, 0.0, 0.0),
simd_make_float3(aTan, 1.0, 0.0),
simd_make_float3(0.0, 0.0, 1.0)
});
simd_float3x3 simd3 = simd_float3x3({
simd_make_float3(mCos, mSin, 0.0),
simd_make_float3(-mSin, mCos, 0.0),
simd_make_float3(0.0, 0.0, 1.0)
});
simd_float3x3 result = simd_mul(simd_mul(simd3, simd2), simd1);
Transform2D resultTransform(result);
return resultTransform;
}
Transform2D Transform2D::makeTransform(
Vector2D const &anchor,
Vector2D const &position,
Vector2D const &scale,
float rotation,
std::optional<float> skew,
std::optional<float> skewAxis
) {
Transform2D result = Transform2D::identity();
if (skew.has_value() && skewAxis.has_value()) {
result = Transform2D::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 = Transform2D::identity().translated(position).rotated(rotation).scaled(Vector2D(scale.x * 0.01, scale.y * 0.01)).translated(Vector2D(-anchor.x, -anchor.y));
}
return result;
}
Transform2D Transform2D::rotated(float degrees) const {
return Transform2D::makeRotation(degreesToRadians(degrees)) * (*this);
}
Transform2D Transform2D::translated(Vector2D const &translation) const {
return Transform2D::makeTranslation(translation.x, translation.y) * (*this);
}
Transform2D Transform2D::scaled(Vector2D const &scale) const {
return Transform2D::makeScale(scale.x, scale.y) * (*this);
}
Transform2D Transform2D::skewed(float skew, float skewAxis) const {
return Transform2D::makeSkew(skew, skewAxis) * (*this);
}
float interpolate(float value, float to, float amount) {
return value + ((to - value) * amount);
}
@ -415,8 +761,15 @@ Vector2D Vector2D::interpolate(
return point.point;
}
::CATransform3D nativeTransform(Transform3D const &value) {
::CATransform3D result;
::CATransform3D nativeTransform(Transform2D const &value) {
CGAffineTransform at = CGAffineTransformMake(
value.rows().columns[0][0], value.rows().columns[0][1],
value.rows().columns[1][0], value.rows().columns[1][1],
value.rows().columns[2][0], value.rows().columns[2][1]
);
return CATransform3DMakeAffineTransform(at);
/*::CATransform3D result;
result.m11 = value.m11;
result.m12 = value.m12;
@ -438,11 +791,20 @@ Vector2D Vector2D::interpolate(
result.m43 = value.m43;
result.m44 = value.m44;
return result;
return result;*/
}
Transform3D fromNativeTransform(::CATransform3D const &value) {
Transform3D result = Transform3D::identity();
Transform2D fromNativeTransform(::CATransform3D const &value) {
CGAffineTransform at = CATransform3DGetAffineTransform(value);
return Transform2D(
simd_float3x3({
simd_make_float3(at.a, at.b, 0.0),
simd_make_float3(at.c, at.d, 0.0),
simd_make_float3(at.tx, at.ty, 1.0)
})
);
/*Transform2D result = Transform2D::identity();
result.m11 = value.m11;
result.m12 = value.m12;
@ -464,47 +826,23 @@ Transform3D fromNativeTransform(::CATransform3D const &value) {
result.m43 = value.m43;
result.m44 = value.m44;
return result;
return result;*/
}
Transform3D Transform3D::makeRotation(float radians, float x, float y, float z) {
if (std::abs(radians) <= FLT_EPSILON || (x == 0.0 && y == 0.0 && z == 0.0)) {
/*Transform3D Transform3D::makeRotation(float radians) {
if (std::abs(radians) <= FLT_EPSILON) {
return Transform3D::identity();
}
float s = sin(radians);
float c = cos(radians);
float len = sqrt(x*x + y*y + z*z);
x /= len; y /= len; z /= len;
Transform3D returnValue = Transform3D::identity();
returnValue.m11 = c + (1.0f - c) * x * x;
returnValue.m12 = (1.0f - c) * x*y + s * z;
returnValue.m13 = (1.0f - c) * x*z - s * y;
returnValue.m14 = 0.0f;
returnValue.m21 = (1.0f - c) * y * x - s * z;
returnValue.m22 = c + (1.0f - c) * y * y;
returnValue.m23 = (1.0f - c) * y * z + s * x;
returnValue.m24 = 0.0f;
returnValue.m31 = (1.0f - c) * z * x + s * y;
returnValue.m32 = (1.0f - c) * y * z - s * x;
returnValue.m33 = c + (1.0f - c) * z * z;
returnValue.m34 = 0.0f;
returnValue.m41 = 0.0f;
returnValue.m42 = 0.0f;
returnValue.m43 = 0.0f;
returnValue.m44 = 1.0f;
return returnValue;
::CGAffineTransform t = CGAffineTransformMake(c, s, -s, c, 0.0f, 0.0f);
return fromNativeTransform(CATransform3DMakeAffineTransform(t));
}
Transform3D Transform3D::rotated(float degrees) const {
return Transform3D::makeRotation(degreesToRadians(degrees), 0.0, 0.0, 1.0) * (*this);
return Transform3D::makeRotation(degreesToRadians(degrees)) * (*this);
}
Transform3D Transform3D::translated(Vector2D const &translation) const {
@ -515,57 +853,14 @@ Transform3D Transform3D::scaled(Vector2D const &scale) const {
return Transform3D::makeScale(scale.x, scale.y, 1.0) * (*this);
}
Transform3D Transform3D::operator*(Transform3D const &b) const {
if (isIdentity()) {
return b;
}
if (b.isIdentity()) {
return *this;
}
simd_float4x4 simdLhs = {
simd_make_float4(b.m11, b.m21, b.m31, b.m41),
simd_make_float4(b.m12, b.m22, b.m32, b.m42),
simd_make_float4(b.m13, b.m23, b.m33, b.m43),
simd_make_float4(b.m14, b.m24, b.m34, b.m44)
};
simd_float4x4 simdRhs = {
simd_make_float4(m11, m21, m31, m41),
simd_make_float4(m12, m22, m32, m42),
simd_make_float4(m13, m23, m33, m43),
simd_make_float4(m14, m24, m34, m44)
};
simd_float4x4 simdResult = simd_mul(simdRhs, simdLhs);
return Transform3D(
simdResult.columns[0][0], simdResult.columns[1][0], simdResult.columns[2][0], simdResult.columns[3][0],
simdResult.columns[0][1], simdResult.columns[1][1], simdResult.columns[2][1], simdResult.columns[3][1],
simdResult.columns[0][2], simdResult.columns[1][2], simdResult.columns[2][2], simdResult.columns[3][2],
simdResult.columns[0][3], simdResult.columns[1][3], simdResult.columns[2][3], simdResult.columns[3][3]
);
}
bool Transform3D::isInvertible() const {
return std::abs(m11 * m22 - m12 * m21) >= 0.00000001;
return Transform2D(*this).isInvertible();
//return std::abs(m11 * m22 - m12 * m21) >= 0.00000001;
}
Transform3D Transform3D::inverted() const {
simd_float4x4 matrix = {
simd_make_float4(m11, m21, m31, m41),
simd_make_float4(m12, m22, m32, m42),
simd_make_float4(m13, m23, m33, m43),
simd_make_float4(m14, m24, m34, m44)
};
simd_float4x4 result = simd_inverse(matrix);
Transform3D nativeResult = Transform3D(
result.columns[0][0], result.columns[1][0], result.columns[2][0], result.columns[3][0],
result.columns[0][1], result.columns[1][1], result.columns[2][1], result.columns[3][1],
result.columns[0][2], result.columns[1][2], result.columns[2][2], result.columns[3][2],
result.columns[0][3], result.columns[1][3], result.columns[2][3], result.columns[3][3]
);
return nativeResult;
}
return Transform2D(*this).inverted().transform3D();
}*/
bool CGRect::intersects(CGRect const &other) const {
return CGRectIntersectsRect(CGRectMake(x, y, width, height), CGRectMake(other.x, other.y, other.width, other.height));
@ -585,29 +880,26 @@ CGRect CGRect::unionWith(CGRect const &other) const {
return CGRect(result.origin.x, result.origin.y, result.size.width, result.size.height);
}
CGRect CGRect::applyingTransform(Transform3D const &transform) const {
CGRect CGRect::applyingTransform(Transform2D const &transform) const {
if (transform.isIdentity()) {
return *this;
}
simd_float3 simdRow1 = simd_make_float3(transform.m11, transform.m12, transform.m14);
simd_float3 simdRow2 = simd_make_float3(transform.m21, transform.m22, transform.m24);
simd_float3 simdRow3 = simd_make_float3(transform.m41, transform.m42, transform.m44);
Vector2D sourceTopLeft = Vector2D(x, y);
Vector2D sourceTopRight = Vector2D(x + width, y);
Vector2D sourceBottomLeft = Vector2D(x, y + height);
Vector2D sourceBottomRight = Vector2D(x + width, y + height);
simd_float3 simdTopLeft = sourceTopLeft.x * simdRow1 + sourceTopLeft.y * simdRow2 + simdRow3;
simd_float3 simdTopRight = sourceTopRight.x * simdRow1 + sourceTopRight.y * simdRow2 + simdRow3;
simd_float3 simdBottomLeft = sourceBottomLeft.x * simdRow1 + sourceBottomLeft.y * simdRow2 + simdRow3;
simd_float3 simdBottomRight = sourceBottomRight.x * simdRow1 + sourceBottomRight.y * simdRow2 + simdRow3;
simd_float4 xs = simd_make_float4(sourceTopLeft.x, sourceTopRight.x, sourceBottomLeft.x, sourceBottomRight.x);
simd_float4 ys = simd_make_float4(sourceTopLeft.y, sourceTopRight.y, sourceBottomLeft.y, sourceBottomRight.y);
Vector2D topLeft = Vector2D(simdTopLeft[0] / simdTopLeft[2], simdTopLeft[1] / simdTopLeft[2]);
Vector2D topRight = Vector2D(simdTopRight[0] / simdTopRight[2], simdTopRight[1] / simdTopRight[2]);
Vector2D bottomLeft = Vector2D(simdBottomLeft[0] / simdBottomLeft[2], simdBottomLeft[1] / simdBottomLeft[2]);
Vector2D bottomRight = Vector2D(simdBottomRight[0] / simdBottomRight[2], simdBottomRight[1] / simdBottomRight[2]);
simd_float4 rx = xs * transform.rows().columns[0][0] + ys * transform.rows().columns[1][0] + transform.rows().columns[2][0];
simd_float4 ry = xs * transform.rows().columns[0][1] + ys * transform.rows().columns[1][1] + transform.rows().columns[2][1];
Vector2D topLeft = Vector2D(rx[0], ry[0]);
Vector2D topRight = Vector2D(rx[1], ry[1]);
Vector2D bottomLeft = Vector2D(rx[2], ry[2]);
Vector2D bottomRight = Vector2D(rx[3], ry[3]);
float minX = simd_reduce_min(simd_make_float4(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x));
float minY = simd_reduce_min(simd_make_float4(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y));