blob: 0fd6b25f571e5b04238d834d57fb13d982f1a177 [file] [log] [blame]
#include "rive/constraints/scale_constraint.hpp"
#include "rive/transform_component.hpp"
#include "rive/math/mat2d.hpp"
#include <cmath>
using namespace rive;
void ScaleConstraint::constrain(TransformComponent* component)
{
const Mat2D& transformA = component->worldTransform();
Mat2D transformB;
Mat2D::decompose(m_ComponentsA, transformA);
if (m_Target == nullptr)
{
Mat2D::copy(transformB, transformA);
TransformComponents::copy(m_ComponentsB, m_ComponentsA);
}
else
{
Mat2D::copy(transformB, m_Target->worldTransform());
if (sourceSpace() == TransformSpace::local)
{
Mat2D inverse;
if (!Mat2D::invert(inverse, getParentWorld(*m_Target)))
{
return;
}
Mat2D::multiply(transformB, inverse, transformB);
}
Mat2D::decompose(m_ComponentsB, transformB);
if (!doesCopy())
{
m_ComponentsB.scaleX(destSpace() == TransformSpace::local
? 1.0f
: m_ComponentsA.scaleX());
}
else
{
m_ComponentsB.scaleX(m_ComponentsB.scaleX() * copyFactor());
if (offset())
{
m_ComponentsB.scaleX(m_ComponentsB.scaleX() *
component->scaleX());
}
}
if (!doesCopyY())
{
m_ComponentsB.scaleY(destSpace() == TransformSpace::local
? 1.0f
: m_ComponentsA.scaleY());
}
else
{
m_ComponentsB.scaleY(m_ComponentsB.scaleY() * copyFactorY());
if (offset())
{
m_ComponentsB.scaleY(m_ComponentsB.scaleY() *
component->scaleY());
}
}
if (destSpace() == TransformSpace::local)
{
// Destination space is in parent transform coordinates. Recompose
// the parent local transform and get it in world, then decompose
// the world for interpolation.
Mat2D::compose(transformB, m_ComponentsB);
Mat2D::multiply(transformB, getParentWorld(*component), transformB);
Mat2D::decompose(m_ComponentsB, transformB);
}
}
bool clamplocal = minMaxSpace() == TransformSpace::local;
if (clamplocal)
{
// Apply min max in local space, so transform to local coordinates
// first.
Mat2D::compose(transformB, m_ComponentsB);
Mat2D inverse;
if (!Mat2D::invert(inverse, getParentWorld(*component)))
{
return;
}
Mat2D::multiply(transformB, inverse, transformB);
Mat2D::decompose(m_ComponentsB, transformB);
}
if (max() && m_ComponentsB.scaleX() > maxValue())
{
m_ComponentsB.scaleX(maxValue());
}
if (min() && m_ComponentsB.scaleX() < minValue())
{
m_ComponentsB.scaleX(minValue());
}
if (maxY() && m_ComponentsB.scaleY() > maxValueY())
{
m_ComponentsB.scaleY(maxValueY());
}
if (minY() && m_ComponentsB.scaleY() < minValueY())
{
m_ComponentsB.scaleY(minValueY());
}
if (clamplocal)
{
// Transform back to world.
Mat2D::compose(transformB, m_ComponentsB);
Mat2D::multiply(transformB, getParentWorld(*component), transformB);
Mat2D::decompose(m_ComponentsB, transformB);
}
float t = strength();
float ti = 1.0f - t;
m_ComponentsB.rotation(m_ComponentsA.rotation());
m_ComponentsB.x(m_ComponentsA.x());
m_ComponentsB.y(m_ComponentsA.y());
m_ComponentsB.scaleX(m_ComponentsA.scaleX() * ti +
m_ComponentsB.scaleX() * t);
m_ComponentsB.scaleY(m_ComponentsA.scaleY() * ti +
m_ComponentsB.scaleY() * t);
m_ComponentsB.skew(m_ComponentsA.skew());
Mat2D::compose(component->mutableWorldTransform(), m_ComponentsB);
}