blob: bfbd526a11e93ba6983591a698f6a546818966af [file] [log] [blame]
#include <rive/solo.hpp>
#include <rive/nested_artboard.hpp>
#include <rive/shapes/rectangle.hpp>
#include <rive/shapes/shape.hpp>
#include <rive/animation/state_machine_instance.hpp>
#include <rive/animation/nested_linear_animation.hpp>
#include <rive/animation/nested_state_machine.hpp>
#include <rive/viewmodel/viewmodel_instance_number.hpp>
#include "rive_file_reader.hpp"
#include "rive_testing.hpp"
#include "utils/serializing_factory.hpp"
#include <catch.hpp>
#include <cstdio>
#include <iostream>
TEST_CASE("collapsed nested artboards do not advance", "[solo]")
{
auto file = ReadRiveFile("assets/solos_with_nested_artboards.riv");
auto artboard = file->artboard("main-artboard")->instance();
artboard->advance(0.0f);
auto solos = artboard->find<rive::Solo>();
REQUIRE(solos.size() == 1);
auto stateMachine = artboard->stateMachineAt(0);
stateMachine->advanceAndApply(0.0f);
artboard->advance(0.75f);
// Testing whether the squares have moved in the artboard is an indirect way
// if checking whether the time of each artboard has advanced
// Unfortunately there is no way of accessing the time of the animations
// directly
auto redNestedArtboard =
stateMachine->artboard()->find<rive::NestedArtboard>("red-artboard");
auto redNestedArtboardArtboard = redNestedArtboard->artboardInstance();
auto movingShapes = redNestedArtboardArtboard->find<rive::Shape>();
auto redRect = movingShapes.at(0);
REQUIRE(redRect->x() > 50);
auto greenNestedArtboard =
stateMachine->artboard()->find<rive::NestedArtboard>("green-artboard");
auto greenNestedArtboardArtboard = greenNestedArtboard->artboardInstance();
auto greenMovingShapes = greenNestedArtboardArtboard->find<rive::Shape>();
auto greenRect = greenMovingShapes.at(0);
REQUIRE(greenRect->x() == 50);
}
TEST_CASE("nested artboards with looping animations will keep main "
"advanceAndApply advancing",
"[nested]")
{
auto file = ReadRiveFile("assets/ball_test.riv");
auto artboard = file->artboard("Artboard")->instance();
artboard->advance(0.0f);
auto stateMachine = artboard->stateMachineAt(0);
REQUIRE(stateMachine->advanceAndApply(0.0f) == true);
REQUIRE(stateMachine->advanceAndApply(1.0f) == true);
REQUIRE(stateMachine->advanceAndApply(1.0f) == true);
}
TEST_CASE("nested artboards with one shot animations will not main "
"advanceAndApply advancing",
"[nested]")
{
auto file = ReadRiveFile("assets/ball_test.riv");
auto artboard = file->artboard("Artboard 2")->instance();
artboard->advance(0.0f);
auto stateMachine = artboard->stateMachineAt(0);
REQUIRE(stateMachine->advanceAndApply(0.0f) == true);
REQUIRE(stateMachine->advanceAndApply(0.9f) == true);
REQUIRE(stateMachine->advanceAndApply(0.1f) == true);
// nested artboards animation is 1s long but has an event to complete
REQUIRE(stateMachine->advanceAndApply(0.1f) == true);
// animation has ended and no events to report
REQUIRE(stateMachine->advanceAndApply(0.1f) == false);
}
TEST_CASE(
"nested artboard remapped timelines controlled by a timeline controlled "
"itself by a joystick correctly apply the time after the joystick ran",
"[nested]")
{
auto file = ReadRiveFile("assets/joystick_nested_remap.riv");
auto artboard = file->artboard("parent")->instance();
artboard->advance(0.0f);
auto stateMachine = artboard->stateMachineAt(0);
REQUIRE(artboard->find<rive::NestedArtboard>("child") != nullptr);
auto child = artboard->find<rive::NestedArtboard>("child");
auto nestedArtboardArtboard = child->artboardInstance();
REQUIRE(nestedArtboardArtboard != nullptr);
auto rect = nestedArtboardArtboard->find<rive::Shape>("rect");
REQUIRE(rect != nullptr);
REQUIRE(rect->x() == 250.0f);
}
TEST_CASE("Nested events don't conflict with parent events.", "[silver]")
{
rive::SerializingFactory silver;
auto file = ReadRiveFile("assets/nested_events.riv", &silver);
auto artboard = file->artboardDefault();
silver.frameSize(artboard->width(), artboard->height());
REQUIRE(artboard != nullptr);
auto stateMachine = artboard->stateMachineAt(0);
int viewModelId = artboard.get()->viewModelId();
auto vmi = viewModelId == -1
? file->createViewModelInstance(artboard.get())
: file->createViewModelInstance(viewModelId, 0);
stateMachine->bindViewModelInstance(vmi);
stateMachine->advanceAndApply(0.1f);
auto renderer = silver.makeRenderer();
artboard->draw(renderer.get());
// Pointer down on left top square, triggers child event 1
silver.addFrame();
stateMachine->pointerDown(rive::Vec2D(50.0f, 50.0f));
// Advance and apply twice to take the transition and apply the next state.
stateMachine->advanceAndApply(0.1f);
stateMachine->advanceAndApply(1.0f);
artboard->draw(renderer.get());
// Pointer down on right top square, triggers child event 2
silver.addFrame();
stateMachine->pointerDown(rive::Vec2D(150.0f, 50.0f));
// Advance and apply twice to take the transition and apply the next state.
stateMachine->advanceAndApply(0.1f);
stateMachine->advanceAndApply(1.0f);
artboard->draw(renderer.get());
// Pointer down on left bottom square, triggers parent event 1
silver.addFrame();
stateMachine->pointerDown(rive::Vec2D(50.0f, 150.0f));
// Advance and apply twice to take the transition and apply the next state.
stateMachine->advanceAndApply(0.1f);
stateMachine->advanceAndApply(1.0f);
artboard->draw(renderer.get());
// Pointer down on right bottom square, triggers parent event 1
silver.addFrame();
stateMachine->pointerDown(rive::Vec2D(150.0f, 150.0f));
// Advance and apply twice to take the transition and apply the next state.
stateMachine->advanceAndApply(0.1f);
stateMachine->advanceAndApply(1.0f);
artboard->draw(renderer.get());
CHECK(silver.matches("nested_events"));
}
TEST_CASE("All nested artboard modes respect hug", "[silver]")
{
rive::SerializingFactory silver;
auto file = ReadRiveFile("assets/nested_hug.riv", &silver);
auto artboard = file->artboardDefault();
silver.frameSize(artboard->width(), artboard->height());
REQUIRE(artboard != nullptr);
auto stateMachine = artboard->stateMachineAt(0);
int viewModelId = artboard.get()->viewModelId();
auto vmi = viewModelId == -1
? file->createViewModelInstance(artboard.get())
: file->createViewModelInstance(viewModelId, 0);
stateMachine->bindViewModelInstance(vmi);
stateMachine->advanceAndApply(0.1f);
auto renderer = silver.makeRenderer();
artboard->draw(renderer.get());
silver.addFrame();
CHECK(silver.matches("nested_hug"));
}
TEST_CASE("Pause and resume nested artboards", "[silver]")
{
rive::SerializingFactory silver;
auto file = ReadRiveFile("assets/pause_nested_artboard.riv", &silver);
auto artboard = file->artboardDefault();
silver.frameSize(artboard->width(), artboard->height());
REQUIRE(artboard != nullptr);
auto stateMachine = artboard->stateMachineAt(0);
auto vmi = file->createViewModelInstance(artboard.get()->viewModelId(), 0);
stateMachine->bindViewModelInstance(vmi);
stateMachine->advanceAndApply(0.1f);
auto renderer = silver.makeRenderer();
artboard->draw(renderer.get());
int frames = (int)(0.25f / 0.016f);
// State machine advances normally
for (int i = 0; i < frames; i++)
{
silver.addFrame();
stateMachine->advanceAndApply(0.016f);
artboard->draw(renderer.get());
}
// Click toggles rectangle color
silver.addFrame();
stateMachine->pointerDown(rive::Vec2D(250.0f, 250.0f));
stateMachine->pointerUp(rive::Vec2D(250.0f, 250.0f));
stateMachine->advanceAndApply(0.016f);
artboard->draw(renderer.get());
// Toggle data bind pauses state machine
silver.addFrame();
stateMachine->pointerDown(rive::Vec2D(25.0f, 25.0f));
stateMachine->pointerUp(rive::Vec2D(25.0f, 25.0f));
stateMachine->advanceAndApply(0.016f);
artboard->draw(renderer.get());
// State machine does not advance
for (int i = 0; i < frames; i++)
{
silver.addFrame();
stateMachine->advanceAndApply(0.016f);
artboard->draw(renderer.get());
}
// Click does not toggle color back
silver.addFrame();
stateMachine->pointerDown(rive::Vec2D(250.0f, 250.0f));
stateMachine->pointerUp(rive::Vec2D(250.0f, 250.0f));
stateMachine->advanceAndApply(0.016f);
artboard->draw(renderer.get());
// Toggle data bind resumes state machine
silver.addFrame();
stateMachine->pointerDown(rive::Vec2D(25.0f, 25.0f));
stateMachine->pointerUp(rive::Vec2D(25.0f, 25.0f));
stateMachine->advanceAndApply(0.016f);
artboard->draw(renderer.get());
// Click toggles color back
silver.addFrame();
stateMachine->pointerDown(rive::Vec2D(250.0f, 250.0f));
stateMachine->pointerUp(rive::Vec2D(250.0f, 250.0f));
stateMachine->advanceAndApply(0.016f);
artboard->draw(renderer.get());
// State machine advances again
for (int i = 0; i < frames; i++)
{
silver.addFrame();
stateMachine->advanceAndApply(0.016f);
artboard->draw(renderer.get());
}
CHECK(silver.matches("pause_nested_artboard"));
}
TEST_CASE("Quantize and speed on nested artboards", "[silver]")
{
rive::SerializingFactory silver;
auto file =
ReadRiveFile("assets/nested_artboard_quantize_and_speed.riv", &silver);
auto artboard = file->artboardDefault();
silver.frameSize(artboard->width(), artboard->height());
REQUIRE(artboard != nullptr);
auto stateMachine = artboard->stateMachineAt(0);
auto vmi = file->createViewModelInstance(artboard.get()->viewModelId(), 0);
auto speedProp =
vmi->propertyValue("speed")->as<rive::ViewModelInstanceNumber>();
auto quantizeProp =
vmi->propertyValue("quant")->as<rive::ViewModelInstanceNumber>();
stateMachine->bindViewModelInstance(vmi);
stateMachine->advanceAndApply(0.1f);
auto renderer = silver.makeRenderer();
artboard->draw(renderer.get());
int frames = (int)(1.0f / 0.016f);
// Multiple state machines in different speed and quantize modes
for (int i = 0; i < frames; i++)
{
silver.addFrame();
stateMachine->advanceAndApply(0.016f);
artboard->draw(renderer.get());
}
// Update speed and quantize values
speedProp->propertyValue(4);
quantizeProp->propertyValue(7);
stateMachine->advanceAndApply(0.016f);
for (int i = 0; i < frames; i++)
{
silver.addFrame();
stateMachine->advanceAndApply(0.016f);
artboard->draw(renderer.get());
}
CHECK(silver.matches("nested_artboard_quantize_and_speed"));
}
TEST_CASE("Nested artboard needs advance", "[silver]")
{
rive::SerializingFactory silver;
auto file = ReadRiveFile("assets/nested_needs_advance.riv", &silver);
auto artboard = file->artboardNamed("node");
silver.frameSize(artboard->width(), artboard->height());
REQUIRE(artboard != nullptr);
auto stateMachine = artboard->stateMachineAt(0);
stateMachine->advanceAndApply(0.1f);
auto renderer = silver.makeRenderer();
artboard->draw(renderer.get());
int frames = 120;
for (int i = 0; i < frames; i++)
{
silver.addFrame();
stateMachine->advanceAndApply(0.016f);
artboard->draw(renderer.get());
}
CHECK(silver.matches("nested_needs_advance"));
}