Runtime/interpolate states cpp

Quite a lot of churn here, I created this off of https://github.com/rive-app/rive/pull/5429 which already generated a lot of code.
On top of it, I also generated the C++ classes, which in turn added a bunch of other missing things related to text.
There's *not that much non-generated* code here, but I'll keep this draft for now..?

Diffs=
8b7587241 Runtime/interpolate states cpp (#5437)

Co-authored-by: Umberto Sonnino <umberto@rive.app>
diff --git a/.rive_head b/.rive_head
index c6d0fe3..ae47926 100644
--- a/.rive_head
+++ b/.rive_head
@@ -1 +1 @@
-9695de6e34a963dd4fa4dc73b72e366c7a68ff04
+8b7587241233b7b884c5fa2976ed0cecf45c8022
diff --git a/dev/defs/animation/blend_animation_direct.json b/dev/defs/animation/blend_animation_direct.json
index 303dc45..b4c99b1 100644
--- a/dev/defs/animation/blend_animation_direct.json
+++ b/dev/defs/animation/blend_animation_direct.json
@@ -36,4 +36,4 @@
       "description": "Source to use when establishing the mix value for the animation. 0 means look at the input, 1 look at the mixValue."
     }
   }
-}
+}
\ No newline at end of file
diff --git a/dev/defs/animation/state_machine_component.json b/dev/defs/animation/state_machine_component.json
index 0c6b927..b3272fb 100644
--- a/dev/defs/animation/state_machine_component.json
+++ b/dev/defs/animation/state_machine_component.json
@@ -36,4 +36,4 @@
       "runtime": false
     }
   }
-}
\ No newline at end of file
+}
diff --git a/dev/defs/animation/state_transition.json b/dev/defs/animation/state_transition.json
index 668273f..072d10e 100644
--- a/dev/defs/animation/state_transition.json
+++ b/dev/defs/animation/state_transition.json
@@ -62,6 +62,26 @@
         "string": "exittime"
       },
       "description": "Duration in milliseconds that must elapse before allowing the state to change. If the flags mark this property as being percentage based, the value is in 0-100% of the outgoing animation's duration"
+    },
+    "interpolationType": {
+      "type": "uint",
+      "initialValue": "1",
+      "key": {
+        "int": 349,
+        "string": "interpolationtype"
+      },
+      "description": "The type of interpolation index in Interpolation applied to this state transition ('linear' by default)."
+    },
+    "interpolatorId": {
+      "type": "Id",
+      "typeRuntime": "uint",
+      "initialValue": "Core.missingId",
+      "initialValueRuntime": "-1",
+      "key": {
+        "int": 350,
+        "string": "interpolatorid"
+      },
+      "description": "The id of the custom interpolator used when interpolation is Cubic."
     }
   }
 }
\ No newline at end of file
diff --git a/dev/defs/artboard.json b/dev/defs/artboard.json
index 3a359a6..5319043 100644
--- a/dev/defs/artboard.json
+++ b/dev/defs/artboard.json
@@ -125,4 +125,4 @@
       "coop": false
     }
   }
-}
\ No newline at end of file
+}
diff --git a/dev/defs/assets/font_asset.json b/dev/defs/assets/font_asset.json
index cd39ee8..f10a790 100644
--- a/dev/defs/assets/font_asset.json
+++ b/dev/defs/assets/font_asset.json
@@ -5,4 +5,4 @@
     "string": "fontasset"
   },
   "extends": "assets/file_asset.json"
-}
\ No newline at end of file
+}
diff --git a/dev/defs/backboard.json b/dev/defs/backboard.json
index 66bd5f3..b194d68 100644
--- a/dev/defs/backboard.json
+++ b/dev/defs/backboard.json
@@ -153,9 +153,9 @@
         "int": 290,
         "string": "toolbaroutputaction"
       },
-      "description": "Primary output action in the toolbar (share, publihs, download)",
+      "description": "Primary output action in the toolbar (share, publish, download)",
       "runtime": false,
       "coop": false
     }
   }
-}
\ No newline at end of file
+}
diff --git a/dev/defs/joystick.json b/dev/defs/joystick.json
index 17137f0..461454e 100644
--- a/dev/defs/joystick.json
+++ b/dev/defs/joystick.json
@@ -118,4 +118,4 @@
       "description": "Identifier used to track the custom handle source of the joystick."
     }
   }
-}
\ No newline at end of file
+}
diff --git a/dev/defs/text/text.json b/dev/defs/text/text.json
index d7cb62d..01868a8 100644
--- a/dev/defs/text/text.json
+++ b/dev/defs/text/text.json
@@ -52,4 +52,4 @@
       "description": "Height of the text object."
     }
   }
-}
\ No newline at end of file
+}
diff --git a/dev/defs/text/text_style.json b/dev/defs/text/text_style.json
index 16624fa..2c31137 100644
--- a/dev/defs/text/text_style.json
+++ b/dev/defs/text/text_style.json
@@ -26,4 +26,4 @@
       }
     }
   }
-}
\ No newline at end of file
+}
diff --git a/dev/generate_core.sh b/dev/generate_core.sh
index c25ec4d..006def7 100755
--- a/dev/generate_core.sh
+++ b/dev/generate_core.sh
@@ -1,3 +1,5 @@
+#!/usr/bin/env bash
+set -e
 
 if [[ ! -f "./bin/core_generator" || "$1" == "build" ]]; then
     mkdir -p ./bin
diff --git a/include/rive/animation/cubic_interpolator_component.hpp b/include/rive/animation/cubic_interpolator_component.hpp
new file mode 100644
index 0000000..ebfd692
--- /dev/null
+++ b/include/rive/animation/cubic_interpolator_component.hpp
@@ -0,0 +1,13 @@
+#ifndef _RIVE_CUBIC_INTERPOLATOR_COMPONENT_HPP_
+#define _RIVE_CUBIC_INTERPOLATOR_COMPONENT_HPP_
+#include "rive/generated/animation/cubic_interpolator_component_base.hpp"
+#include <stdio.h>
+namespace rive
+{
+class CubicInterpolatorComponent : public CubicInterpolatorComponentBase
+{
+public:
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/include/rive/animation/state_transition.hpp b/include/rive/animation/state_transition.hpp
index 3d0529e..5fefb4c 100644
--- a/include/rive/animation/state_transition.hpp
+++ b/include/rive/animation/state_transition.hpp
@@ -1,5 +1,6 @@
 #ifndef _RIVE_STATE_TRANSITION_HPP_
 #define _RIVE_STATE_TRANSITION_HPP_
+#include "rive/animation/cubic_interpolator.hpp"
 #include "rive/animation/state_transition_flags.hpp"
 #include "rive/generated/animation/state_transition_base.hpp"
 #include <stdio.h>
@@ -34,6 +35,7 @@
         return static_cast<StateTransitionFlags>(flags());
     }
     LayerState* m_StateTo = nullptr;
+    CubicInterpolator* m_Interpolator = nullptr;
 
     std::vector<TransitionCondition*> m_Conditions;
     void addCondition(TransitionCondition* condition);
@@ -41,6 +43,7 @@
 public:
     ~StateTransition() override;
     const LayerState* stateTo() const { return m_StateTo; }
+    inline CubicInterpolator* interpolator() const { return m_Interpolator; }
 
     StatusCode onAddedDirty(CoreContext* context) override;
     StatusCode onAddedClean(CoreContext* context) override;
diff --git a/include/rive/generated/animation/state_transition_base.hpp b/include/rive/generated/animation/state_transition_base.hpp
index d50874a..9b526ac 100644
--- a/include/rive/generated/animation/state_transition_base.hpp
+++ b/include/rive/generated/animation/state_transition_base.hpp
@@ -32,12 +32,16 @@
     static const uint16_t flagsPropertyKey = 152;
     static const uint16_t durationPropertyKey = 158;
     static const uint16_t exitTimePropertyKey = 160;
+    static const uint16_t interpolationTypePropertyKey = 349;
+    static const uint16_t interpolatorIdPropertyKey = 350;
 
 private:
     uint32_t m_StateToId = -1;
     uint32_t m_Flags = 0;
     uint32_t m_Duration = 0;
     uint32_t m_ExitTime = 0;
+    uint32_t m_InterpolationType = 1;
+    uint32_t m_InterpolatorId = -1;
 
 public:
     inline uint32_t stateToId() const { return m_StateToId; }
@@ -84,6 +88,28 @@
         exitTimeChanged();
     }
 
+    inline uint32_t interpolationType() const { return m_InterpolationType; }
+    void interpolationType(uint32_t value)
+    {
+        if (m_InterpolationType == value)
+        {
+            return;
+        }
+        m_InterpolationType = value;
+        interpolationTypeChanged();
+    }
+
+    inline uint32_t interpolatorId() const { return m_InterpolatorId; }
+    void interpolatorId(uint32_t value)
+    {
+        if (m_InterpolatorId == value)
+        {
+            return;
+        }
+        m_InterpolatorId = value;
+        interpolatorIdChanged();
+    }
+
     Core* clone() const override;
     void copy(const StateTransitionBase& object)
     {
@@ -91,6 +117,8 @@
         m_Flags = object.m_Flags;
         m_Duration = object.m_Duration;
         m_ExitTime = object.m_ExitTime;
+        m_InterpolationType = object.m_InterpolationType;
+        m_InterpolatorId = object.m_InterpolatorId;
         StateMachineLayerComponent::copy(object);
     }
 
@@ -110,6 +138,12 @@
             case exitTimePropertyKey:
                 m_ExitTime = CoreUintType::deserialize(reader);
                 return true;
+            case interpolationTypePropertyKey:
+                m_InterpolationType = CoreUintType::deserialize(reader);
+                return true;
+            case interpolatorIdPropertyKey:
+                m_InterpolatorId = CoreUintType::deserialize(reader);
+                return true;
         }
         return StateMachineLayerComponent::deserialize(propertyKey, reader);
     }
@@ -119,6 +153,8 @@
     virtual void flagsChanged() {}
     virtual void durationChanged() {}
     virtual void exitTimeChanged() {}
+    virtual void interpolationTypeChanged() {}
+    virtual void interpolatorIdChanged() {}
 };
 } // namespace rive
 
diff --git a/include/rive/generated/core_registry.hpp b/include/rive/generated/core_registry.hpp
index 5ba4c82..7698ca9 100644
--- a/include/rive/generated/core_registry.hpp
+++ b/include/rive/generated/core_registry.hpp
@@ -457,6 +457,12 @@
             case StateTransitionBase::exitTimePropertyKey:
                 object->as<StateTransitionBase>()->exitTime(value);
                 break;
+            case StateTransitionBase::interpolationTypePropertyKey:
+                object->as<StateTransitionBase>()->interpolationType(value);
+                break;
+            case StateTransitionBase::interpolatorIdPropertyKey:
+                object->as<StateTransitionBase>()->interpolatorId(value);
+                break;
             case LinearAnimationBase::fpsPropertyKey:
                 object->as<LinearAnimationBase>()->fps(value);
                 break;
@@ -1043,6 +1049,10 @@
                 return object->as<StateTransitionBase>()->duration();
             case StateTransitionBase::exitTimePropertyKey:
                 return object->as<StateTransitionBase>()->exitTime();
+            case StateTransitionBase::interpolationTypePropertyKey:
+                return object->as<StateTransitionBase>()->interpolationType();
+            case StateTransitionBase::interpolatorIdPropertyKey:
+                return object->as<StateTransitionBase>()->interpolatorId();
             case LinearAnimationBase::fpsPropertyKey:
                 return object->as<LinearAnimationBase>()->fps();
             case LinearAnimationBase::durationPropertyKey:
@@ -1428,6 +1438,8 @@
             case StateTransitionBase::flagsPropertyKey:
             case StateTransitionBase::durationPropertyKey:
             case StateTransitionBase::exitTimePropertyKey:
+            case StateTransitionBase::interpolationTypePropertyKey:
+            case StateTransitionBase::interpolatorIdPropertyKey:
             case LinearAnimationBase::fpsPropertyKey:
             case LinearAnimationBase::durationPropertyKey:
             case LinearAnimationBase::loopValuePropertyKey:
diff --git a/src/animation/state_machine_instance.cpp b/src/animation/state_machine_instance.cpp
index 544aa9a..5a1e00d 100644
--- a/src/animation/state_machine_instance.cpp
+++ b/src/animation/state_machine_instance.cpp
@@ -1,26 +1,27 @@
-#include "rive/animation/state_machine_instance.hpp"
-#include "rive/animation/state_machine_input.hpp"
+#include "rive/animation/animation_state_instance.hpp"
+#include "rive/animation/animation_state.hpp"
+#include "rive/animation/any_state.hpp"
+#include "rive/animation/cubic_interpolator.hpp"
+#include "rive/animation/entry_state.hpp"
+#include "rive/animation/nested_state_machine.hpp"
+#include "rive/animation/state_instance.hpp"
 #include "rive/animation/state_machine_bool.hpp"
+#include "rive/animation/state_machine_input_instance.hpp"
+#include "rive/animation/state_machine_input.hpp"
+#include "rive/animation/state_machine_instance.hpp"
+#include "rive/animation/state_machine_layer.hpp"
+#include "rive/animation/state_machine_listener.hpp"
 #include "rive/animation/state_machine_number.hpp"
 #include "rive/animation/state_machine_trigger.hpp"
-#include "rive/animation/state_machine_input_instance.hpp"
 #include "rive/animation/state_machine.hpp"
-#include "rive/animation/state_machine_layer.hpp"
-#include "rive/animation/any_state.hpp"
-#include "rive/animation/entry_state.hpp"
 #include "rive/animation/state_transition.hpp"
 #include "rive/animation/transition_condition.hpp"
-#include "rive/animation/animation_state.hpp"
-#include "rive/animation/state_instance.hpp"
-#include "rive/animation/animation_state_instance.hpp"
-#include "rive/animation/state_machine_listener.hpp"
-#include "rive/shapes/shape.hpp"
 #include "rive/math/aabb.hpp"
 #include "rive/math/hit_test.hpp"
-#include "rive/nested_artboard.hpp"
 #include "rive/nested_animation.hpp"
-#include "rive/animation/nested_state_machine.hpp"
+#include "rive/nested_artboard.hpp"
 #include "rive/rive_counter.hpp"
+#include "rive/shapes/shape.hpp"
 #include <unordered_map>
 
 using namespace rive;
@@ -233,13 +234,21 @@
             m_HoldAnimation = nullptr;
         }
 
+        CubicInterpolator* cubic = nullptr;
+        if (m_Transition != nullptr && m_Transition->interpolator() != nullptr)
+        {
+            cubic = m_Transition->interpolator();
+        }
+
         if (m_StateFrom != nullptr && m_Mix < 1.0f)
         {
-            m_StateFrom->apply(m_MixFrom);
+            auto fromMix = cubic != nullptr ? cubic->transform(m_MixFrom) : m_MixFrom;
+            m_StateFrom->apply(fromMix);
         }
         if (m_CurrentState != nullptr)
         {
-            m_CurrentState->apply(m_Mix);
+            auto mix = cubic != nullptr ? cubic->transform(m_Mix) : m_Mix;
+            m_CurrentState->apply(mix);
         }
     }
 
diff --git a/src/animation/state_transition.cpp b/src/animation/state_transition.cpp
index 5efa228..208790b 100644
--- a/src/animation/state_transition.cpp
+++ b/src/animation/state_transition.cpp
@@ -1,14 +1,15 @@
-#include "rive/animation/state_transition.hpp"
-#include "rive/importers/import_stack.hpp"
-#include "rive/importers/layer_state_importer.hpp"
-#include "rive/animation/layer_state.hpp"
-#include "rive/animation/transition_condition.hpp"
+#include "rive/animation/animation_state_instance.hpp"
 #include "rive/animation/animation_state.hpp"
+#include "rive/animation/cubic_interpolator.hpp"
+#include "rive/animation/layer_state.hpp"
 #include "rive/animation/linear_animation.hpp"
 #include "rive/animation/state_machine_input_instance.hpp"
 #include "rive/animation/state_machine_trigger.hpp"
-#include "rive/animation/animation_state_instance.hpp"
+#include "rive/animation/state_transition.hpp"
+#include "rive/animation/transition_condition.hpp"
 #include "rive/animation/transition_trigger_condition.hpp"
+#include "rive/importers/import_stack.hpp"
+#include "rive/importers/layer_state_importer.hpp"
 
 using namespace rive;
 
@@ -23,6 +24,17 @@
 StatusCode StateTransition::onAddedDirty(CoreContext* context)
 {
     StatusCode code;
+
+    if (interpolatorId() != -1)
+    {
+        auto coreObject = context->resolve(interpolatorId());
+        if (coreObject == nullptr || !coreObject->is<CubicInterpolator>())
+        {
+            return StatusCode::MissingObject;
+        }
+        m_Interpolator = coreObject->as<CubicInterpolator>();
+    }
+
     for (auto condition : m_Conditions)
     {
         if ((code = condition->onAddedDirty(context)) != StatusCode::Ok)
diff --git a/src/generated/animation/cubic_interpolator_base.cpp b/src/generated/animation/cubic_interpolator_base.cpp
deleted file mode 100644
index 1745e60..0000000
--- a/src/generated/animation/cubic_interpolator_base.cpp
+++ /dev/null
@@ -1,4 +0,0 @@
-#include "rive/generated/animation/cubic_interpolator_base.hpp"
-#include "rive/animation/cubic_interpolator.hpp"
-
-using namespace rive;
\ No newline at end of file