Libraries

Bunch of changes, i think its at the point where it will be helpful to get a review, there is a *lot* of outstanding work to do here, but i'm hoping that much of it can be done while this is in master but not enabled for uat yet. so @susan101566 and I can work through that set together, with smaller more targeted fixes and changes.

There are a couple of things i want to figure out to make sure we can merge this:
- is the Resolver approach going to work (library components resolve to real components)
- writing library assets into the parent needs a bit more love, it should not cause problems unless you're using libraries
- I want to do a small refactor on the exporter before merge to keep asset writing and asset index updates together as suggested by @susan101566. there's a larger refactor here but i think that can be done in due course.
- re-check editor-only deliniation

After refactoring those i also need to re-check that all these things still work, because chances are i've broken something in the meantime.

for
    using a library in as a nested artboard &
    using a library as part of using a nested artboard that contains a library
check
    using a library animation
    using a library stateMachine
    using a library stateMachineInputs
        we can key state machine input changes
        we can trigger nested state machine input changes from teh state machine editor
    using a library with assets

exporting all the above and view in cpp viewer
exporting all the above and view in dart viewer

There is a large list of items that need to be worked on beyond this. but hopefully we can do that more incrementally. this includes but is not limited to:
- object validations make sense
- the asset panel is interacting as we want
- right click menus make sense
- out of band loading works
- library api failures are handled
- refactor how we add library assets
- refactor asset exporting
- tracking
- smi paths'

Diffs=
9d8a7e81fa Libraries (#9255)

Co-authored-by: Arthur Vivian <arthur@rive.app>
Co-authored-by: Maxwell Talbot <talbot.maxwell@gmail.com>
Co-authored-by: Susan Wang <susan@rive.app>
This commit is contained in:
mjtalbot
2025-04-03 17:27:06 +00:00
parent b4952c69fc
commit 9122e58a54
25 changed files with 340 additions and 36 deletions

2
.gitignore vendored
View File

@@ -91,4 +91,6 @@ dependencies/windows/cache
# Local development setup
compile_commands.json
# running tests results in this
tests/unit_tests/dependencies
tests/unit_tests/default.profraw

View File

@@ -1 +1 @@
f359c918e0135e8c432f7ba3068fff7dbba0ac07
9d8a7e81fa467fc85614952f4566af060e1d43f7

View File

@@ -4,7 +4,7 @@
"int": 31,
"string": "linearanimation"
},
"extends": "animation/animation.json",
"extends": "animation/linear_animation_resolver.json",
"properties": {
"fps": {
"type": "uint",

View File

@@ -0,0 +1,10 @@
{
"name": "LinearAnimationResolver",
"key": {
"int": 550,
"string": "linearanimationresolver"
},
"abstract": true,
"runtime": false,
"extends": "animation/animation.json"
}

View File

@@ -6,7 +6,6 @@
},
"abstract": true,
"extends": "nested_animation.json",
"generic": "animation/linear_animation.json",
"properties": {
"mix": {
"type": "double",

View File

@@ -5,5 +5,5 @@
"string": "nestedStateMachine"
},
"extends": "nested_animation.json",
"generic": "animation/state_machine.json"
"generic": "animation/state_machine_resolver.json"
}

View File

@@ -4,7 +4,7 @@
"int": 53,
"string": "statemachine"
},
"extends": "animation/animation.json",
"extends": "animation/state_machine_resolver.json",
"properties": {
"editingLayerId": {
"type": "Id",

View File

@@ -0,0 +1,10 @@
{
"name": "StateMachineResolver",
"key": {
"int": 551,
"string": "statemachineresolver"
},
"abstract": true,
"runtime": false,
"extends": "animation/animation.json"
}

View File

@@ -29,6 +29,10 @@ public:
{
StateMachineInstance* smInstance =
parent->as<NestedStateMachine>()->stateMachineInstance();
if (smInstance == nullptr)
{
return nullptr;
}
auto inputInstance = smInstance->input(this->inputId());
return inputInstance;
}

View File

@@ -1428,20 +1428,24 @@ StateMachineInstance::StateMachineInstance(const StateMachine* machine,
{
if (animation->is<NestedStateMachine>())
{
auto notifier =
animation->as<NestedStateMachine>()->stateMachineInstance();
if (auto notifier = animation->as<NestedStateMachine>()
->stateMachineInstance())
{
notifier->setNestedArtboard(nestedArtboard);
notifier->addNestedEventListener(this);
}
}
else if (animation->is<NestedLinearAnimation>())
{
auto notifier =
animation->as<NestedLinearAnimation>()->animationInstance();
if (auto notifier = animation->as<NestedLinearAnimation>()
->animationInstance())
{
notifier->setNestedArtboard(nestedArtboard);
notifier->addNestedEventListener(this);
}
}
}
}
sortHitComponents();
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,150 @@
#include <rive/file.hpp>
#include <rive/node.hpp>
#include <rive/nested_artboard.hpp>
#include <rive/shapes/clipping_shape.hpp>
#include <rive/shapes/rectangle.hpp>
#include <rive/assets/image_asset.hpp>
#include <rive/assets/font_asset.hpp>
#include <rive/animation/nested_simple_animation.hpp>
#include <rive/animation/nested_state_machine.hpp>
#include <rive/relative_local_asset_loader.hpp>
#include <utils/no_op_factory.hpp>
#include <utils/no_op_renderer.hpp>
#include "rive_file_reader.hpp"
#include <catch.hpp>
#include <cstdio>
TEST_CASE("File with library artboard loads", "[libraries]")
{
// created by test in rive_core.
auto file = ReadRiveFile("assets/library_export_test.riv");
auto nestedArtboard =
file->artboard()->find<rive::NestedArtboard>("The nested artboard");
REQUIRE(nestedArtboard != nullptr);
REQUIRE(nestedArtboard->name() == "The nested artboard");
REQUIRE(nestedArtboard->x() == 1);
REQUIRE(nestedArtboard->y() == 2);
REQUIRE(nestedArtboard->artboardId() == -1);
// time to find the library asset somewhere
auto assets = file->assets();
REQUIRE(assets.size() == 0);
// todo assert nested artboard structure
}
TEST_CASE("File with library animation loads", "[libraries]")
{
// created by test in rive_core.
auto file = ReadRiveFile("assets/library_export_animation_test.riv");
auto nestedArtboard =
file->artboard()->find<rive::NestedArtboard>("The nested artboard");
REQUIRE(nestedArtboard != nullptr);
REQUIRE(nestedArtboard->name() == "The nested artboard");
REQUIRE(nestedArtboard->x() == 1);
REQUIRE(nestedArtboard->y() == 2);
REQUIRE(nestedArtboard->artboardId() == -1);
auto nestedSimpleAnimation =
file->artboard()->find<rive::NestedSimpleAnimation>("");
REQUIRE(nestedSimpleAnimation != nullptr);
REQUIRE(nestedSimpleAnimation->name() == "");
REQUIRE(nestedSimpleAnimation->isPlaying() == true);
// // TODO: figure out what the id should be?
REQUIRE(nestedSimpleAnimation->animationId() == -1);
// // time to find the library asset somewhere
auto assets = file->assets();
REQUIRE(assets.size() == 0);
REQUIRE(nestedArtboard->nestedAnimations().size() == 1);
// todo assert nested artboard has animation structure
}
TEST_CASE("File with library state machine loads", "[libraries]")
{
// created by test in rive_core.
auto file = ReadRiveFile("assets/library_export_state_machine_test.riv");
auto nestedArtboard =
file->artboard()->find<rive::NestedArtboard>("The nested artboard");
REQUIRE(nestedArtboard != nullptr);
REQUIRE(nestedArtboard->name() == "The nested artboard");
REQUIRE(nestedArtboard->x() == 1);
REQUIRE(nestedArtboard->y() == 2);
REQUIRE(nestedArtboard->artboardId() == -1);
auto nestedSimpleAnimation =
file->artboard()->find<rive::NestedStateMachine>("");
REQUIRE(nestedSimpleAnimation != nullptr);
REQUIRE(nestedSimpleAnimation->name() == "");
REQUIRE(nestedSimpleAnimation->animationId() == -1);
// time to find the library asset somewhere
// make sure its loaded?
auto assets = file->assets();
REQUIRE(assets.size() == 0);
REQUIRE(nestedArtboard->nestedAnimations().size() == 1);
// todo assert nested artboard has state machine structure
}
// TODO reinstate, but right now we're missing fonts locally, need to get
// backend to uat to recreate this test. we are literally just loading files
// here. TEST_CASE("File with library", "[libraries]")
// {
// TODO: create this file in the editor tests.
// auto file = ReadRiveFile("assets/library_with_text_and_image.riv");
// // library, image and text
// auto assets = file->assets();
// REQUIRE(assets.size() == 3);
// auto libraryAsset = assets[0];
// REQUIRE(libraryAsset->is<rive::LibraryAsset>());
// REQUIRE(libraryAsset->name() == "library - Version 1");
// auto fontAsset = assets[1];
// REQUIRE(fontAsset->is<rive::FontAsset>());
// REQUIRE(fontAsset->name() == "Inter");
// auto imageAsset = assets[2];
// REQUIRE(imageAsset->is<rive::ImageAsset>());
// REQUIRE(imageAsset->name() == "tree");
// // library, image and text
// auto libraryAssets =
// static_cast<rive::LibraryAsset*>(libraryAsset)->riveFile()->assets();
// REQUIRE(libraryAssets.size() == 2);
// auto librayFontAsset = libraryAssets[0];
// REQUIRE(librayFontAsset->is<rive::FontAsset>());
// REQUIRE(librayFontAsset->name() == "Inter");
// auto libraryImageAsset = libraryAssets[1];
// REQUIRE(libraryImageAsset->is<rive::ImageAsset>());
// REQUIRE(libraryImageAsset->name() == "tree");
// }
// we are literally just loading files here.
TEST_CASE("File with library including image", "[libraries]")
{
// TODO: create this file in the editor tests.
auto file = ReadRiveFile("assets/library_with_image.riv");
// library, image and text
auto assets = file->assets();
REQUIRE(assets.size() == 0);
// TODO: asset image
}
TEST_CASE("File with multiple libraries including image", "[libraries]")
{
// TODO: create this file in the editor tests.
auto file = ReadRiveFile("assets/double_library_with_image.riv");
// library, image and text
auto assets = file->assets();
REQUIRE(assets.size() == 0);
// TODO: asset multiple images
}

View File

@@ -0,0 +1,58 @@
#include <rive/file.hpp>
#include <rive/node.hpp>
#include <rive/nested_artboard.hpp>
#include <rive/shapes/clipping_shape.hpp>
#include <rive/shapes/rectangle.hpp>
#include <rive/animation/nested_simple_animation.hpp>
#include <rive/animation/nested_state_machine.hpp>
#include <rive/animation/nested_bool.hpp>
#include <rive/animation/nested_trigger.hpp>
#include <rive/animation/nested_number.hpp>
#include <rive/relative_local_asset_loader.hpp>
#include <utils/no_op_factory.hpp>
#include <utils/no_op_renderer.hpp>
#include "rive_file_reader.hpp"
#include <catch.hpp>
#include <cstdio>
TEST_CASE("File with library state machine inputs loads", "[state_machine]")
{
// created by test in rive_core.
auto file = ReadRiveFile("assets/library_export_with_smi.riv");
auto nestedArtboard =
file->artboard()->find<rive::NestedArtboard>("The nested artboard");
REQUIRE(nestedArtboard != nullptr);
REQUIRE(nestedArtboard->name() == "The nested artboard");
REQUIRE(nestedArtboard->x() == 1);
REQUIRE(nestedArtboard->y() == 2);
REQUIRE(nestedArtboard->artboardId() == -1);
auto nestedSimpleAnimation =
file->artboard()->find<rive::NestedStateMachine>("");
REQUIRE(nestedSimpleAnimation != nullptr);
REQUIRE(nestedSimpleAnimation->name() == "");
REQUIRE(nestedSimpleAnimation->animationId() == -1);
auto nestedTrigger = file->artboard()->find<rive::NestedTrigger>("");
REQUIRE(nestedTrigger != nullptr);
REQUIRE(nestedTrigger->name() == "");
REQUIRE(nestedTrigger->inputId() == -1);
auto nestedBool = file->artboard()->find<rive::NestedBool>("");
REQUIRE(nestedBool != nullptr);
REQUIRE(nestedBool->name() == "");
REQUIRE(nestedBool->inputId() == -1);
auto nestedNumber = file->artboard()->find<rive::NestedNumber>("");
REQUIRE(nestedNumber != nullptr);
REQUIRE(nestedNumber->name() == "");
REQUIRE(nestedNumber->inputId() == -1);
// time to find the library asset somewhere
// make sure its loaded?
auto assets = file->assets();
REQUIRE(assets.size() == 0);
// todo assert state machine input structure holds up!
}

View File

@@ -0,0 +1,51 @@
#include <rive/file.hpp>
#include <rive/node.hpp>
#include <rive/nested_artboard.hpp>
#include <rive/shapes/clipping_shape.hpp>
#include <rive/shapes/rectangle.hpp>
#include <rive/animation/nested_simple_animation.hpp>
#include <rive/animation/nested_state_machine.hpp>
#include <rive/animation/nested_bool.hpp>
#include <rive/animation/nested_trigger.hpp>
#include <rive/animation/nested_number.hpp>
#include <rive/relative_local_asset_loader.hpp>
#include <utils/no_op_factory.hpp>
#include <utils/no_op_renderer.hpp>
#include "rive_file_reader.hpp"
#include <catch.hpp>
#include <cstdio>
TEST_CASE("File with state machine inputs loads", "[state_machine]")
{
// created by test in rive_core.
auto file = ReadRiveFile("assets/smi_test.riv");
auto nestedArtboard = file->artboard()->find<rive::NestedArtboard>(
"artboard to nest component");
REQUIRE(nestedArtboard != nullptr);
REQUIRE(nestedArtboard->x() == 100);
REQUIRE(nestedArtboard->y() == 100);
REQUIRE(nestedArtboard->name() == "artboard to nest component");
REQUIRE(nestedArtboard->artboardId() == 1);
auto nestedSimpleAnimation =
file->artboard()->find<rive::NestedStateMachine>("");
REQUIRE(nestedSimpleAnimation != nullptr);
REQUIRE(nestedSimpleAnimation->name() == "");
REQUIRE(nestedSimpleAnimation->animationId() == 0);
auto nestedTrigger = file->artboard()->find<rive::NestedTrigger>("");
REQUIRE(nestedTrigger != nullptr);
REQUIRE(nestedTrigger->name() == "");
REQUIRE(nestedTrigger->inputId() == 0);
auto nestedBool = file->artboard()->find<rive::NestedBool>("");
REQUIRE(nestedBool != nullptr);
REQUIRE(nestedBool->name() == "");
REQUIRE(nestedBool->inputId() == 1);
auto nestedNumber = file->artboard()->find<rive::NestedNumber>("");
REQUIRE(nestedNumber != nullptr);
REQUIRE(nestedNumber->name() == "");
REQUIRE(nestedNumber->inputId() == 2);
}

View File

@@ -10,22 +10,36 @@ MINGW*) machine=windows ;;
esac
CONFIG=debug
for var in "$@"; do
if [[ $var = "release" ]]; then
CONFIG=release
elif [ "$var" = "memory" ]; then
MATCH=
while [[ $# -gt 0 ]]; do
case $1 in
-m|--match)
MATCH="$2"
shift # past argument
shift # past value
;;
lldb)
echo Starting debugger...
UTILITY='lldb'
shift # past argument
;;
memory)
echo Will perform memory checks...
UTILITY='leaks --atExit --'
shift
elif [ "$var" = "lldb" ]; then
echo Starting debugger...
UTILITY='lldb'
;;
release)
CONFIG=release
shift
elif [ "$var" = "rebaseline" ]; then
;;
rebaseline)
export REBASELINE_SILVERS=true
shift
fi
;;
*)
shift # past argument
;;
esac
done
if [[ ! -f "dependencies/bin/premake5" ]]; then
@@ -36,6 +50,9 @@ if [[ ! -f "dependencies/bin/premake5" ]]; then
# once a stable one is avaialble that supports it
git clone --depth 1 --branch v5.0.0-beta3 https://github.com/premake/premake-core.git
pushd premake-core
git pull --tags
git checkout v5.0.0-beta3
# note, latest premake is not compatible with our fatal warnings...
if [[ $LOCAL_ARCH == "arm64" ]]; then
PREMAKE_MAKE_ARCH=ARM
else
@@ -89,7 +106,7 @@ if [[ $machine = "macosx" ]]; then
popd
rm -fR silvers/tarnished
mkdir -p silvers/tarnished
$UTILITY $OUT_DIR/unit_tests
$UTILITY $OUT_DIR/unit_tests "$MATCH"
for var in "$@"; do
if [[ $var = "coverage" ]]; then
xcrun llvm-profdata merge -sparse default.profraw -o default.profdata

View File

@@ -7,8 +7,8 @@ rive_tess = '../../tess'
rive_skia = '../../skia'
skia = dependencies .. '/skia'
dofile(rive .. '/decoders/premake5_v2.lua')
if _OPTIONS.renderer == 'tess' then
dofile(rive .. '/decoders/premake5_v2.lua')
dofile(path.join(path.getabsolute(rive_tess) .. '/build', 'premake5_tess.lua'))
else
-- tess renderer includes this
@@ -19,9 +19,7 @@ dofile(path.join(path.getabsolute(rive) .. '/cg_renderer', 'premake5.lua'))
project('rive_viewer')
do
if _OPTIONS.renderer == 'tess' then
dependson('rive_decoders')
end
kind('ConsoleApp')
defines({ 'WITH_RIVE_TEXT', 'WITH_RIVE_AUDIO', 'WITH_RIVE_LAYOUT', 'YOGA_EXPORT=' })
@@ -29,6 +27,7 @@ do
includedirs({
'../include',
rive .. '/include',
rive .. '/decoders/include',
rive .. '/skia/renderer/include', -- for font backends
dependencies,
dependencies .. '/sokol',
@@ -37,7 +36,7 @@ do
yoga,
})
links({ 'rive', 'rive_harfbuzz', 'rive_sheenbidi', 'rive_yoga' })
links({ 'rive','rive_decoders', 'rive_harfbuzz', 'rive_sheenbidi', 'rive_yoga' })
libdirs({ rive .. '/build/%{cfg.system}/bin/%{cfg.buildcfg}' })