blob: 0855badcca8b9778ecb98d9a7432ac53c48ac44d [file] [log] [blame]
#include "rive/nested_artboard.hpp"
#include "rive/artboard.hpp"
#include "rive/backboard.hpp"
#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;
NestedArtboard::NestedArtboard() {}
NestedArtboard::~NestedArtboard() {}
Core* NestedArtboard::clone() const {
NestedArtboard* nestedArtboard = static_cast<NestedArtboard*>(NestedArtboardBase::clone());
if (m_Artboard == nullptr) {
return nestedArtboard;
}
auto ni = m_Artboard->instance();
nestedArtboard->nest(ni.release());
return nestedArtboard;
}
void NestedArtboard::nest(Artboard* artboard) {
assert(artboard != nullptr);
m_Artboard = artboard;
m_Artboard->frameOrigin(false);
m_Artboard->opacity(renderOpacity());
m_Instance = nullptr;
if (artboard->isInstance()) {
m_Instance.reset(static_cast<ArtboardInstance*>(artboard)); // take ownership
}
m_Artboard->advance(0.0f);
}
static Mat2D makeTranslate(const Artboard* artboard) {
return Mat2D::fromTranslate(-artboard->originX() * artboard->width(),
-artboard->originY() * artboard->height());
}
void NestedArtboard::draw(Renderer* renderer) {
if (m_Artboard == nullptr) {
return;
}
if (!clip(renderer)) {
// We didn't clip, so make sure to save as we'll be doing some
// transformations.
renderer->save();
}
renderer->transform(worldTransform());
m_Artboard->draw(renderer);
renderer->restore();
}
Core* NestedArtboard::hitTest(HitInfo* hinfo, const Mat2D& xform) {
if (m_Artboard == nullptr) {
return nullptr;
}
hinfo->mounts.push_back(this);
auto mx = xform * worldTransform() * makeTranslate(m_Artboard);
if (auto c = m_Artboard->hitTest(hinfo, &mx)) {
return c;
}
hinfo->mounts.pop_back();
return nullptr;
}
StatusCode NestedArtboard::import(ImportStack& importStack) {
auto backboardImporter = importStack.latest<BackboardImporter>(Backboard::typeKey);
if (backboardImporter == nullptr) {
return StatusCode::MissingObject;
}
backboardImporter->addNestedArtboard(this);
return Super::import(importStack);
}
void NestedArtboard::addNestedAnimation(NestedAnimation* nestedAnimation) {
m_NestedAnimations.push_back(nestedAnimation);
}
StatusCode NestedArtboard::onAddedClean(CoreContext* context) {
// N.B. The nested instance will be null here for the source artboards.
// Instances will have a nestedInstance available. This is a good thing as
// it ensures that we only instance animations in artboard instances. It
// does require that we always use an artboard instance (not just the source
// artboard) when working with nested artboards, but in general this is good
// practice for any loaded Rive file.
assert(m_Artboard == nullptr || m_Artboard == m_Instance.get());
if (m_Instance) {
for (auto animation : m_NestedAnimations) {
animation->initializeAnimation(m_Instance.get());
}
}
return Super::onAddedClean(context);
}
bool NestedArtboard::advance(float elapsedSeconds) {
if (m_Artboard == nullptr) {
return false;
}
for (auto animation : m_NestedAnimations) {
animation->advance(elapsedSeconds);
}
return m_Artboard->advance(elapsedSeconds);
}
void NestedArtboard::update(ComponentDirt value) {
Super::update(value);
if (hasDirt(value, ComponentDirt::RenderOpacity) && m_Artboard != nullptr) {
m_Artboard->opacity(renderOpacity());
}
}
bool NestedArtboard::hasNestedStateMachines() const {
for (auto animation : m_NestedAnimations) {
if (animation->is<NestedStateMachine>()) {
return true;
}
}
return false;
}
Span<NestedAnimation*> NestedArtboard::nestedAnimations() { return toSpan(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;
}