Add support for nested state machines and nested hit tests.
diff --git a/include/rive/animation/nested_state_machine.hpp b/include/rive/animation/nested_state_machine.hpp
index aa30f02..125e83f 100644
--- a/include/rive/animation/nested_state_machine.hpp
+++ b/include/rive/animation/nested_state_machine.hpp
@@ -1,14 +1,26 @@
#ifndef _RIVE_NESTED_STATE_MACHINE_HPP_
#define _RIVE_NESTED_STATE_MACHINE_HPP_
#include "rive/generated/animation/nested_state_machine_base.hpp"
-#include <stdio.h>
+#include "rive/math/vec2d.hpp"
+#include <memory>
+
namespace rive {
class ArtboardInstance;
-
+ class StateMachineInstance;
class NestedStateMachine : public NestedStateMachineBase {
+ private:
+ std::unique_ptr<StateMachineInstance> m_StateMachineInstance;
+
public:
+ NestedStateMachine();
+ ~NestedStateMachine() override;
void advance(float elapsedSeconds) override;
void initializeAnimation(ArtboardInstance*) override;
+ StateMachineInstance* stateMachineInstance();
+
+ void pointerMove(Vec2D position);
+ void pointerDown(Vec2D position);
+ void pointerUp(Vec2D position);
};
} // namespace rive
diff --git a/include/rive/animation/state_machine_instance.hpp b/include/rive/animation/state_machine_instance.hpp
index 6f47a50..5b05aa6 100644
--- a/include/rive/animation/state_machine_instance.hpp
+++ b/include/rive/animation/state_machine_instance.hpp
@@ -19,6 +19,7 @@
class Shape;
class StateMachineLayerInstance;
class HitShape;
+ class NestedArtboard;
class StateMachineInstance : public Scene {
friend class SMIInput;
@@ -27,13 +28,15 @@
const StateMachine* m_Machine;
bool m_NeedsAdvance = false;
- std::vector<SMIInput*> m_InputInstances; // we own each pointer
+ std::vector<SMIInput*> m_InputInstances; // we own each pointer
size_t m_LayerCount;
StateMachineLayerInstance* m_Layers;
void markNeedsAdvance();
std::vector<std::unique_ptr<HitShape>> m_HitShapes;
+ std::vector<NestedArtboard*> m_HitNestedArtboards;
+
/// Provide a hitEvent if you want to process a down or an up for the pointer position too.
void processEvent(Vec2D position, EventType hitEvent = EventType::updateHover);
diff --git a/include/rive/artboard.hpp b/include/rive/artboard.hpp
index 34def2f..f7cb07b 100644
--- a/include/rive/artboard.hpp
+++ b/include/rive/artboard.hpp
@@ -99,6 +99,7 @@
CommandPath* backgroundPath() const { return m_BackgroundPath.get(); }
const std::vector<Core*>& objects() const { return m_Objects; }
+ const std::vector<NestedArtboard*> nestedArtboards() const { return m_NestedArtboards; }
AABB bounds() const;
diff --git a/include/rive/nested_artboard.hpp b/include/rive/nested_artboard.hpp
index 71e1c13..1fdc746 100644
--- a/include/rive/nested_artboard.hpp
+++ b/include/rive/nested_artboard.hpp
@@ -29,6 +29,15 @@
Core* clone() const override;
bool advance(float elapsedSeconds);
void update(ComponentDirt value) override;
+
+ bool hasNestedStateMachines() const;
+ const std::vector<NestedAnimation*>& nestedAnimations() const;
+
+ /// Convert a world space (relative to the artboard that this
+ /// NestedArtboard is a child of) to the local space of the Artboard
+ /// nested within. Returns true when the conversion succeeds, and false
+ /// when one is not possible.
+ bool worldToLocal(Vec2D world, Vec2D* local);
};
} // namespace rive
diff --git a/src/animation/nested_state_machine.cpp b/src/animation/nested_state_machine.cpp
index 139572a..c453867 100644
--- a/src/animation/nested_state_machine.cpp
+++ b/src/animation/nested_state_machine.cpp
@@ -1,7 +1,39 @@
#include "rive/animation/nested_state_machine.hpp"
+#include "rive/animation/state_machine_instance.hpp"
using namespace rive;
-void NestedStateMachine::advance(float elapsedSeconds) {}
+NestedStateMachine::NestedStateMachine() {}
+NestedStateMachine::~NestedStateMachine() {}
-void NestedStateMachine::initializeAnimation(ArtboardInstance*) {}
\ No newline at end of file
+void NestedStateMachine::advance(float elapsedSeconds) {
+ if (m_StateMachineInstance != nullptr) {
+ m_StateMachineInstance->advance(elapsedSeconds);
+ }
+}
+
+void NestedStateMachine::initializeAnimation(ArtboardInstance* artboard) {
+ m_StateMachineInstance = artboard->stateMachineAt(animationId());
+}
+
+StateMachineInstance* NestedStateMachine::stateMachineInstance() {
+ return m_StateMachineInstance.get();
+}
+
+void NestedStateMachine::pointerMove(Vec2D position) {
+ if (m_StateMachineInstance != nullptr) {
+ m_StateMachineInstance->pointerMove(position);
+ }
+}
+
+void NestedStateMachine::pointerDown(Vec2D position) {
+ if (m_StateMachineInstance != nullptr) {
+ m_StateMachineInstance->pointerDown(position);
+ }
+}
+
+void NestedStateMachine::pointerUp(Vec2D position) {
+ if (m_StateMachineInstance != nullptr) {
+ m_StateMachineInstance->pointerUp(position);
+ }
+}
\ No newline at end of file
diff --git a/src/animation/state_machine_instance.cpp b/src/animation/state_machine_instance.cpp
index e6d2a60..bbf40dd 100644
--- a/src/animation/state_machine_instance.cpp
+++ b/src/animation/state_machine_instance.cpp
@@ -17,6 +17,9 @@
#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 <unordered_map>
using namespace rive;
@@ -237,8 +240,10 @@
} // namespace rive
void StateMachineInstance::processEvent(Vec2D position, EventType hitEvent) {
- position -= Vec2D(m_ArtboardInstance->originX() * m_ArtboardInstance->width(),
- m_ArtboardInstance->originY() * m_ArtboardInstance->height());
+ if (m_ArtboardInstance->frameOrigin()) {
+ position -= Vec2D(m_ArtboardInstance->originX() * m_ArtboardInstance->width(),
+ m_ArtboardInstance->originY() * m_ArtboardInstance->height());
+ }
const float hitRadius = 2;
auto hitArea = AABB(position.x - hitRadius,
@@ -275,6 +280,41 @@
}
}
}
+
+ // TODO: store a hittable abstraction for HitShape and NestedArtboard that
+ // can be sorted by drawOrder so they can be iterated in one loop and early
+ // out if any hit stops propagation (also require the ability to mark a hit
+ // as able to stop propagation)
+ for (auto nestedArtboard : m_HitNestedArtboards) {
+ Vec2D nestedPosition;
+ if (!nestedArtboard->worldToLocal(position, &nestedPosition)) {
+ // Mounted artboard isn't ready or has a 0 scale transform.
+ continue;
+ }
+ // var nestedPosition = nestedArtboard.worldToLocal(position);
+ // if (nestedPosition == null) {
+ // // Mounted artboard isn't ready or has a 0 scale transform.
+ // continue;
+ // }
+ for (auto nestedAnimation : nestedArtboard->nestedAnimations()) {
+ if (nestedAnimation->is<NestedStateMachine>()) {
+ auto nestedStateMachine = nestedAnimation->as<NestedStateMachine>();
+ switch (hitEvent) {
+ case EventType::down:
+ nestedStateMachine->pointerDown(nestedPosition);
+ break;
+ case EventType::up:
+ nestedStateMachine->pointerUp(nestedPosition);
+ break;
+ case EventType::updateHover:
+ nestedStateMachine->pointerMove(nestedPosition);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
}
void StateMachineInstance::pointerMove(Vec2D position) {
@@ -343,6 +383,12 @@
hitShape->events.push_back(event);
}
}
+
+ for (auto nestedArtboard : instance->nestedArtboards()) {
+ if (nestedArtboard->hasNestedStateMachines()) {
+ m_HitNestedArtboards.push_back(nestedArtboard);
+ }
+ }
}
StateMachineInstance::~StateMachineInstance() {
diff --git a/src/nested_artboard.cpp b/src/nested_artboard.cpp
index 6bd13d8..0e26962 100644
--- a/src/nested_artboard.cpp
+++ b/src/nested_artboard.cpp
@@ -4,6 +4,8 @@
#include "rive/importers/import_stack.hpp"
#include "rive/importers/backboard_importer.hpp"
#include "rive/nested_animation.hpp"
+#include "rive/animation/nested_state_machine.hpp"
+#include <cassert>
using namespace rive;
@@ -24,6 +26,7 @@
assert(artboard != nullptr);
m_Artboard = artboard;
+ m_Artboard->frameOrigin(false);
m_Instance = nullptr;
if (artboard->isInstance()) {
m_Instance.reset(static_cast<ArtboardInstance*>(artboard)); // take ownership
@@ -45,7 +48,7 @@
// transformations.
renderer->save();
}
- renderer->transform(worldTransform() * makeTranslate(m_Artboard));
+ renderer->transform(worldTransform());
m_Artboard->draw(renderer);
renderer->restore();
}
@@ -110,3 +113,31 @@
m_Artboard->opacity(renderOpacity());
}
}
+
+bool NestedArtboard::hasNestedStateMachines() const {
+ for (auto animation : m_NestedAnimations) {
+ if (animation->is<NestedStateMachine>()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+const std::vector<NestedAnimation*>& NestedArtboard::nestedAnimations() const {
+ return m_NestedAnimations;
+}
+
+bool NestedArtboard::worldToLocal(Vec2D world, Vec2D* local) {
+ assert(local != nullptr);
+ if (m_Artboard == nullptr) {
+ return false;
+ }
+ Mat2D toMountedArtboard;
+ if (!worldTransform().invert(&toMountedArtboard)) {
+ return false;
+ }
+
+ *local = toMountedArtboard * world;
+
+ return true;
+}
\ No newline at end of file