| #include "rive/constraints/transform_constraint.hpp" | |
| #include "rive/transform_component.hpp" | |
| #include "rive/math/mat2d.hpp" | |
| #include "rive/math/math_types.hpp" | |
| #include "rive/math/aabb.hpp" | |
| using namespace rive; | |
| const Mat2D TransformConstraint::targetTransform() const | |
| { | |
| auto* tgt = target(); | |
| AABB bounds = tgt->constraintBounds(); | |
| Mat2D local = | |
| Mat2D::fromTranslate(bounds.left() + bounds.width() * originX(), | |
| bounds.top() + bounds.height() * originY()); | |
| return tgt->worldTransform() * local; | |
| } | |
| void TransformConstraint::originXChanged() { markConstraintDirty(); } | |
| void TransformConstraint::originYChanged() { markConstraintDirty(); } | |
| void TransformConstraint::constrain(TransformComponent* component) | |
| { | |
| auto* tgt = target(); | |
| if (tgt == nullptr || tgt->isCollapsed()) | |
| { | |
| return; | |
| } | |
| const Mat2D& transformA = component->worldTransform(); | |
| Mat2D transformB(targetTransform()); | |
| if (sourceSpace() == TransformSpace::local) | |
| { | |
| const Mat2D& targetParentWorld = getParentWorld(*tgt); | |
| Mat2D inverse; | |
| if (!targetParentWorld.invert(&inverse)) | |
| { | |
| return; | |
| } | |
| transformB = inverse * transformB; | |
| } | |
| if (destSpace() == TransformSpace::local) | |
| { | |
| const Mat2D& targetParentWorld = getParentWorld(*component); | |
| transformB = targetParentWorld * transformB; | |
| } | |
| TransformConstraint::constrainWorld(component, | |
| transformA, | |
| m_ComponentsA, | |
| transformB, | |
| m_ComponentsB, | |
| strength()); | |
| // Only here: constrainWorld is also the scroll constraints' generic blend, | |
| // and they target their own transform plus a delta, with no anchor to land. | |
| landAnchor(component, strength()); | |
| } | |
| void TransformConstraint::constrainWorld(TransformComponent* component, | |
| Mat2D from, | |
| TransformComponents componentsFrom, | |
| Mat2D to, | |
| TransformComponents componentsTo, | |
| float strength) | |
| { | |
| componentsFrom = from.decompose(); | |
| componentsTo = to.decompose(); | |
| float angleA = std::fmod(componentsFrom.rotation(), math::PI * 2); | |
| float angleB = std::fmod(componentsTo.rotation(), math::PI * 2); | |
| float diff = angleB - angleA; | |
| if (diff > math::PI) | |
| { | |
| diff -= math::PI * 2; | |
| } | |
| else if (diff < -math::PI) | |
| { | |
| diff += math::PI * 2; | |
| } | |
| float t = strength; | |
| float ti = 1.0f - t; | |
| componentsTo.rotation(angleA + diff * t); | |
| componentsTo.x(componentsFrom.x() * ti + componentsTo.x() * t); | |
| componentsTo.y(componentsFrom.y() * ti + componentsTo.y() * t); | |
| componentsTo.scaleX(componentsFrom.scaleX() * ti + | |
| componentsTo.scaleX() * t); | |
| componentsTo.scaleY(componentsFrom.scaleY() * ti + | |
| componentsTo.scaleY() * t); | |
| componentsTo.skew(componentsFrom.skew() * ti + componentsTo.skew() * t); | |
| component->mutableWorldTransform() = Mat2D::compose(componentsTo); | |
| } |