| #include "rive/constraints/constraint.hpp" | |
| #include "rive/container_component.hpp" | |
| #include "rive/transform_component.hpp" | |
| #include "rive/core_context.hpp" | |
| #include "rive/math/mat2d.hpp" | |
| using namespace rive; | |
| StatusCode Constraint::onAddedDirty(CoreContext* context) | |
| { | |
| StatusCode result = Super::onAddedDirty(context); | |
| #ifndef WITH_RIVE_EDITOR | |
| // Runtime-only path; editor build registers via | |
| // `editorParentChanged` (dispatcher Pass 4.5). | |
| if (!parent()->is<TransformComponent>()) | |
| { | |
| return StatusCode::InvalidObject; | |
| } | |
| parent()->as<TransformComponent>()->addConstraint(this); | |
| #endif | |
| return result; | |
| } | |
| void Constraint::markConstraintDirty() | |
| { | |
| parent()->as<TransformComponent>()->markTransformDirty(); | |
| } | |
| void Constraint::strengthChanged() { markConstraintDirty(); } | |
| void Constraint::buildDependencies() | |
| { | |
| Super::buildDependencies(); | |
| parent()->addDependent(this); | |
| } | |
| void Constraint::onDirty(ComponentDirt dirt) | |
| { | |
| // Whenever the constraint gets any dirt, make sure to mark the constrained | |
| // component dirty. | |
| markConstraintDirty(); | |
| } | |
| static Mat2D identity; | |
| const Mat2D& rive::getParentWorld(const TransformComponent& component) | |
| { | |
| auto parent = component.parent(); | |
| if (parent->is<WorldTransformComponent>()) | |
| { | |
| return parent->as<WorldTransformComponent>()->worldTransform(); | |
| } | |
| return identity; | |
| } | |
| void Constraint::composeKeepingAnchor(TransformComponent* component, | |
| const TransformComponents& composed) | |
| { | |
| Mat2D& world = component->mutableWorldTransform(); | |
| const Vec2D anchor = component->localAnchor(); | |
| if (anchor.x == 0.0f && anchor.y == 0.0f) | |
| { | |
| world = Mat2D::compose(composed); | |
| return; | |
| } | |
| const Vec2D before = world * anchor; | |
| Mat2D result = Mat2D::compose(composed); | |
| const Vec2D after = result * anchor; | |
| result[4] += before.x - after.x; | |
| result[5] += before.y - after.y; | |
| world = result; | |
| } | |
| void Constraint::landAnchor(TransformComponent* component, float strength) | |
| { | |
| const Vec2D anchor = component->localAnchor(); | |
| if (anchor.x == 0.0f && anchor.y == 0.0f) | |
| { | |
| return; | |
| } | |
| Mat2D& world = component->mutableWorldTransform(); | |
| world[4] -= (world[0] * anchor.x + world[2] * anchor.y) * strength; | |
| world[5] -= (world[1] * anchor.x + world[3] * anchor.y) * strength; | |
| } | |
| void Constraint::composeLandingAnchor(TransformComponent* component, | |
| const TransformComponents& composed, | |
| float strength) | |
| { | |
| component->mutableWorldTransform() = Mat2D::compose(composed); | |
| landAnchor(component, strength); | |
| } | |
| Mat2D Constraint::offsetInParentFrame(TransformComponent* component, | |
| const Mat2D& offset) | |
| { | |
| const Mat2D& parentWorld = getParentWorld(*component); | |
| const Vec2D delta = | |
| (parentWorld * offset).translation() - parentWorld.translation(); | |
| Mat2D result = component->worldTransform(); | |
| result[4] += delta.x; | |
| result[5] += delta.y; | |
| return result; | |
| } |