| #include "rive/constraints/translation_constraint.hpp" | |
| #include "rive/transform_component.hpp" | |
| #include "rive/math/mat2d.hpp" | |
| #include "rive/math/vec2d.hpp" | |
| #include <cmath> | |
| using namespace rive; | |
| void TranslationConstraint::constrain(TransformComponent* component) | |
| { | |
| if (m_Target != nullptr && m_Target->isCollapsed()) | |
| { | |
| return; | |
| } | |
| Mat2D& transformA = component->mutableWorldTransform(); | |
| Vec2D translationA(transformA[4], transformA[5]); | |
| Vec2D translationB; | |
| if (m_Target == nullptr) | |
| { | |
| translationB = translationA; | |
| } | |
| else | |
| { | |
| Mat2D transformB(m_Target->worldTransform()); | |
| if (sourceSpace() == TransformSpace::local) | |
| { | |
| const Mat2D& targetParentWorld = getParentWorld(*m_Target); | |
| Mat2D inverse; | |
| if (!targetParentWorld.invert(&inverse)) | |
| { | |
| return; | |
| } | |
| transformB = inverse * transformB; | |
| } | |
| translationB = transformB.translation(); | |
| if (!doesCopy()) | |
| { | |
| translationB.x = destSpace() == TransformSpace::local ? 0.0f : translationA.x; | |
| } | |
| else | |
| { | |
| translationB.x *= copyFactor(); | |
| if (offset()) | |
| { | |
| translationB.x += component->x(); | |
| } | |
| } | |
| if (!doesCopyY()) | |
| { | |
| translationB.y = destSpace() == TransformSpace::local ? 0.0f : translationA.y; | |
| } | |
| else | |
| { | |
| translationB.y *= copyFactorY(); | |
| if (offset()) | |
| { | |
| translationB.y += component->y(); | |
| } | |
| } | |
| if (destSpace() == TransformSpace::local) | |
| { | |
| // Destination space is in parent transform coordinates. | |
| translationB = getParentWorld(*component) * translationB; | |
| } | |
| } | |
| bool clampLocal = minMaxSpace() == TransformSpace::local; | |
| if (clampLocal) | |
| { | |
| // Apply min max in local space, so transform to local coordinates | |
| // first. | |
| Mat2D inverse; | |
| if (!getParentWorld(*component).invert(&inverse)) | |
| { | |
| return; | |
| } | |
| // Get our target world coordinates in parent local. | |
| translationB = inverse * translationB; | |
| } | |
| if (max() && translationB.x > maxValue()) | |
| { | |
| translationB.x = maxValue(); | |
| } | |
| if (min() && translationB.x < minValue()) | |
| { | |
| translationB.x = minValue(); | |
| } | |
| if (maxY() && translationB.y > maxValueY()) | |
| { | |
| translationB.y = maxValueY(); | |
| } | |
| if (minY() && translationB.y < minValueY()) | |
| { | |
| translationB.y = minValueY(); | |
| } | |
| if (clampLocal) | |
| { | |
| // Transform back to world. | |
| translationB = getParentWorld(*component) * translationB; | |
| } | |
| float t = strength(); | |
| float ti = 1.0f - t; | |
| // Just interpolate world translation | |
| transformA[4] = translationA.x * ti + translationB.x * t; | |
| transformA[5] = translationA.y * ti + translationB.y * t; | |
| } |