| #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; | |
| m_ComponentsA = transformA.decompose(); | |
| if (m_Target == nullptr) { | |
| transformB = transformA; | |
| m_ComponentsB = m_ComponentsA; | |
| } else { | |
| transformB = m_Target->worldTransform(); | |
| if (sourceSpace() == TransformSpace::local) { | |
| Mat2D inverse; | |
| if (!getParentWorld(*m_Target).invert(&inverse)) { | |
| return; | |
| } | |
| transformB = inverse * transformB; | |
| } | |
| m_ComponentsB = transformB.decompose(); | |
| 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. | |
| transformB = Mat2D::compose(m_ComponentsB); | |
| transformB = getParentWorld(*component) * transformB; | |
| m_ComponentsB = transformB.decompose(); | |
| } | |
| } | |
| bool clamplocal = minMaxSpace() == TransformSpace::local; | |
| if (clamplocal) { | |
| // Apply min max in local space, so transform to local coordinates | |
| // first. | |
| transformB = Mat2D::compose(m_ComponentsB); | |
| Mat2D inverse; | |
| if (!getParentWorld(*component).invert(&inverse)) { | |
| return; | |
| } | |
| transformB = inverse * transformB; | |
| m_ComponentsB = transformB.decompose(); | |
| } | |
| 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. | |
| transformB = Mat2D::compose(m_ComponentsB); | |
| transformB = getParentWorld(*component) * transformB; | |
| m_ComponentsB = transformB.decompose(); | |
| } | |
| 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()); | |
| component->mutableWorldTransform() = Mat2D::compose(m_ComponentsB); | |
| } |