// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include #include #include #include #include inline QUrl testFileUrl(const QString &fileName) { static const QString dir = QTest::qFindTestData("data"); QString result = dir; result += QLatin1Char('/'); result += fileName; return QUrl::fromLocalFile(result); } class Tst_BlendTrees : public QObject { Q_OBJECT private Q_SLOTS: void checkImport(); void testBlendAnimationNode(); }; void Tst_BlendTrees::checkImport() { QQmlEngine engine; QQmlComponent component(&engine); component.setData("import QtQuick; import QtQuick.Timeline; import QtQuick.Timeline.BlendTrees; Item { }", QUrl()); QScopedPointer object(component.create()); QVERIFY2(!object.isNull(), qPrintable(component.errorString())); } void Tst_BlendTrees::testBlendAnimationNode() { QQmlEngine engine; QQmlComponent component(&engine); component.loadUrl(testFileUrl("BlendTreeTest.qml")); QScopedPointer object(component.create()); QVERIFY2(!object.isNull(), qPrintable(component.errorString())); // Get all of the necessary objects auto *timeline = object->findChild("timeline"); auto *timelineAnimation1 = object->findChild("animation1"); QVERIFY2(timelineAnimation1, "Could not find animation1"); auto *timelineAnimation2 = object->findChild("animation2"); QVERIFY2(timelineAnimation2, "Could not find animation2"); auto *animation1Node = object->findChild("animation1Node"); QVERIFY2(animation1Node, "Could not find animation1Node"); auto *animation2Node = object->findChild("animation2Node"); QVERIFY2(animation2Node, "Could not find animation2Node"); auto *blendAnimation = object->findChild("blendAnimation"); QVERIFY2(blendAnimation, "Could not find blendAnimation"); auto *rectangle = object->findChild("rectangle"); QVERIFY2(rectangle, "Could not find rectangle"); auto *animation1Controller = object->findChild("animation1Controller"); QVERIFY2(animation1Controller, "Could not find animation1Controller"); auto *animation2Controller = object->findChild("animation2Controller"); QVERIFY2(animation2Controller, "Could not find animation2Controller"); // At this point nothing should be happening because the animations have controllers // attached to this which forces them to always be paused. Starting states is: // animation1Node.currentFrame == 0 // animation2Node.currentFrame == 100 // rectangle.x = 100 // rectangle.y = 100 // rectangle.color = #ff7f00 QCOMPARE(animation1Node->property("currentFrame").toInt(), 0); QCOMPARE(animation2Node->property("currentFrame").toInt(), 100); QCOMPARE(rectangle->property("x").toInt(), 100); QCOMPARE(rectangle->property("y").toInt(), 100); QCOMPARE(rectangle->property("color").value(), QColor("#ff7f00")); // Push animation1 to end // Should be a blend of 50% blend of animation1 and and 50% blend of animation2 // animation 1 should be at frame 100 (100%) // animation 2 should be at frame 100 (0%) animation1Controller->setProperty("progress", 1.0f); QCOMPARE(animation1Node->property("currentFrame").toInt(), 100); QCOMPARE(animation2Node->property("currentFrame").toInt(), 100); QCOMPARE(rectangle->property("x").toInt(), 200); QCOMPARE(rectangle->property("y").toInt(), 200); QCOMPARE(rectangle->property("color").value(), QColor("#ffff00")); // Push animation2 to end // Should be a blend of 50% blend of animation1 and and 50% blend of animation2 // animation 1 should be at frame 100 (100%) // animation 2 should be at frame 200 (100%) animation2Controller->setProperty("progress", 1.0f); QCOMPARE(animation1Node->property("currentFrame").toInt(), 100); QCOMPARE(animation2Node->property("currentFrame").toInt(), 200); QCOMPARE(rectangle->property("x").toInt(), 100); QCOMPARE(rectangle->property("y").toInt(), 300); QCOMPARE(rectangle->property("color").value(), QColor("#ff7f7f")); // Change weight to 0.0 // Should be a blend of 100% blend of animation1 and and 0% blend of animation2 // animation 1 should be at frame 100 (100%) // animation 2 should be at frame 200 (100%) blendAnimation->setProperty("weight", 0.0f); QCOMPARE(animation1Node->property("currentFrame").toInt(), 100); QCOMPARE(animation2Node->property("currentFrame").toInt(), 200); QCOMPARE(rectangle->property("x").toInt(), 200); QCOMPARE(rectangle->property("y").toInt(), 200); QCOMPARE(rectangle->property("color").value(), QColor("#ffff00")); // Change weight to 1.0 // Should be a blend of 0% blend of animation1 and and 100% blend of animation2 // animation 1 should be at frame 100 (100%) // animation 2 should be at frame 200 (100%) blendAnimation->setProperty("weight", 1.0f); QCOMPARE(animation1Node->property("currentFrame").toInt(), 100); QCOMPARE(animation2Node->property("currentFrame").toInt(), 200); QCOMPARE(rectangle->property("x").toInt(), 0); QCOMPARE(rectangle->property("y").toInt(), 400); QCOMPARE(rectangle->property("color").value(), QColor("#ff00ff")); // Change animation1 to start (should be the same as previous) // Should be a blend of 0% blend of animation1 and and 100% blend of animation2 // animation 1 should be at frame 0 (0%) // animation 2 should be at frame 200 (100%) animation1Controller->setProperty("progress", 0.0f); QCOMPARE(animation1Node->property("currentFrame").toInt(), 0); QCOMPARE(animation2Node->property("currentFrame").toInt(), 200); QCOMPARE(rectangle->property("x").toInt(), 0); QCOMPARE(rectangle->property("y").toInt(), 400); QCOMPARE(rectangle->property("color").value(), QColor("#ff00ff")); // Change weight to 0.0 // Should be a blend of 100% blend of animation1 and and 0% blend of animation2 // animation 1 should be at frame 0 (0%) // animation 2 should be at frame 200 (100%) blendAnimation->setProperty("weight", 0.0f); QCOMPARE(animation1Node->property("currentFrame").toInt(), 0); QCOMPARE(animation2Node->property("currentFrame").toInt(), 200); QCOMPARE(rectangle->property("x").toInt(), 0); QCOMPARE(rectangle->property("y").toInt(), 0); QCOMPARE(rectangle->property("color").value(), QColor("#ff0000")); // Change animation2 to start (should be the same as previous) // Should be a blend of 100% blend of animation1 and and 0% blend of animation2 // animation 1 should be at frame 0 (0%) // animation 2 should be at frame 100 (0%) animation2Controller->setProperty("progress", 0.0f); QCOMPARE(animation1Node->property("currentFrame").toInt(), 0); QCOMPARE(animation2Node->property("currentFrame").toInt(), 100); QCOMPARE(rectangle->property("x").toInt(), 0); QCOMPARE(rectangle->property("y").toInt(), 0); QCOMPARE(rectangle->property("color").value(), QColor("#ff0000")); // Disable committing of changes by animationBlendNode blendAnimation->setProperty("outputEnabled", false); // Now changing either animation progress or weight should have no effect on the scene animation1Controller->setProperty("progress", 1.0f); QCOMPARE(animation1Node->property("currentFrame").toInt(), 100); QCOMPARE(animation2Node->property("currentFrame").toInt(), 100); QCOMPARE(rectangle->property("x").toInt(), 0); QCOMPARE(rectangle->property("y").toInt(), 0); QCOMPARE(rectangle->property("color").value(), QColor("#ff0000")); animation2Controller->setProperty("progress", 1.0f); QCOMPARE(animation1Node->property("currentFrame").toInt(), 100); QCOMPARE(animation2Node->property("currentFrame").toInt(), 200); QCOMPARE(rectangle->property("x").toInt(), 0); QCOMPARE(rectangle->property("y").toInt(), 0); QCOMPARE(rectangle->property("color").value(), QColor("#ff0000")); blendAnimation->setProperty("weight", 0.5f); QCOMPARE(animation1Node->property("currentFrame").toInt(), 100); QCOMPARE(animation2Node->property("currentFrame").toInt(), 200); QCOMPARE(rectangle->property("x").toInt(), 0); QCOMPARE(rectangle->property("y").toInt(), 0); QCOMPARE(rectangle->property("color").value(), QColor("#ff0000")); // re-enable committing of changes by animationBlendNode // This should cause the animation to blend to the new state blendAnimation->setProperty("outputEnabled", true); QCOMPARE(animation1Node->property("currentFrame").toInt(), 100); QCOMPARE(animation2Node->property("currentFrame").toInt(), 200); QCOMPARE(rectangle->property("x").toInt(), 100); QCOMPARE(rectangle->property("y").toInt(), 300); QCOMPARE(rectangle->property("color").value(), QColor("#ff7f7f")); // Test disconnecting animation1 blendAnimation->setProperty("source1", QVariant()); animation1Controller->setProperty("progress", 0.0f); QCOMPARE(animation1Node->property("currentFrame").toInt(), 0); QCOMPARE(animation2Node->property("currentFrame").toInt(), 200); QCOMPARE(rectangle->property("x").toInt(), 100); QCOMPARE(rectangle->property("y").toInt(), 300); QCOMPARE(rectangle->property("color").value(), QColor("#ff7f7f")); // Test disconnecting animation2 blendAnimation->setProperty("source2", QVariant()); animation2Controller->setProperty("progress", 0.0f); QCOMPARE(animation1Node->property("currentFrame").toInt(), 0); QCOMPARE(animation2Node->property("currentFrame").toInt(), 100); QCOMPARE(rectangle->property("x").toInt(), 100); QCOMPARE(rectangle->property("y").toInt(), 300); QCOMPARE(rectangle->property("color").value(), QColor("#ff7f7f")); // Disable committing of changes by animationBlendNode blendAnimation->setProperty("outputEnabled", false); // Try outputting dirrectly from animation1 animation1Node->setProperty("outputEnabled", true); // Should have changed to be the value of animation1 at frame 0 QCOMPARE(rectangle->property("x").toInt(), 0); QCOMPARE(rectangle->property("y").toInt(), 0); QCOMPARE(rectangle->property("color").value(), QColor("#ff0000")); animation1Controller->setProperty("progress", 1.0f); // Should have changed to be the value of animation1 at frame 100 QCOMPARE(rectangle->property("x").toInt(), 200); QCOMPARE(rectangle->property("y").toInt(), 200); QCOMPARE(rectangle->property("color").value(), QColor("#ffff00")); // Disable outputting dirrectly from animation1 animation1Node->setProperty("outputEnabled", false); // Try outputting dirrectly from animation2 animation2Node->setProperty("outputEnabled", true); // Nothing should have changed since both would be at frame 100 anyway QCOMPARE(animation1Node->property("currentFrame").toInt(), 100); QCOMPARE(animation2Node->property("currentFrame").toInt(), 100); QCOMPARE(rectangle->property("x").toInt(), 200); QCOMPARE(rectangle->property("y").toInt(), 200); QCOMPARE(rectangle->property("color").value(), QColor("#ffff00")); animation2Controller->setProperty("progress", 1.0f); QCOMPARE(animation1Node->property("currentFrame").toInt(), 100); QCOMPARE(animation2Node->property("currentFrame").toInt(), 200); QCOMPARE(rectangle->property("x").toInt(), 0); QCOMPARE(rectangle->property("y").toInt(), 400); QCOMPARE(rectangle->property("color").value(), QColor("#ff00ff")); // Try breaking animation2Node's connection to timeline (the source of frame data) // currentFrame should change but the output should not animation2Node->setProperty("timeline", QVariant()); animation2Controller->setProperty("progress", 0.0f); QCOMPARE(animation2Node->property("currentFrame").toInt(), 100); QCOMPARE(rectangle->property("x").toInt(), 0); QCOMPARE(rectangle->property("y").toInt(), 400); QCOMPARE(rectangle->property("color").value(), QColor("#ff00ff")); // reattach the timeline animation2Node->setProperty("timeline", QVariant::fromValue(timeline)); // Should update the output now that we can fetch frameData for frame 100 QCOMPARE(animation2Node->property("currentFrame").toInt(), 100); QCOMPARE(rectangle->property("x").toInt(), 200); QCOMPARE(rectangle->property("y").toInt(), 200); QCOMPARE(rectangle->property("color").value(), QColor("#ffff00")); // Try breaking animation2Node's connection to animation animation2Node->setProperty("animation", QVariant()); animation2Controller->setProperty("progress", 1.0f); QCOMPARE(rectangle->property("x").toInt(), 200); QCOMPARE(rectangle->property("y").toInt(), 200); QCOMPARE(rectangle->property("color").value(), QColor("#ffff00")); // reattach the animation animation2Node->setProperty("animation", QVariant::fromValue(timelineAnimation2)); // Should update the output now that we can fetch frameData for frame 200 QCOMPARE(rectangle->property("x").toInt(), 0); QCOMPARE(rectangle->property("y").toInt(), 400); QCOMPARE(rectangle->property("color").value(), QColor("#ff00ff")); } QTEST_MAIN(Tst_BlendTrees) #include "tst_blendtrees.moc"