diff --git a/.rive_head b/.rive_head index bba85ad8..ee132d10 100644 --- a/.rive_head +++ b/.rive_head @@ -1 +1 @@ -db5688c4afb57751ea6278b2b1f1c072d6e69adb +420d27a5bc052e39f9592f1561070ad509589ed1 diff --git a/dev/defs/constraints/follow_path_constraint.json b/dev/defs/constraints/follow_path_constraint.json index c5ff2f3f..5b44ed78 100644 --- a/dev/defs/constraints/follow_path_constraint.json +++ b/dev/defs/constraints/follow_path_constraint.json @@ -4,7 +4,7 @@ "int": 165, "string": "followpathconstraint" }, - "extends": "constraints/transform_constraint.json", + "extends": "constraints/transform_space_constraint.json", "properties": { "distance": { "type": "double", diff --git a/include/rive/constraints/follow_path_constraint.hpp b/include/rive/constraints/follow_path_constraint.hpp index a6221d97..391d1f92 100644 --- a/include/rive/constraints/follow_path_constraint.hpp +++ b/include/rive/constraints/follow_path_constraint.hpp @@ -1,6 +1,7 @@ #ifndef _RIVE_FOLLOW_PATH_CONSTRAINT_HPP_ #define _RIVE_FOLLOW_PATH_CONSTRAINT_HPP_ #include "rive/generated/constraints/follow_path_constraint_base.hpp" +#include "rive/math/transform_components.hpp" #include "rive/shapes/metrics_path.hpp" #include namespace rive @@ -9,12 +10,15 @@ class FollowPathConstraint : public FollowPathConstraintBase { private: std::unique_ptr m_WorldPath; + TransformComponents m_ComponentsA; + TransformComponents m_ComponentsB; public: void distanceChanged() override; void orientChanged() override; StatusCode onAddedClean(CoreContext* context) override; - const Mat2D targetTransform() const override; + const Mat2D targetTransform() const; + void constrain(TransformComponent* component) override; void update(ComponentDirt value) override; void buildDependencies() override; }; diff --git a/include/rive/generated/constraints/follow_path_constraint_base.hpp b/include/rive/generated/constraints/follow_path_constraint_base.hpp index 1c4d3492..089ba159 100644 --- a/include/rive/generated/constraints/follow_path_constraint_base.hpp +++ b/include/rive/generated/constraints/follow_path_constraint_base.hpp @@ -1,14 +1,14 @@ #ifndef _RIVE_FOLLOW_PATH_CONSTRAINT_BASE_HPP_ #define _RIVE_FOLLOW_PATH_CONSTRAINT_BASE_HPP_ -#include "rive/constraints/transform_constraint.hpp" +#include "rive/constraints/transform_space_constraint.hpp" #include "rive/core/field_types/core_bool_type.hpp" #include "rive/core/field_types/core_double_type.hpp" namespace rive { -class FollowPathConstraintBase : public TransformConstraint +class FollowPathConstraintBase : public TransformSpaceConstraint { protected: - typedef TransformConstraint Super; + typedef TransformSpaceConstraint Super; public: static const uint16_t typeKey = 165; @@ -20,7 +20,6 @@ class FollowPathConstraintBase : public TransformConstraint switch (typeKey) { case FollowPathConstraintBase::typeKey: - case TransformConstraintBase::typeKey: case TransformSpaceConstraintBase::typeKey: case TargetedConstraintBase::typeKey: case ConstraintBase::typeKey: @@ -82,7 +81,7 @@ class FollowPathConstraintBase : public TransformConstraint m_Distance = object.m_Distance; m_Orient = object.m_Orient; m_Offset = object.m_Offset; - TransformConstraint::copy(object); + TransformSpaceConstraint::copy(object); } bool deserialize(uint16_t propertyKey, BinaryReader& reader) override @@ -99,7 +98,7 @@ class FollowPathConstraintBase : public TransformConstraint m_Offset = CoreBoolType::deserialize(reader); return true; } - return TransformConstraint::deserialize(propertyKey, reader); + return TransformSpaceConstraint::deserialize(propertyKey, reader); } protected: diff --git a/include/rive/generated/core_registry.hpp b/include/rive/generated/core_registry.hpp index f797a3b7..e2956df7 100644 --- a/include/rive/generated/core_registry.hpp +++ b/include/rive/generated/core_registry.hpp @@ -150,12 +150,12 @@ class CoreRegistry return new DistanceConstraint(); case IKConstraintBase::typeKey: return new IKConstraint(); - case TransformConstraintBase::typeKey: - return new TransformConstraint(); case FollowPathConstraintBase::typeKey: return new FollowPathConstraint(); case TranslationConstraintBase::typeKey: return new TranslationConstraint(); + case TransformConstraintBase::typeKey: + return new TransformConstraint(); case ScaleConstraintBase::typeKey: return new ScaleConstraint(); case RotationConstraintBase::typeKey: @@ -645,15 +645,15 @@ class CoreRegistry case TransformComponentConstraintYBase::maxValueYPropertyKey: object->as()->maxValueY(value); break; + case FollowPathConstraintBase::distancePropertyKey: + object->as()->distance(value); + break; case TransformConstraintBase::originXPropertyKey: object->as()->originX(value); break; case TransformConstraintBase::originYPropertyKey: object->as()->originY(value); break; - case FollowPathConstraintBase::distancePropertyKey: - object->as()->distance(value); - break; case WorldTransformComponentBase::opacityPropertyKey: object->as()->opacity(value); break; @@ -1298,12 +1298,12 @@ class CoreRegistry return object->as()->minValueY(); case TransformComponentConstraintYBase::maxValueYPropertyKey: return object->as()->maxValueY(); + case FollowPathConstraintBase::distancePropertyKey: + return object->as()->distance(); case TransformConstraintBase::originXPropertyKey: return object->as()->originX(); case TransformConstraintBase::originYPropertyKey: return object->as()->originY(); - case FollowPathConstraintBase::distancePropertyKey: - return object->as()->distance(); case WorldTransformComponentBase::opacityPropertyKey: return object->as()->opacity(); case TransformComponentBase::rotationPropertyKey: @@ -1703,9 +1703,9 @@ class CoreRegistry case TransformComponentConstraintYBase::copyFactorYPropertyKey: case TransformComponentConstraintYBase::minValueYPropertyKey: case TransformComponentConstraintYBase::maxValueYPropertyKey: + case FollowPathConstraintBase::distancePropertyKey: case TransformConstraintBase::originXPropertyKey: case TransformConstraintBase::originYPropertyKey: - case FollowPathConstraintBase::distancePropertyKey: case WorldTransformComponentBase::opacityPropertyKey: case TransformComponentBase::rotationPropertyKey: case TransformComponentBase::scaleXPropertyKey: diff --git a/src/constraints/follow_path_constraint.cpp b/src/constraints/follow_path_constraint.cpp index 4866f2d6..c57afb36 100644 --- a/src/constraints/follow_path_constraint.cpp +++ b/src/constraints/follow_path_constraint.cpp @@ -3,9 +3,12 @@ #include "rive/constraints/follow_path_constraint.hpp" #include "rive/factory.hpp" #include "rive/math/contour_measure.hpp" +#include "rive/math/mat2d.hpp" +#include "rive/math/math_types.hpp" #include "rive/shapes/metrics_path.hpp" #include "rive/shapes/path.hpp" #include "rive/shapes/shape.hpp" +#include "rive/transform_component.hpp" #include #include #include @@ -70,6 +73,60 @@ const Mat2D FollowPathConstraint::targetTransform() const return transformB; } +void FollowPathConstraint::constrain(TransformComponent* component) +{ + if (m_Target == nullptr) + { + return; + } + + const Mat2D& transformA = component->worldTransform(); + Mat2D transformB(targetTransform()); + if (sourceSpace() == TransformSpace::local) + { + const Mat2D& targetParentWorld = getParentWorld(*m_Target); + + Mat2D inverse; + if (!targetParentWorld.invert(&inverse)) + { + return; + } + transformB = inverse * transformB; + } + if (destSpace() == TransformSpace::local) + { + const Mat2D& targetParentWorld = getParentWorld(*component); + transformB = targetParentWorld * transformB; + } + + m_ComponentsA = transformA.decompose(); + m_ComponentsB = transformB.decompose(); + + float angleA = std::fmod(m_ComponentsA.rotation(), math::PI * 2); + float angleB = std::fmod(m_ComponentsB.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; + + m_ComponentsB.rotation(angleA + diff * t); + m_ComponentsB.x(m_ComponentsA.x() * ti + m_ComponentsB.x() * t); + m_ComponentsB.y(m_ComponentsA.y() * ti + m_ComponentsB.y() * t); + 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() * ti + m_ComponentsB.skew() * t); + + component->mutableWorldTransform() = Mat2D::compose(m_ComponentsB); +} + void FollowPathConstraint::update(ComponentDirt value) { if (!m_Target->is())