Renderer in editor

Starting to let tests run.

Diffs=
a0a6c0d3b Renderer in editor (#7495)

Co-authored-by: Luigi Rosso <luigi-rosso@users.noreply.github.com>
Co-authored-by: hernan <hernan@rive.app>
diff --git a/.rive_head b/.rive_head
index f234bbf..7510dfe 100644
--- a/.rive_head
+++ b/.rive_head
@@ -1 +1 @@
-876a2dca5228aa6a27e934c3e0f430853a157931
+a0a6c0d3bd385f32dd51e1fbcb75306332b9d1cc
diff --git a/build/premake5.lua b/build/premake5.lua
index 576761c..52dd2a6 100644
--- a/build/premake5.lua
+++ b/build/premake5.lua
@@ -48,7 +48,7 @@
         harfbuzz .. '/src',
         sheenbidi .. '/Headers',
         miniaudio,
-        yoga
+        yoga,
     })
 
     defines({ 'YOGA_EXPORT=' })
@@ -163,11 +163,6 @@
         objdir('%{cfg.system}/arm64/obj/%{cfg.buildcfg}')
     end
 
-    filter('system:emscripten')
-    do
-        buildoptions({ '-pthread' })
-    end
-
     filter('configurations:debug')
     do
         defines({ 'DEBUG' })
@@ -217,4 +212,4 @@
 newoption({
     trigger = 'with_rive_layout',
     description = 'Compiles in layout features.',
-})
\ No newline at end of file
+})
diff --git a/build/rive_build_config.lua b/build/rive_build_config.lua
index f6ff510..b6138c4 100644
--- a/build/rive_build_config.lua
+++ b/build/rive_build_config.lua
@@ -141,7 +141,7 @@
     default = 'default',
 })
 
--- This is just to match our old windows config. Gamekit specifically sets
+-- This is just to match our old windows config. Rive Native specifically sets
 -- static/dynamic and maybe we should do the same elsewhere.
 filter({ 'system:windows', 'options:windows_runtime=default' })
 do
diff --git a/include/rive/animation/nested_state_machine.hpp b/include/rive/animation/nested_state_machine.hpp
index c76729a..8c0476b 100644
--- a/include/rive/animation/nested_state_machine.hpp
+++ b/include/rive/animation/nested_state_machine.hpp
@@ -28,6 +28,9 @@
     HitResult pointerDown(Vec2D position);
     HitResult pointerUp(Vec2D position);
     HitResult pointerExit(Vec2D position);
+#ifdef WITH_RIVE_TOOLS
+    bool hitTest(Vec2D position) const;
+#endif
 
     void addNestedInput(NestedInput* input);
     size_t inputCount() { return m_nestedInputs.size(); }
diff --git a/include/rive/animation/state_machine_input_instance.hpp b/include/rive/animation/state_machine_input_instance.hpp
index e5f732c..8c82a71 100644
--- a/include/rive/animation/state_machine_input_instance.hpp
+++ b/include/rive/animation/state_machine_input_instance.hpp
@@ -20,9 +20,6 @@
     friend class StateMachineLayerInstance;
 
 private:
-    StateMachineInstance* m_MachineInstance;
-    const StateMachineInput* m_Input;
-
     virtual void advanced() {}
 
 protected:
@@ -32,10 +29,17 @@
 
 public:
     virtual ~SMIInput() {}
-    const StateMachineInput* input() const { return m_Input; }
+    const StateMachineInput* input() const { return m_input; }
 
     const std::string& name() const;
     uint16_t inputCoreType() const;
+
+private:
+    StateMachineInstance* m_machineInstance;
+    const StateMachineInput* m_input;
+#ifdef WITH_RIVE_TOOLS
+    uint64_t m_index = 0;
+#endif
 };
 
 class SMIBool : public SMIInput
@@ -72,16 +76,16 @@
     friend class TransitionTriggerCondition;
 
 private:
-    bool m_Fired = false;
+    bool m_fired = false;
 
     SMITrigger(const StateMachineTrigger* input, StateMachineInstance* machineInstance);
-    void advanced() override { m_Fired = false; }
+    void advanced() override { m_fired = false; }
 
 public:
     void fire();
 
 #ifdef TESTING
-    bool didFire() { return m_Fired; }
+    bool didFire() { return m_fired; }
 #endif
 };
 } // namespace rive
diff --git a/include/rive/animation/state_machine_instance.hpp b/include/rive/animation/state_machine_instance.hpp
index 786366f..c59ee17 100644
--- a/include/rive/animation/state_machine_instance.hpp
+++ b/include/rive/animation/state_machine_instance.hpp
@@ -32,6 +32,11 @@
 class KeyedProperty;
 class EventReport;
 
+#ifdef WITH_RIVE_TOOLS
+class StateMachineInstance;
+typedef void (*InputChanged)(StateMachineInstance*, uint64_t);
+#endif
+
 class StateMachineInstance : public Scene, public NestedEventNotifier, public NestedEventListener
 {
     friend class SMIInput;
@@ -92,6 +97,9 @@
     HitResult pointerDown(Vec2D position) override;
     HitResult pointerUp(Vec2D position) override;
     HitResult pointerExit(Vec2D position) override;
+#ifdef WITH_RIVE_TOOLS
+    bool hitTest(Vec2D position) const;
+#endif
 
     float durationSeconds() const override { return -1; }
     Loop loop() const override { return Loop::oneShot; }
@@ -131,6 +139,11 @@
     std::vector<std::unique_ptr<HitComponent>> m_hitComponents;
     StateMachineInstance* m_parentStateMachineInstance = nullptr;
     NestedArtboard* m_parentNestedArtboard = nullptr;
+#ifdef WITH_RIVE_TOOLS
+public:
+    void onInputChanged(InputChanged callback) { m_inputChangedCallback = callback; }
+    InputChanged m_inputChangedCallback = nullptr;
+#endif
 };
 } // namespace rive
 #endif
diff --git a/include/rive/artboard.hpp b/include/rive/artboard.hpp
index 1051af9..85353c1 100644
--- a/include/rive/artboard.hpp
+++ b/include/rive/artboard.hpp
@@ -44,6 +44,10 @@
 class SMINumber;
 class SMITrigger;
 
+#ifdef WITH_RIVE_TOOLS
+typedef void (*ArtboardCallback)(Artboard*);
+#endif
+
 class Artboard : public ArtboardBase, public CoreContext, public ShapePaintContainer
 {
     friend class File;
@@ -135,8 +139,8 @@
     }
 #endif
 
-    bool advance(double elapsedSeconds);
-    bool advanceInternal(double elapsedSeconds, bool isRoot);
+    bool advance(double elapsedSeconds, bool nested = true);
+    bool advanceInternal(double elapsedSeconds, bool isRoot, bool nested = true);
     bool hasChangedDrawOrderInLastUpdate() { return m_HasChangedDrawOrderInLastUpdate; };
     Drawable* firstDrawable() { return m_FirstDrawable; };
 
@@ -161,9 +165,10 @@
     NestedArtboard* nestedArtboard(const std::string& name) const;
     NestedArtboard* nestedArtboardAtPath(const std::string& path) const;
 
-    float originalWidth() { return m_originalWidth; }
-    float originalHeight() { return m_originalHeight; }
-
+    float originalWidth() const { return m_originalWidth; }
+    float originalHeight() const { return m_originalHeight; }
+    float layoutWidth() const;
+    float layoutHeight() const;
     AABB bounds() const;
 
     // Can we hide these from the public? (they use playable)
@@ -341,8 +346,18 @@
     rcp<AudioEngine> audioEngine() const;
     void audioEngine(rcp<AudioEngine> audioEngine);
 #endif
+
+#ifdef WITH_RIVE_LAYOUT
+    void propagateSize() override;
+#endif
 private:
     float m_volume = 1.0f;
+#ifdef WITH_RIVE_TOOLS
+    ArtboardCallback m_layoutChangedCallback = nullptr;
+
+public:
+    void onLayoutChanged(ArtboardCallback callback) { m_layoutChangedCallback = callback; }
+#endif
 };
 
 class ArtboardInstance : public Artboard
diff --git a/include/rive/core/binary_writer.hpp b/include/rive/core/binary_writer.hpp
index 213e295..8d87cee 100644
--- a/include/rive/core/binary_writer.hpp
+++ b/include/rive/core/binary_writer.hpp
@@ -1,4 +1,3 @@
-
 #ifndef _RIVE_CORE_BINARY_WRITER_HPP_
 #define _RIVE_CORE_BINARY_WRITER_HPP_
 
@@ -24,6 +23,8 @@
     void write(const uint8_t* bytes, std::size_t length);
     void write(uint8_t value);
     void writeDouble(double value);
+    void write(uint16_t value);
+    void write(uint32_t value);
     void clear();
 };
 } // namespace rive
diff --git a/include/rive/layout_component.hpp b/include/rive/layout_component.hpp
index 6b6e31a..c070587 100644
--- a/include/rive/layout_component.hpp
+++ b/include/rive/layout_component.hpp
@@ -32,7 +32,7 @@
 
 class LayoutComponent : public LayoutComponentBase
 {
-private:
+protected:
     LayoutComponentStyle* m_style = nullptr;
     std::unique_ptr<LayoutData> m_layoutData;
 
@@ -66,7 +66,7 @@
 #ifdef WITH_RIVE_LAYOUT
     LayoutComponent();
     void syncStyle();
-    void propagateSize();
+    virtual void propagateSize();
     void updateLayoutBounds();
     void update(ComponentDirt value) override;
     StatusCode onAddedDirty(CoreContext* context) override;
diff --git a/include/rive/nested_animation.hpp b/include/rive/nested_animation.hpp
index fc31df6..70edc4d 100644
--- a/include/rive/nested_animation.hpp
+++ b/include/rive/nested_animation.hpp
@@ -35,17 +35,14 @@
 
     void notifyListeners(const std::vector<Event*>& events)
     {
-        if (m_nestedArtboard != nullptr)
+        std::vector<EventReport> eventReports;
+        for (auto event : events)
         {
-            std::vector<EventReport> eventReports;
-            for (auto event : events)
-            {
-                eventReports.push_back(EventReport(event, 0));
-            }
-            for (auto listener : m_nestedEventListeners)
-            {
-                listener->notify(eventReports, m_nestedArtboard);
-            }
+            eventReports.push_back(EventReport(event, 0));
+        }
+        for (auto listener : m_nestedEventListeners)
+        {
+            listener->notify(eventReports, m_nestedArtboard);
         }
     }
 
diff --git a/include/rive/shapes/paint/trim_path.hpp b/include/rive/shapes/paint/trim_path.hpp
index d937481..150af98 100644
--- a/include/rive/shapes/paint/trim_path.hpp
+++ b/include/rive/shapes/paint/trim_path.hpp
@@ -8,13 +8,12 @@
 
 namespace rive
 {
-enum class TrimPathMode : unsigned char
+enum class TrimPathMode : uint8_t
 {
     sequential = 1,
     synchronized = 2
 
 };
-class ContourMeasure;
 
 class TrimPath : public TrimPathBase, public StrokeEffect
 {
diff --git a/src/animation/nested_state_machine.cpp b/src/animation/nested_state_machine.cpp
index f973429..6045b56 100644
--- a/src/animation/nested_state_machine.cpp
+++ b/src/animation/nested_state_machine.cpp
@@ -39,6 +39,17 @@
     return m_StateMachineInstance.get();
 }
 
+#ifdef WITH_RIVE_TOOLS
+bool NestedStateMachine::hitTest(Vec2D position) const
+{
+    if (m_StateMachineInstance != nullptr)
+    {
+        return m_StateMachineInstance->hitTest(position);
+    }
+    return false;
+}
+#endif
+
 HitResult NestedStateMachine::pointerMove(Vec2D position)
 {
     if (m_StateMachineInstance != nullptr)
diff --git a/src/animation/state_machine_input_instance.cpp b/src/animation/state_machine_input_instance.cpp
index 5c2fb33..d2e98d6 100644
--- a/src/animation/state_machine_input_instance.cpp
+++ b/src/animation/state_machine_input_instance.cpp
@@ -7,14 +7,24 @@
 using namespace rive;
 
 SMIInput::SMIInput(const StateMachineInput* input, StateMachineInstance* machineInstance) :
-    m_MachineInstance(machineInstance), m_Input(input)
+    m_machineInstance(machineInstance), m_input(input)
 {}
 
-uint16_t SMIInput::inputCoreType() const { return m_Input->coreType(); }
+uint16_t SMIInput::inputCoreType() const { return m_input->coreType(); }
 
-const std::string& SMIInput::name() const { return m_Input->name(); }
+const std::string& SMIInput::name() const { return m_input->name(); }
 
-void SMIInput::valueChanged() { m_MachineInstance->markNeedsAdvance(); }
+void SMIInput::valueChanged()
+{
+    m_machineInstance->markNeedsAdvance();
+#ifdef WITH_RIVE_TOOLS
+    auto callback = m_machineInstance->m_inputChangedCallback;
+    if (callback != nullptr)
+    {
+        callback(m_machineInstance, m_index);
+    }
+#endif
+}
 
 // bool
 
@@ -54,10 +64,10 @@
 
 void SMITrigger::fire()
 {
-    if (m_Fired)
+    if (m_fired)
     {
         return;
     }
-    m_Fired = true;
+    m_fired = true;
     valueChanged();
 }
diff --git a/src/animation/state_machine_instance.cpp b/src/animation/state_machine_instance.cpp
index c6f7316..d564b87 100644
--- a/src/animation/state_machine_instance.cpp
+++ b/src/animation/state_machine_instance.cpp
@@ -430,6 +430,9 @@
     {}
     virtual ~HitComponent() {}
     virtual HitResult processEvent(Vec2D position, ListenerType hitType, bool canHit) = 0;
+#ifdef WITH_RIVE_TOOLS
+    virtual bool hitTest(Vec2D position) const = 0;
+#endif
 
 protected:
     Component* m_component;
@@ -451,8 +454,10 @@
     std::vector<const StateMachineListener*> listeners;
 
     bool hitTest(Vec2D position) const
+#ifdef WITH_RIVE_TOOLS
+        override
+#endif
     {
-
         auto shape = m_component->as<Shape>();
         auto worldBounds = shape->worldBounds();
         if (!worldBounds.contains(position))
@@ -516,6 +521,36 @@
         HitComponent(nestedArtboard, stateMachineInstance)
     {}
     ~HitNestedArtboard() override {}
+
+#ifdef WITH_RIVE_TOOLS
+    bool hitTest(Vec2D position) const override
+    {
+        auto nestedArtboard = m_component->as<NestedArtboard>();
+        if (nestedArtboard->isCollapsed())
+        {
+            return false;
+        }
+        Vec2D nestedPosition;
+        if (!nestedArtboard->worldToLocal(position, &nestedPosition))
+        {
+            // Mounted artboard isn't ready or has a 0 scale transform.
+            return false;
+        }
+
+        for (auto nestedAnimation : nestedArtboard->nestedAnimations())
+        {
+            if (nestedAnimation->is<NestedStateMachine>())
+            {
+                auto nestedStateMachine = nestedAnimation->as<NestedStateMachine>();
+                if (nestedStateMachine->hitTest(nestedPosition))
+                {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+#endif
     HitResult processEvent(Vec2D position, ListenerType hitType, bool canHit) override
     {
         auto nestedArtboard = m_component->as<NestedArtboard>();
@@ -589,7 +624,6 @@
     bool hitOpaque = false;
     for (const auto& hitShape : m_hitComponents)
     {
-
         // TODO: quick reject.
 
         HitResult hitResult = hitShape->processEvent(position, hitType, !hitOpaque);
@@ -605,6 +639,28 @@
     return hitSomething ? hitOpaque ? HitResult::hitOpaque : HitResult::hit : HitResult::none;
 }
 
+#ifdef WITH_RIVE_TOOLS
+bool StateMachineInstance::hitTest(Vec2D position) const
+{
+    if (m_artboardInstance->frameOrigin())
+    {
+        position -= Vec2D(m_artboardInstance->originX() * m_artboardInstance->width(),
+                          m_artboardInstance->originY() * m_artboardInstance->height());
+    }
+
+    for (const auto& hitShape : m_hitComponents)
+    {
+        // TODO: quick reject.
+
+        if (hitShape->hitTest(position))
+        {
+            return true;
+        }
+    }
+    return false;
+}
+#endif
+
 HitResult StateMachineInstance::pointerMove(Vec2D position)
 {
     return updateListeners(position, ListenerType::move);
@@ -650,6 +706,13 @@
                 // Sanity check.
                 break;
         }
+#ifdef WITH_RIVE_TOOLS
+        auto instance = m_inputInstances[i];
+        if (instance != nullptr)
+        {
+            instance->m_index = i;
+        }
+#endif
     }
 
     m_layerCount = machine->layerCount();
@@ -958,12 +1021,9 @@
             }
         }
         // Bubble the event up to parent artboard state machines immediately
-        if (nestedArtboard() != nullptr)
+        for (auto listener : nestedEventListeners())
         {
-            for (auto listener : nestedEventListeners())
-            {
-                listener->notify(events, nestedArtboard());
-            }
+            listener->notify(events, nestedArtboard());
         }
 
         for (auto report : events)
diff --git a/src/animation/transition_trigger_condition.cpp b/src/animation/transition_trigger_condition.cpp
index a9ed142..86391fe 100644
--- a/src/animation/transition_trigger_condition.cpp
+++ b/src/animation/transition_trigger_condition.cpp
@@ -21,7 +21,7 @@
     }
     auto triggerInput = static_cast<const SMITrigger*>(inputInstance);
 
-    if (triggerInput->m_Fired)
+    if (triggerInput->m_fired)
     {
         return true;
     }
diff --git a/src/artboard.cpp b/src/artboard.cpp
index 25aa369..a72cba2 100644
--- a/src/artboard.cpp
+++ b/src/artboard.cpp
@@ -86,6 +86,8 @@
     // these will be re-built in update() -- are they needed here?
     m_BackgroundPath = factory()->makeEmptyRenderPath();
     m_ClipPath = factory()->makeEmptyRenderPath();
+    m_layoutSizeWidth = width();
+    m_layoutSizeHeight = height();
 
 #ifdef WITH_RIVE_LAYOUT
     markLayoutDirty(this);
@@ -444,6 +446,37 @@
 
 void Artboard::onDirty(ComponentDirt dirt) { m_Dirt |= ComponentDirt::Components; }
 
+#ifdef WITH_RIVE_LAYOUT
+void Artboard::propagateSize()
+{
+    addDirt(ComponentDirt::Path);
+#ifdef WITH_RIVE_TOOLS
+    if (m_layoutChangedCallback != nullptr)
+    {
+        m_layoutChangedCallback(this);
+    }
+#endif
+}
+#endif
+
+float Artboard::layoutWidth() const
+{
+#ifdef WITH_RIVE_LAYOUT
+    return m_layoutSizeWidth;
+#else
+    return width();
+#endif
+}
+
+float Artboard::layoutHeight() const
+{
+#ifdef WITH_RIVE_LAYOUT
+    return m_layoutSizeHeight;
+#else
+    return height();
+#endif
+}
+
 void Artboard::update(ComponentDirt value)
 {
     if (hasDirt(value, ComponentDirt::DrawOrder))
@@ -452,11 +485,14 @@
     }
     if (hasDirt(value, ComponentDirt::Path))
     {
-        AABB bg = AABB::fromLTWH(-width() * originX(), -height() * originY(), width(), height());
+        AABB bg = AABB::fromLTWH(-layoutWidth() * originX(),
+                                 -layoutHeight() * originY(),
+                                 layoutWidth(),
+                                 layoutHeight());
         AABB clip;
         if (m_FrameOrigin)
         {
-            clip = {0.0f, 0.0f, width(), height()};
+            clip = {0.0f, 0.0f, layoutWidth(), layoutHeight()};
         }
         else
         {
@@ -530,7 +566,7 @@
     return false;
 }
 
-bool Artboard::advanceInternal(double elapsedSeconds, bool isRoot)
+bool Artboard::advanceInternal(double elapsedSeconds, bool isRoot, bool nested)
 {
     bool didUpdate = false;
     m_HasChangedDrawOrderInLastUpdate = false;
@@ -604,17 +640,23 @@
             didUpdate = true;
         }
     }
-    for (auto nestedArtboard : m_NestedArtboards)
+    if (nested)
     {
-        if (nestedArtboard->advance((float)elapsedSeconds))
+        for (auto nestedArtboard : m_NestedArtboards)
         {
-            didUpdate = true;
+            if (nestedArtboard->advance((float)elapsedSeconds))
+            {
+                didUpdate = true;
+            }
         }
     }
     return didUpdate;
 }
 
-bool Artboard::advance(double elapsedSeconds) { return advanceInternal(elapsedSeconds, true); }
+bool Artboard::advance(double elapsedSeconds, bool nested)
+{
+    return advanceInternal(elapsedSeconds, true, nested);
+}
 
 Core* Artboard::hitTest(HitInfo* hinfo, const Mat2D* xform)
 {
@@ -626,7 +668,7 @@
     auto mx = xform ? *xform : Mat2D();
     if (m_FrameOrigin)
     {
-        mx *= Mat2D::fromTranslate(width() * originX(), height() * originY());
+        mx *= Mat2D::fromTranslate(layoutWidth() * originX(), layoutHeight() * originY());
     }
 
     Drawable* last = m_FirstDrawable;
@@ -666,8 +708,8 @@
     if (m_FrameOrigin)
     {
         Mat2D artboardTransform;
-        artboardTransform[4] = width() * originX();
-        artboardTransform[5] = height() * originY();
+        artboardTransform[4] = layoutWidth() * originX();
+        artboardTransform[5] = layoutHeight() * originY();
         renderer->transform(artboardTransform);
     }
 
@@ -709,9 +751,11 @@
 
 AABB Artboard::bounds() const
 {
-    return m_FrameOrigin
-               ? AABB(0.0f, 0.0f, width(), height())
-               : AABB::fromLTWH(-width() * originX(), -height() * originY(), width(), height());
+    return m_FrameOrigin ? AABB(0.0f, 0.0f, layoutWidth(), layoutHeight())
+                         : AABB::fromLTWH(-layoutWidth() * originX(),
+                                          -layoutHeight() * originY(),
+                                          layoutWidth(),
+                                          layoutHeight());
 }
 
 bool Artboard::isTranslucent() const
diff --git a/src/core/binary_writer.cpp b/src/core/binary_writer.cpp
index ef0fd68..323e586 100644
--- a/src/core/binary_writer.cpp
+++ b/src/core/binary_writer.cpp
@@ -100,8 +100,6 @@
     m_Stream->write(bytes, length);
 }
 
-void BinaryWriter::write(uint8_t value) { m_Stream->write(&value, 1); }
-
 void BinaryWriter::writeDouble(double value)
 {
     auto bytes = reinterpret_cast<uint8_t*>(&value);
@@ -117,4 +115,10 @@
     }
 }
 
+void BinaryWriter::write(uint8_t value) { m_Stream->write((const uint8_t*)&value, 1); }
+
+void BinaryWriter::write(uint16_t value) { m_Stream->write((const uint8_t*)&value, 2); }
+
+void BinaryWriter::write(uint32_t value) { m_Stream->write((const uint8_t*)&value, 4); }
+
 void BinaryWriter::clear() { m_Stream->clear(); }
\ No newline at end of file
diff --git a/src/layout_component.cpp b/src/layout_component.cpp
index 2f393e2..350b7cc 100644
--- a/src/layout_component.cpp
+++ b/src/layout_component.cpp
@@ -236,7 +236,6 @@
         YGValue{m_style->maxWidth(), m_style->maxWidthUnits()};
     ygStyle.maxDimensions()[YGDimensionHeight] =
         YGValue{m_style->maxHeight(), m_style->maxHeightUnits()};
-
     ygStyle.gap()[YGGutterColumn] =
         YGValue{m_style->gapHorizontal(), m_style->gapHorizontalUnits()};
     ygStyle.gap()[YGGutterRow] = YGValue{m_style->gapVertical(), m_style->gapVerticalUnits()};
diff --git a/src/shapes/paint/trim_path.cpp b/src/shapes/paint/trim_path.cpp
index 6b0e334..8cdabd7 100644
--- a/src/shapes/paint/trim_path.cpp
+++ b/src/shapes/paint/trim_path.cpp
@@ -146,9 +146,12 @@
 void TrimPath::invalidateTrim()
 {
     m_renderPath = nullptr;
-    auto stroke = parent()->as<Stroke>();
-    stroke->parent()->addDirt(ComponentDirt::Paint);
-    stroke->invalidateRendering();
+    if (parent() != nullptr)
+    {
+        auto stroke = parent()->as<Stroke>();
+        stroke->parent()->addDirt(ComponentDirt::Paint);
+        stroke->invalidateRendering();
+    }
 }
 
 void TrimPath::startChanged() { invalidateTrim(); }
diff --git a/test/clip_test.cpp b/test/clip_test.cpp
index d31a3dd..d4dbdeb 100644
--- a/test/clip_test.cpp
+++ b/test/clip_test.cpp
@@ -60,7 +60,7 @@
 
     auto artboard = file->artboard("Center");
     REQUIRE(artboard != nullptr);
-    artboard->updateComponents();
+    artboard->advance(0.0f);
     REQUIRE(artboard->originX() == 0.5);
     REQUIRE(artboard->originY() == 0.5);
     {