up-to-date submodule (rive-cpp)
authorHermet Park <chuneon.park@samsung.com>
Mon, 10 May 2021 05:15:13 +0000 (14:15 +0900)
committerHermet Park <chuneon.park@samsung.com>
Mon, 10 May 2021 05:15:13 +0000 (14:15 +0900)
Change-Id: I2483bf6c15fe46510b599855c5f676d6e6128fe8

38 files changed:
submodule/.vscode/settings.json
submodule/README.md
submodule/build.sh
submodule/build/premake5.lua
submodule/dev/defs/bones/tendon.json
submodule/dev/defs/shapes/rectangle.json
submodule/dev/test.sh
submodule/include/animation/linear_animation_instance.hpp
submodule/include/animation/state_machine_input_instance.hpp
submodule/include/animation/state_machine_instance.hpp
submodule/include/drawable.hpp
submodule/include/generated/core_registry.hpp
submodule/include/generated/shapes/rectangle_base.hpp
submodule/include/importers/state_machine_layer_importer.hpp
submodule/include/shapes/paint/linear_gradient.hpp
submodule/include/shapes/paint/shape_paint.hpp
submodule/include/shapes/paint/shape_paint_mutator.hpp
submodule/include/shapes/parametric_path.hpp
submodule/include/shapes/rectangle.hpp
submodule/skia/recorder/src/extractor.cpp
submodule/skia/renderer/src/skia_renderer.cpp
submodule/src/animation/linear_animation_instance.cpp
submodule/src/animation/state_machine_instance.cpp
submodule/src/artboard.cpp
submodule/src/core/binary_reader.cpp
submodule/src/file.cpp
submodule/src/importers/state_machine_layer_importer.cpp
submodule/src/shapes/clipping_shape.cpp
submodule/src/shapes/metrics_path.cpp
submodule/src/shapes/paint/linear_gradient.cpp
submodule/src/shapes/paint/shape_paint_mutator.cpp
submodule/src/shapes/paint/solid_color.cpp
submodule/src/shapes/parametric_path.cpp
submodule/src/shapes/path.cpp
submodule/src/shapes/rectangle.cpp
submodule/test/main_test.cpp
submodule/test/path_test.cpp
submodule/test/state_machine_test.cpp

index 19acd03892de7c089dbb586bb50e275cee980254..8e596c5c6935118c82fb50472a16326972f98b3b 100644 (file)
@@ -1,4 +1,8 @@
 {
+    "disasexpl.associations": {
+        "**/*.c": "${workspaceFolder}/build/bin/assembly/${fileDirname}/${fileBasenameNoExtension}.S",
+        "**/*.cpp": "${workspaceFolder}/build/bin/assembly/${fileDirname}/${fileBasenameNoExtension}.S"
+    },
     "files.associations": {
         "type_traits": "cpp",
         "string": "cpp",
index 6e6f02376725d2dba153dc446cae6240ab927f36..5ad14cb0a8156c272ceebaf7a3caf8bf21903e2c 100644 (file)
@@ -1,10 +1,49 @@
 # rive-cpp
-C++ runtime for Rive
+C++ runtime for [Rive](https://rive.app). Provides these runtime features:
+- Loading Artboards and their contents from **.riv** files.
+- Querying LinearAnimations and StateMachines from Artboards.
+- Making changes to Artboard hierarchy (fundamentally same guts used by LinearAnimations and StateMachines) and effienclty solving those changes via Artboard::advance.
+- Abstract Renderer for submitting high level vector path commands with retained path objects to optimize and minimize path re-computation (ultimately up to the concrete rendering implementation).
+- Example concrete renderer written in C++ with [Skia](https://skia.org/). Skia renderer code is in [skia/renderer/src/skia_renderer.cpp](skia/renderer/src/skia_renderer.cpp).
+
+## Build System
+We use [premake5](https://premake.github.io/). The Rive dev team primarily works on MacOS. There is some work done by the community to also support Windows and Linux. PRs welcomed for specific platforms you with to support! We encourage you to use premake as it's highly extensible and configurable for a variety of platforms.
 
 ## Build
 In the ```rive``` directory, run ```build.sh``` to debug build and ```build.sh release``` for a release build.
 
 ## Testing
+Uses the [Catch2](https://github.com/catchorg/Catch2) testing framework.
+
+```
+cd dev
+./test.sh
+```
+
 In the ```dev``` directory, run ```test.sh``` to compile and execute the tests.
 
-The tests live in ```rive/test```. To add new tests, create a new ```xxx_test.cpp``` file here. The test harness will automatically pick up the new file.
\ No newline at end of file
+The tests live in ```rive/test```. To add new tests, create a new ```xxx_test.cpp``` file here. The test harness will automatically pick up the new file.
+
+There's a VSCode command provided to ```run tests``` from the Tasks: Run Task command palette. 
+
+## Memory Checks
+Note that if you're on MacOS you'll want to install valgrind, which is somewhat complicated these days. This is the easiest solution (please PR a better one when it becomes available).
+```
+brew tap LouisBrunner/valgrind
+brew install --HEAD LouisBrunner/valgrind/valgrind
+```
+You can now run the all the tests through valgrind by running ```test.sh memory```.
+
+## Disassembly Explorer
+If you want to examine the generated assembly code per cpp file, install [Disassembly Explorer](https://marketplace.visualstudio.com/items?itemName=dseight.disasexpl) in VSCode.
+
+A ```disassemble``` task is provided to compile and preview the generated assembly. You can reach it via the Tasks: Run Task command palette or you can bind it to a shortcut by editing your VSCode keybindings.json:
+```
+[
+    {
+        "key": "cmd+d",
+        "command": "workbench.action.tasks.runTask",
+        "args": "disassemble"
+    }
+]
+```
\ No newline at end of file
index efaada44fee3a1be2b0c2bfd8fde5e6e2d3fe132..8b29a8cd7ab07c64ffab9ee1e983a49132194903 100755 (executable)
@@ -12,7 +12,8 @@ then
 elif [ "$OPTION" = "clean" ]
 then
     echo Cleaning project ...
-    premake5 gmake2 && make clean
+    premake5 gmake2 && make clean && make clean config=release
+
 elif [ "$OPTION" = "release" ]
 then
     premake5 gmake2 && make config=release -j7
index 89e2205f21e4b64c64ba5c47e4c35d697cc414e1..70a4d2a0a690609082cb229e5fee73ba46579336 100644 (file)
@@ -1,26 +1,29 @@
 workspace "rive"
-configurations {"debug", "release"}
+    configurations {"debug", "release"}
 
 project "rive"
-kind "StaticLib"
-language "C++"
-cppdialect "C++17"
-targetdir "bin/%{cfg.buildcfg}"
-objdir "obj/%{cfg.buildcfg}"
-includedirs {"../include"}
+    kind "StaticLib"
+    language "C++"
+    cppdialect "C++17"
+    targetdir "bin/%{cfg.buildcfg}"
+    objdir "obj/%{cfg.buildcfg}"
+    includedirs {"../include"}
 
-files {"../src/**.cpp"}
+    files {"../src/**.cpp"}
 
-buildoptions {"-Wall", "-fno-exceptions", "-fno-rtti"}
+    buildoptions {"-Wall", "-fno-exceptions", "-fno-rtti"}
 
-filter "configurations:debug"
-defines {"DEBUG"}
-symbols "On"
+    filter "system:windows"
+        defines {"_USE_MATH_DEFINES"}
 
-filter "configurations:release"
-defines {"RELEASE"}
-defines { "NDEBUG" }
-optimize "On"
+    filter "configurations:debug"
+        defines {"DEBUG"}
+        symbols "On"
+
+    filter "configurations:release"
+        defines {"RELEASE"}
+        defines {"NDEBUG"}
+        optimize "On"
 
 -- Clean Function --
 newaction {
index 6ec698e1c0ace9deb73b6d0df11bdc28e409f545..f92567a634eb8b54f1b2fbb2aac4342a6c665a92 100644 (file)
@@ -6,16 +6,6 @@
   },
   "extends": "component.json",
   "properties": {
-    "index": {
-      "type": "uint",
-      "initialValue": "0",
-      "key": {
-        "int": 118,
-        "string": "index"
-      },
-      "description": "The index used for weighting. Implicit at runtime. Required at edit time to allow undo ops to put them back in order.",
-      "runtime": false
-    },
     "boneId": {
       "type": "Id",
       "typeRuntime": "uint",
index d771905fff01e429f9317cc72e737e18696668bd..59fae900acd8becb24b16e33217db9e5ee097e0d 100644 (file)
@@ -6,14 +6,54 @@
   },
   "extends": "shapes/parametric_path.json",
   "properties": {
-    "cornerRadius": {
+    "linkCornerRadius": {
+      "type": "bool",
+      "initialValue": "true",
+      "key": {
+        "int": 164,
+        "string": "linkcornerradius"
+      },
+      "description": "Whether the TL corner radius defines all the radiuses"
+    },
+    "cornerRadiusTL": {
       "type": "double",
       "initialValue": "0",
+      "animates": true,
       "key": {
         "int": 31,
-        "string": "cornerRadius"
+        "string": "cornerRadiusTL"
+      },
+      "description": "Top left radius of the corners of this rectangle"
+    },
+    "cornerRadiusTR": {
+      "type": "double",
+      "initialValue": "0",
+      "animates": true,
+      "key": {
+        "int": 161,
+        "string": "cornerRadiusTR"
+      },
+      "description": "Top right radius of the corners of this rectangle"
+    },
+    "cornerRadiusBL": {
+      "type": "double",
+      "initialValue": "0",
+      "animates": true,
+      "key": {
+        "int": 162,
+        "string": "cornerRadiusBL"
+      },
+      "description": "Bottom left radius of the corners of this rectangle"
+    },
+    "cornerRadiusBR": {
+      "type": "double",
+      "initialValue": "0",
+      "animates": true,
+      "key": {
+        "int": 163,
+        "string": "cornerRadiusBR"
       },
-      "description": "Radius of the corners of this rectangle"
+      "description": "Bottom right radius of the corners of this rectangle"
     }
   }
 }
\ No newline at end of file
index 619c57951c6b480bc6bf81943ca4a114516aec13..4f7acb973ae866c32fd2f6cd401fdd54cd3910a6 100755 (executable)
@@ -2,6 +2,7 @@
 pushd test &>/dev/null
 
 OPTION=$1
+UTILITY=
 
 if [ "$OPTION" = "help" ]
 then
@@ -13,13 +14,18 @@ then
     echo Cleaning project ...
     premake5 clean || exit 1
     shift
+elif [ "$OPTION" = "memory" ]
+then
+  echo Will perform memory checks...
+  UTILITY='valgrind --leak-check=full'
+  shift
 fi
 
 premake5 gmake2 || exit 1
 make -j7 || exit 1
 for file in ./build/bin/debug/*; do
   echo testing $file
-  $file "$1"
+  $UTILITY $file "$1"
 done
 
 popd &>/dev/null
\ No newline at end of file
index 78da2ec4713228e476afef5d51213d9b554e50f2..0fb1fb0cbf4b4c48be08ec033bbcddd446cf6844 100644 (file)
@@ -31,6 +31,23 @@ namespace rive
                // to
                float time() const { return m_Time; }
 
+               // Returns the direction that we are currently playing in
+               float direction() const { return m_Direction; }
+
+               // Update the direction of the animation instance, positive value for
+               // forwards Negative for backwards
+               void direction(int direction)
+               {
+                       if (direction > 0)
+                       {
+                               m_Direction = 1;
+                       }
+                       else
+                       {
+                               m_Direction = -1;
+                       }
+               }
+
                // Sets the animation's point in time.
                void time(float value);
 
index d9849ad5f3dccbf8331f17185bc6732ff48eea19..d622ef99331a0e0194cce615a7f74b454f94c95e 100644 (file)
@@ -12,10 +12,12 @@ namespace rive
        class StateMachineNumber;
        class StateMachineTrigger;
        class TransitionTriggerCondition;
+       class StateMachineLayerInstance;
 
        class SMIInput
        {
                friend class StateMachineInstance;
+               friend class StateMachineLayerInstance;
 
        private:
                StateMachineInstance* m_MachineInstance;
@@ -32,7 +34,7 @@ namespace rive
        public:
                virtual ~SMIInput() {}
                const StateMachineInput* input() const { return m_Input; }
-               
+
                const std::string& name() const;
                uint16_t inputCoreType() const;
        };
index bcfbc6dc8621e1a741f62726008555dee76635a7..953d454df3a63f1fd541154de0b9da25b767ecba 100644 (file)
@@ -3,10 +3,12 @@
 
 #include <string>
 #include <stddef.h>
+#include "animation/linear_animation_instance.hpp"
 
 namespace rive
 {
        class StateMachine;
+       class LayerState;
        class SMIInput;
        class Artboard;
        class SMIBool;
@@ -20,18 +22,18 @@ namespace rive
                friend class SMIInput;
 
        private:
-               StateMachine* m_Machine;
+               const StateMachine* m_Machine;
                bool m_NeedsAdvance = false;
 
                size_t m_InputCount;
                SMIInput** m_InputInstances;
-               unsigned int m_LayerCount;
+               size_t m_LayerCount;
                StateMachineLayerInstance* m_Layers;
 
                void markNeedsAdvance();
 
        public:
-               StateMachineInstance(StateMachine* machine);
+               StateMachineInstance(const StateMachine* machine);
                ~StateMachineInstance();
 
                // Advance the state machine by the specified time. Returns true if the
@@ -50,6 +52,18 @@ namespace rive
                SMIBool* getBool(std::string name) const;
                SMINumber* getNumber(std::string name) const;
                SMITrigger* getTrigger(std::string name) const;
+
+               const size_t currentAnimationCount() const;
+               const LinearAnimationInstance* currentAnimationByIndex(size_t index) const;
+
+               // The number of state changes that occurred across all layers on the
+               // previous advance.
+               size_t stateChangedCount() const;
+
+               // Returns the state name for states that changed in layers on the
+               // previously called advance. If the index of out of range, it returns
+               // the empty string.
+               const LayerState* stateChangedByIndex(size_t index) const;
        };
 } // namespace rive
-#endif
\ No newline at end of file
+#endif
index 7d5b79582750d7d175c78e366b54bc5cbd15913a..8e1204139f6fab08e82a4a2431576a7a692596b3 100644 (file)
@@ -9,7 +9,7 @@ namespace rive
        class ClippingShape;
        class Artboard;
        class DrawRules;
-       
+
        class Drawable : public DrawableBase
        {
                friend class Artboard;
@@ -30,6 +30,13 @@ namespace rive
                {
                        return m_ClippingShapes;
                }
+
+               const inline bool isHidden() const
+               {
+                       // For now we have a single drawable flag, when we have more we can
+                       // make an actual enum for this.
+                       return (drawableFlags() & 0x1) == 0x1;
+               }
        };
 } // namespace rive
 
index e090b44129f1664f3e849b5d718180d00ebfc1f9..a8721128318c99c5c68b82795ce60dc5dc0db179 100644 (file)
@@ -426,8 +426,17 @@ namespace rive
                                case ParametricPathBase::originYPropertyKey:
                                        object->as<ParametricPathBase>()->originY(value);
                                        break;
-                               case RectangleBase::cornerRadiusPropertyKey:
-                                       object->as<RectangleBase>()->cornerRadius(value);
+                               case RectangleBase::cornerRadiusTLPropertyKey:
+                                       object->as<RectangleBase>()->cornerRadiusTL(value);
+                                       break;
+                               case RectangleBase::cornerRadiusTRPropertyKey:
+                                       object->as<RectangleBase>()->cornerRadiusTR(value);
+                                       break;
+                               case RectangleBase::cornerRadiusBLPropertyKey:
+                                       object->as<RectangleBase>()->cornerRadiusBL(value);
+                                       break;
+                               case RectangleBase::cornerRadiusBRPropertyKey:
+                                       object->as<RectangleBase>()->cornerRadiusBR(value);
                                        break;
                                case CubicMirroredVertexBase::rotationPropertyKey:
                                        object->as<CubicMirroredVertexBase>()->rotation(value);
@@ -552,6 +561,9 @@ namespace rive
                                case PointsPathBase::isClosedPropertyKey:
                                        object->as<PointsPathBase>()->isClosed(value);
                                        break;
+                               case RectangleBase::linkCornerRadiusPropertyKey:
+                                       object->as<RectangleBase>()->linkCornerRadius(value);
+                                       break;
                                case ClippingShapeBase::isVisiblePropertyKey:
                                        object->as<ClippingShapeBase>()->isVisible(value);
                                        break;
@@ -730,8 +742,14 @@ namespace rive
                                        return object->as<ParametricPathBase>()->originX();
                                case ParametricPathBase::originYPropertyKey:
                                        return object->as<ParametricPathBase>()->originY();
-                               case RectangleBase::cornerRadiusPropertyKey:
-                                       return object->as<RectangleBase>()->cornerRadius();
+                               case RectangleBase::cornerRadiusTLPropertyKey:
+                                       return object->as<RectangleBase>()->cornerRadiusTL();
+                               case RectangleBase::cornerRadiusTRPropertyKey:
+                                       return object->as<RectangleBase>()->cornerRadiusTR();
+                               case RectangleBase::cornerRadiusBLPropertyKey:
+                                       return object->as<RectangleBase>()->cornerRadiusBL();
+                               case RectangleBase::cornerRadiusBRPropertyKey:
+                                       return object->as<RectangleBase>()->cornerRadiusBR();
                                case CubicMirroredVertexBase::rotationPropertyKey:
                                        return object->as<CubicMirroredVertexBase>()->rotation();
                                case CubicMirroredVertexBase::distancePropertyKey:
@@ -820,6 +838,8 @@ namespace rive
                                        return object->as<StrokeBase>()->transformAffectsStroke();
                                case PointsPathBase::isClosedPropertyKey:
                                        return object->as<PointsPathBase>()->isClosed();
+                               case RectangleBase::linkCornerRadiusPropertyKey:
+                                       return object->as<RectangleBase>()->linkCornerRadius();
                                case ClippingShapeBase::isVisiblePropertyKey:
                                        return object->as<ClippingShapeBase>()->isVisible();
                        }
@@ -907,7 +927,10 @@ namespace rive
                                case ParametricPathBase::heightPropertyKey:
                                case ParametricPathBase::originXPropertyKey:
                                case ParametricPathBase::originYPropertyKey:
-                               case RectangleBase::cornerRadiusPropertyKey:
+                               case RectangleBase::cornerRadiusTLPropertyKey:
+                               case RectangleBase::cornerRadiusTRPropertyKey:
+                               case RectangleBase::cornerRadiusBLPropertyKey:
+                               case RectangleBase::cornerRadiusBRPropertyKey:
                                case CubicMirroredVertexBase::rotationPropertyKey:
                                case CubicMirroredVertexBase::distancePropertyKey:
                                case PolygonBase::cornerRadiusPropertyKey:
@@ -947,6 +970,7 @@ namespace rive
                                case ShapePaintBase::isVisiblePropertyKey:
                                case StrokeBase::transformAffectsStrokePropertyKey:
                                case PointsPathBase::isClosedPropertyKey:
+                               case RectangleBase::linkCornerRadiusPropertyKey:
                                case ClippingShapeBase::isVisiblePropertyKey:
                                        return CoreBoolType::id;
                                default:
index 46e42f053246ef9c42ee404930551917fb2ba2e0..59591c7b1bd9f40a9d691defcf9d13b73a697e2f 100644 (file)
@@ -1,5 +1,6 @@
 #ifndef _RIVE_RECTANGLE_BASE_HPP_
 #define _RIVE_RECTANGLE_BASE_HPP_
+#include "core/field_types/core_bool_type.hpp"
 #include "core/field_types/core_double_type.hpp"
 #include "shapes/parametric_path.hpp"
 namespace rive
@@ -33,35 +34,103 @@ namespace rive
 
                uint16_t coreType() const override { return typeKey; }
 
-               static const uint16_t cornerRadiusPropertyKey = 31;
+               static const uint16_t linkCornerRadiusPropertyKey = 164;
+               static const uint16_t cornerRadiusTLPropertyKey = 31;
+               static const uint16_t cornerRadiusTRPropertyKey = 161;
+               static const uint16_t cornerRadiusBLPropertyKey = 162;
+               static const uint16_t cornerRadiusBRPropertyKey = 163;
 
        private:
-               float m_CornerRadius = 0.0f;
+               bool m_LinkCornerRadius = true;
+               float m_CornerRadiusTL = 0.0f;
+               float m_CornerRadiusTR = 0.0f;
+               float m_CornerRadiusBL = 0.0f;
+               float m_CornerRadiusBR = 0.0f;
        public:
-               inline float cornerRadius() const { return m_CornerRadius; }
-               void cornerRadius(float value)
+               inline bool linkCornerRadius() const { return m_LinkCornerRadius; }
+               void linkCornerRadius(bool value)
                {
-                       if (m_CornerRadius == value)
+                       if (m_LinkCornerRadius == value)
                        {
                                return;
                        }
-                       m_CornerRadius = value;
-                       cornerRadiusChanged();
+                       m_LinkCornerRadius = value;
+                       linkCornerRadiusChanged();
+               }
+
+               inline float cornerRadiusTL() const { return m_CornerRadiusTL; }
+               void cornerRadiusTL(float value)
+               {
+                       if (m_CornerRadiusTL == value)
+                       {
+                               return;
+                       }
+                       m_CornerRadiusTL = value;
+                       cornerRadiusTLChanged();
+               }
+
+               inline float cornerRadiusTR() const { return m_CornerRadiusTR; }
+               void cornerRadiusTR(float value)
+               {
+                       if (m_CornerRadiusTR == value)
+                       {
+                               return;
+                       }
+                       m_CornerRadiusTR = value;
+                       cornerRadiusTRChanged();
+               }
+
+               inline float cornerRadiusBL() const { return m_CornerRadiusBL; }
+               void cornerRadiusBL(float value)
+               {
+                       if (m_CornerRadiusBL == value)
+                       {
+                               return;
+                       }
+                       m_CornerRadiusBL = value;
+                       cornerRadiusBLChanged();
+               }
+
+               inline float cornerRadiusBR() const { return m_CornerRadiusBR; }
+               void cornerRadiusBR(float value)
+               {
+                       if (m_CornerRadiusBR == value)
+                       {
+                               return;
+                       }
+                       m_CornerRadiusBR = value;
+                       cornerRadiusBRChanged();
                }
 
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
                        {
-                               case cornerRadiusPropertyKey:
-                                       m_CornerRadius = CoreDoubleType::deserialize(reader);
+                               case linkCornerRadiusPropertyKey:
+                                       m_LinkCornerRadius = CoreBoolType::deserialize(reader);
+                                       return true;
+                               case cornerRadiusTLPropertyKey:
+                                       m_CornerRadiusTL = CoreDoubleType::deserialize(reader);
+                                       return true;
+                               case cornerRadiusTRPropertyKey:
+                                       m_CornerRadiusTR = CoreDoubleType::deserialize(reader);
+                                       return true;
+                               case cornerRadiusBLPropertyKey:
+                                       m_CornerRadiusBL = CoreDoubleType::deserialize(reader);
+                                       return true;
+                               case cornerRadiusBRPropertyKey:
+                                       m_CornerRadiusBR = CoreDoubleType::deserialize(reader);
                                        return true;
                        }
                        return ParametricPath::deserialize(propertyKey, reader);
                }
 
        protected:
-               virtual void cornerRadiusChanged() {}
+               virtual void linkCornerRadiusChanged() {}
+               virtual void cornerRadiusTLChanged() {}
+               virtual void cornerRadiusTRChanged() {}
+               virtual void cornerRadiusBLChanged() {}
+               virtual void cornerRadiusBRChanged() {}
        };
 } // namespace rive
 
index 56a9b954df0c58b358c1af4b22400a12730c985b..c8e666a04c3b792e84569db1ed12d6c27a5f673a 100644 (file)
@@ -7,17 +7,17 @@ namespace rive
 {
        class StateMachineLayer;
        class LayerState;
-       class ArtboardImporter;
+       class Artboard;
 
        class StateMachineLayerImporter : public ImportStackObject
        {
        private:
                StateMachineLayer* m_Layer;
-               ArtboardImporter* m_ArtboardImporter;
+               const Artboard* m_Artboard;
 
        public:
                StateMachineLayerImporter(StateMachineLayer* layer,
-                                         ArtboardImporter* artboardImporter);
+                                         const Artboard* artboard);
                void addState(LayerState* state);
                StatusCode resolve() override;
                bool readNullObject() override;
index a56de45dc8604241ff884d81f9e33a76cdc0ddd4..645e829f29f378bbdeeb047b4a27001c89e91a99 100644 (file)
@@ -13,7 +13,6 @@ namespace rive
        {
        private:
                std::vector<GradientStop*> m_Stops;
-               bool m_PaintsInWorldSpace;
                Node* m_ShapePaintContainer = nullptr;
 
        public:
@@ -24,8 +23,6 @@ namespace rive
                }
                void addStop(GradientStop* stop);
                void update(ComponentDirt value) override;
-               bool paintsInWorldSpace() const;
-               void paintsInWorldSpace(bool value);
                void markGradientDirty();
                void markStopsDirty();
 
index 8c39bbca7d7c2a55cfd04df1bbeb6aeabd7ebc4c..c0a67c762e58ecab473cd11d343c172bcd1aa82d 100644 (file)
@@ -35,6 +35,11 @@ namespace rive
                virtual PathSpace pathSpace() const = 0;
 
                virtual void draw(Renderer* renderer, CommandPath* path) = 0;
+
+               /// Get the component that represents the ShapePaintMutator for this
+               /// ShapePaint. It'll be one of SolidColor, LinearGradient, or
+               /// RadialGradient.
+               Component* paint() const { return m_PaintMutator->component(); }
        };
 } // namespace rive
 
index 44b0d48b60590949e2c0a132e4c3eb358f07eea1..cbc32a538532b6f986e680e6f5c2cce722d5e038 100644 (file)
@@ -10,11 +10,13 @@ namespace rive
        private:
                float m_RenderOpacity = 1.0f;
                RenderPaint* m_RenderPaint = nullptr;
+               /// The Component providing this ShapePaintMutator interface.
+               Component* m_Component = nullptr;
 
        protected:
                /// Hook up this paint mutator as the mutator for the shape paint
                /// expected to be the parent.
-               bool initPaintMutator(Component* parent);
+               bool initPaintMutator(Component* component);
                virtual void renderOpacityChanged() = 0;
 
                RenderPaint* renderPaint() const { return m_RenderPaint; }
@@ -22,6 +24,8 @@ namespace rive
        public:
                float renderOpacity() const { return m_RenderOpacity; }
                void renderOpacity(float value);
+
+               Component* component() const { return m_Component; }
        };
 } // namespace rive
 #endif
\ No newline at end of file
index 07950fb54ebbebe7bbf2ac602084102ed787fff6..728fc99a1e78efdf74b5c881e06e9f72a435916d 100644 (file)
@@ -8,6 +8,8 @@ namespace rive
        protected:
                void widthChanged() override;
                void heightChanged() override;
+               void originXChanged() override;
+               void originYChanged() override;
        };
 } // namespace rive
 
index f292e89b7718c379faf04ebc6014687336bec304..5b071f6853c69ca458aacf407a31623c2318c0b9 100644 (file)
@@ -14,7 +14,10 @@ namespace rive
                void update(ComponentDirt value) override;
 
        protected:
-               void cornerRadiusChanged() override;
+               void cornerRadiusTLChanged() override;
+               void cornerRadiusTRChanged() override;
+               void cornerRadiusBLChanged() override;
+               void cornerRadiusBRChanged() override;
        };
 } // namespace rive
 
index 9b6c2d5824b80cfc56ab00b9a6566337fea1fd7c..ef73b36816d012f07541422904058bf218dc4d7c 100644 (file)
@@ -247,6 +247,12 @@ void RiveFrameExtractor::advanceFrame() { animation_instance->advance(ifps); }
 
 sk_sp<SkImage> RiveFrameExtractor::getSnapshot()
 {
+
+       // I see a canvas and I want to paint it black.
+       // (without this transparent background dont get cleared.)
+       SkPaint paint;
+       rasterCanvas->clear(SK_ColorBLACK);
+
        // hmm "no deafault constructor exists bla bla... "
        rive::SkiaRenderer renderer(rasterCanvas);
 
index fb26a059bd6a41b9480e1f806649fe503a4d157a..dc0fce4b1eca5cc35410b7e3c511f171e5243d1a 100644 (file)
@@ -23,9 +23,8 @@ void SkiaRenderPath::fillRule(FillRule value)
 void SkiaRenderPath::reset() { m_Path.reset(); }
 void SkiaRenderPath::addRenderPath(RenderPath* path, const Mat2D& transform)
 {
-       m_Path.addPath(
-           reinterpret_cast<SkiaRenderPath*>(path)->m_Path,
-           ToSkia::convert(transform));
+       m_Path.addPath(reinterpret_cast<SkiaRenderPath*>(path)->m_Path,
+                      ToSkia::convert(transform));
 }
 
 void SkiaRenderPath::moveTo(float x, float y) { m_Path.moveTo(x, y); }
index 179bcc1983f67edd623afef3716a9eb9de5df694..705986c713f356fe89e688c3e0019fbfaa287bfc 100644 (file)
@@ -41,7 +41,7 @@ bool LinearAnimationInstance::advance(float elapsedSeconds)
        switch (animation.loop())
        {
                case Loop::oneShot:
-                       if (frames > end)
+                       if (m_Direction == 1 && frames > end)
                        {
                                keepGoing = false;
                                m_SpilledTime = (frames - end) / fps;
@@ -49,13 +49,31 @@ bool LinearAnimationInstance::advance(float elapsedSeconds)
                                m_Time = frames / fps;
                                didLoop = true;
                        }
+                       else if (m_Direction == -1 && frames < start)
+                       {
+                               keepGoing = false;
+                               m_SpilledTime = (start - frames) / fps;
+                               frames = start;
+                               m_Time = frames / fps;
+                               didLoop = true;
+                       }
                        break;
                case Loop::loop:
-                       if (frames >= end)
+                       if (m_Direction == 1 && frames >= end)
                        {
                                m_SpilledTime = (frames - end) / fps;
                                frames = m_Time * fps;
                                frames = start + std::fmod(frames - start, range);
+
+                               m_Time = frames / fps;
+                               didLoop = true;
+                       }
+                       else if (m_Direction == -1 && frames <= start)
+                       {
+
+                               m_SpilledTime = (start - frames) / fps;
+                               frames = m_Time * fps;
+                               frames = end - std::abs(std::fmod(start - frames, range));
                                m_Time = frames / fps;
                                didLoop = true;
                        }
@@ -112,5 +130,7 @@ void LinearAnimationInstance::time(float value)
        m_TotalTime = value - start;
        m_LastTotalTime = m_TotalTime - diff;
 
+       // leaving this RIGHT now. but is this required? it kinda messes up
+       // playing things backwards and seeking. what purpose does it solve?
        m_Direction = 1;
 }
index 9a3863755360d8db4842db982b74aa64ff2ef3c9..d84309c4cc87112d3edbfddb0602aa73de55de50 100644 (file)
@@ -10,7 +10,6 @@
 #include "animation/entry_state.hpp"
 #include "animation/state_transition.hpp"
 #include "animation/transition_condition.hpp"
-#include "animation/linear_animation_instance.hpp"
 #include "animation/animation_state.hpp"
 
 using namespace rive;
@@ -30,6 +29,7 @@ namespace rive
                LinearAnimationInstance* m_AnimationInstance = nullptr;
                LinearAnimationInstance* m_AnimationInstanceFrom = nullptr;
                float m_Mix = 1.0f;
+               bool m_stateChangedOnAdvance = false;
 
        public:
                void init(const StateMachineLayer* layer)
@@ -38,9 +38,11 @@ namespace rive
                        m_CurrentState = m_Layer->entryState();
                }
 
-               bool advance(float seconds, SMIInput** inputs)
+               bool advance(float seconds, SMIInput** inputs, size_t inputCount)
                {
                        bool keepGoing = false;
+                       m_stateChangedOnAdvance = false;
+
                        if (m_AnimationInstance != nullptr)
                        {
                                keepGoing = m_AnimationInstance->advance(seconds);
@@ -68,6 +70,12 @@ namespace rive
 
                        for (int i = 0; updateState(inputs); i++)
                        {
+                               // Reset inputs between updates.
+                               for (size_t i = 0; i < inputCount; i++)
+                               {
+                                       inputs[i]->advanced();
+                               }
+
                                if (i == maxIterations)
                                {
                                        fprintf(stderr, "StateMachine exceeded max iterations.\n");
@@ -94,6 +102,7 @@ namespace rive
                                return false;
                        }
                        m_CurrentState = stateTo;
+                       m_stateChangedOnAdvance = true;
                        return true;
                }
 
@@ -220,10 +229,25 @@ namespace rive
                                    artboard, m_AnimationInstance->time(), m_Mix);
                        }
                }
+
+               bool stateChangedOnAdvance() const
+               {
+                       return m_stateChangedOnAdvance;
+               }
+
+               const LayerState* currentState() 
+               {
+                       return m_CurrentState;
+               }
+
+               const LinearAnimationInstance* currentAnimation() const
+               {
+                       return m_AnimationInstance;
+               }
        };
 } // namespace rive
 
-StateMachineInstance::StateMachineInstance(StateMachine* machine) :
+StateMachineInstance::StateMachineInstance(const StateMachine* machine) :
     m_Machine(machine)
 {
        m_InputCount = machine->inputCount();
@@ -280,7 +304,7 @@ bool StateMachineInstance::advance(float seconds)
        m_NeedsAdvance = false;
        for (int i = 0; i < m_LayerCount; i++)
        {
-               if (m_Layers[i].advance(seconds, m_InputInstances))
+               if (m_Layers[i].advance(seconds, m_InputInstances, m_InputCount))
                {
                        m_NeedsAdvance = true;
                }
@@ -350,4 +374,64 @@ SMITrigger* StateMachineInstance::getTrigger(std::string name) const
                }
        }
        return nullptr;
+}
+
+size_t StateMachineInstance::stateChangedCount() const
+{
+       size_t count = 0;
+       for (int i = 0; i < m_LayerCount; i++)
+       {
+               if (m_Layers[i].stateChangedOnAdvance())
+               {
+                       count++;
+               } 
+       }
+       return count;
+}
+
+const LayerState* StateMachineInstance::stateChangedByIndex(size_t index) const
+{
+       size_t count = 0;
+       for (int i = 0; i < m_LayerCount; i++)
+       {
+               if (m_Layers[i].stateChangedOnAdvance())
+               {
+                       if (count == index)
+                       {
+                               return m_Layers[i].currentState();
+                       }
+                       count++;
+               } 
+       }
+       return nullptr;
+}
+
+const size_t StateMachineInstance::currentAnimationCount() const
+{
+       size_t count = 0;
+       for (int i = 0; i < m_LayerCount; i++)
+       {
+               if(m_Layers[i].currentAnimation() != nullptr)
+               {
+                       count++;
+               }
+       }
+       return count;
+}
+
+const LinearAnimationInstance* StateMachineInstance::currentAnimationByIndex(size_t index) const
+{
+       size_t count = 0;
+       for (int i = 0; i < m_LayerCount; i++)
+       {
+               if (m_Layers[i].currentAnimation() != nullptr)
+               {
+                       if (count == index)
+                       {
+                               return m_Layers[i].currentAnimation();
+                       }
+                       count++;
+               } 
+       }
+       return nullptr;
 }
\ No newline at end of file
index 0f367bda4b01ce2e99bd94815eb685d3bf84ef9a..571270f628bc055baf98349f3388c8b8bdff7231 100644 (file)
@@ -429,6 +429,10 @@ void Artboard::draw(Renderer* renderer)
        for (auto drawable = m_FirstDrawable; drawable != nullptr;
             drawable = drawable->prev)
        {
+               if (drawable->isHidden())
+               {
+                       continue;
+               }
                drawable->draw(renderer);
        }
 
index e0725b35de1e445de1f59f3fa91e5ae4de91217e..66efb614ed9846e819bad47581d810cca7177af3 100644 (file)
@@ -1,5 +1,6 @@
 #include "core/binary_reader.hpp"
 #include "core/reader.h"
+#include <vector>
 
 using namespace rive;
 
@@ -11,7 +12,10 @@ BinaryReader::BinaryReader(uint8_t* bytes, size_t length) :
 {
 }
 
-bool BinaryReader::reachedEnd() const { return m_Position == m_End || didOverflow(); }
+bool BinaryReader::reachedEnd() const
+{
+       return m_Position == m_End || didOverflow();
+}
 
 size_t BinaryReader::lengthInBytes() const { return m_Length; }
 
@@ -44,7 +48,7 @@ std::string BinaryReader::readString()
                return std::string();
        }
 
-       char rawValue[length + 1];
+       std::vector<char> rawValue(length + 1);
        auto readBytes = decode_string(length, m_Position, m_End, &rawValue[0]);
        if (readBytes != length)
        {
@@ -52,7 +56,7 @@ std::string BinaryReader::readString()
                return std::string();
        }
        m_Position += readBytes;
-       return std::string(rawValue);
+       return std::string(rawValue.data(), length);
 }
 
 double BinaryReader::readFloat64()
index e0dbaa1c5b3d0d71cb38ea421840b143bb335460..1525353d910dfef02203150f4f29d357b573affb 100644 (file)
@@ -185,7 +185,8 @@ ImportResult File::read(BinaryReader& reader, const RuntimeHeader& header)
                                }
 
                                stackObject = new StateMachineLayerImporter(
-                                   object->as<StateMachineLayer>(), artboardImporter);
+                                   object->as<StateMachineLayer>(),
+                                   artboardImporter->artboard());
 
                                break;
                        }
index 80fa057b6e07c397d102bd48fb71017730610b1d..4ab1722b65c023242da517a3ec20f28fee875818 100644 (file)
@@ -6,9 +6,9 @@
 #include "artboard.hpp"
 
 using namespace rive;
-StateMachineLayerImporter::StateMachineLayerImporter(
-    StateMachineLayer* layer, ArtboardImporter* artboardImporter) :
-    m_Layer(layer), m_ArtboardImporter(artboardImporter)
+StateMachineLayerImporter::StateMachineLayerImporter(StateMachineLayer* layer,
+                                                     const Artboard* artboard) :
+    m_Layer(layer), m_Artboard(artboard)
 {
 }
 void StateMachineLayerImporter::addState(LayerState* state)
@@ -18,7 +18,7 @@ void StateMachineLayerImporter::addState(LayerState* state)
 
 StatusCode StateMachineLayerImporter::resolve()
 {
-       auto artboard = m_ArtboardImporter->artboard();
+
        for (auto state : m_Layer->m_States)
        {
                if (state->is<AnimationState>())
@@ -28,7 +28,7 @@ StatusCode StateMachineLayerImporter::resolve()
                        if (animationState->animationId() != -1)
                        {
                                animationState->m_Animation =
-                                   artboard->animation(animationState->animationId());
+                                   m_Artboard->animation(animationState->animationId());
                                if (animationState->m_Animation == nullptr)
                                {
                                        return StatusCode::MissingObject;
index f4d68ee2f610c2bc224a25ca9769f0511a7e8355..b23005d64190692c866e35be162ccb554bce33b3 100644 (file)
@@ -97,7 +97,11 @@ void ClippingShape::update(ComponentDirt value)
                m_RenderPath->fillRule((FillRule)fillRule());
                for (auto shape : m_Shapes)
                {
-                       m_RenderPath->addPath(shape->pathComposer()->worldPath(), identity);
+                       if (!shape->isHidden())
+                       {
+                               m_RenderPath->addPath(shape->pathComposer()->worldPath(),
+                                                     identity);
+                       }
                }
        }
 }
\ No newline at end of file
index b4ebde29649dffd86d54889e7a82966e8643182f..c3a26c1778eac3a3fa3bd3b875602498bd2b1f91 100644 (file)
@@ -55,16 +55,7 @@ void MetricsPath::cubicTo(
        m_Points.emplace_back(Vec2D(x, y));
 }
 
-void MetricsPath::close()
-{
-       if (m_Parts.back().type == PathPart::line)
-       {
-               // We auto close the last part if it's a cubic, if it's not then make
-               // sure to add the final part in so we can compute its length too.
-               m_Parts.push_back(PathPart(0, m_Points.size()));
-               m_Points.emplace_back(m_Points[0]);
-       }
-}
+void MetricsPath::close() {}
 
 static void computeHull(const Vec2D& from,
                         const Vec2D& fromOut,
@@ -158,7 +149,7 @@ float MetricsPath::computeLength(const Mat2D& transform)
        // transform is changing (path may not have been reset but got added with
        // another transform).
        m_TransformedPoints.resize(m_Points.size());
-       for (int i = 0, l = m_Points.size(); i < l; i++)
+       for (size_t i = 0, l = m_Points.size(); i < l; i++)
        {
                Vec2D::transform(m_TransformedPoints[i], m_Points[i], transform);
        }
index 72004d726d955beb9b9b30df610a109ffa0ce6f3..6f8bedba199b42ce2d11b15f7521068c60084fee 100644 (file)
@@ -5,6 +5,7 @@
 #include "shapes/paint/color.hpp"
 #include "shapes/paint/gradient_stop.hpp"
 #include "shapes/shape_paint_container.hpp"
+#include "shapes/paint/shape_paint.hpp"
 #include <algorithm>
 
 using namespace rive;
@@ -17,24 +18,13 @@ StatusCode LinearGradient::onAddedDirty(CoreContext* context)
                return code;
        }
 
-       if (!initPaintMutator(parent()))
+       if (!initPaintMutator(this))
        {
                return StatusCode::MissingObject;
        }
        return StatusCode::Ok;
 }
 
-bool LinearGradient::paintsInWorldSpace() const { return m_PaintsInWorldSpace; }
-void LinearGradient::paintsInWorldSpace(bool value)
-{
-       if (m_PaintsInWorldSpace == value)
-       {
-               return;
-       }
-       m_PaintsInWorldSpace = value;
-       addDirt(ComponentDirt::Paint);
-}
-
 void LinearGradient::buildDependencies()
 {
        auto p = parent();
@@ -72,6 +62,8 @@ void LinearGradient::update(ComponentDirt value)
 
        bool worldTransformed = hasDirt(value, ComponentDirt::WorldTransform);
 
+       bool paintsInWorldSpace =
+           parent()->as<ShapePaint>()->pathSpace() == PathSpace::World;
        // We rebuild the gradient if the gradient is dirty or we paint in world
        // space and the world space transform has changed, or the local transform
        // has changed. Local transform changes when a stop moves in local space.
@@ -79,8 +71,7 @@ void LinearGradient::update(ComponentDirt value)
            hasDirt(value,
                    ComponentDirt::Paint | ComponentDirt::RenderOpacity |
                        ComponentDirt::Transform) ||
-           (m_PaintsInWorldSpace && worldTransformed);
-
+           (paintsInWorldSpace && worldTransformed);
        if (rebuildGradient)
        {
                auto paint = renderPaint();
@@ -89,7 +80,7 @@ void LinearGradient::update(ComponentDirt value)
                // Check if we need to update the world space gradient (if there's no
                // shape container, presumably it's the artboard and we're already in
                // world).
-               if (m_PaintsInWorldSpace && m_ShapePaintContainer != nullptr)
+               if (paintsInWorldSpace && m_ShapePaintContainer != nullptr)
                {
                        // Get the start and end of the gradient in world coordinates (world
                        // transform of the shape).
index ac6e3a4d869746019152b1e0b67e82075e4298ee..c1263faa6ce2dac3b6eb729b8ed6f61e3d46bb4b 100644 (file)
@@ -4,12 +4,14 @@
 
 using namespace rive;
 
-bool ShapePaintMutator::initPaintMutator(Component* parent)
+bool ShapePaintMutator::initPaintMutator(Component* component)
 {
+       auto parent = component->parent();
+       m_Component = component;
        if (parent->is<ShapePaint>())
        {
-        // Set this object as the mutator for the shape paint and get a
-        // reference to the paint we'll be mutating.
+               // Set this object as the mutator for the shape paint and get a
+               // reference to the paint we'll be mutating.
                m_RenderPaint = parent->as<ShapePaint>()->initRenderPaint(this);
                return true;
        }
index cd095c33bebf33ccb16465b1e748011cc2323be6..987b31e9eeb32863bf535d410bf7020634d5eee9 100644 (file)
@@ -12,7 +12,7 @@ StatusCode SolidColor::onAddedDirty(CoreContext* context)
        {
                return code;
        }
-       if (!initPaintMutator(parent()))
+       if (!initPaintMutator(this))
        {
                return StatusCode::MissingObject;
        }
index e35da28c2e0204fe0d38a735a87ca95f5458f410..d5d7fe6b60946f6820c210abb3e0ef37281aaaa4 100644 (file)
@@ -3,4 +3,6 @@
 using namespace rive;
 
 void ParametricPath::widthChanged() { markPathDirty(); }
-void ParametricPath::heightChanged() { markPathDirty(); }
\ No newline at end of file
+void ParametricPath::heightChanged() { markPathDirty(); }
+void ParametricPath::originXChanged() { markPathDirty(); }
+void ParametricPath::originYChanged() { markPathDirty(); }
\ No newline at end of file
index c49ce80afe5bdc58e99eddbbd1b9b94259e6779c..179c8b7a27ce2d14e0ff37855c67fb1b8e3e573c 100644 (file)
@@ -263,6 +263,10 @@ static void buildPath(CommandPath& commandPath,
                {
                        commandPath.cubicTo(outX, outY, startInX, startInY, startX, startY);
                }
+               else
+               {
+                       commandPath.lineTo(startX, startY);
+               }
                commandPath.close();
        }
 }
index 367d86a53aa7c049763f15f063eef9afcb98634c..3cd739e40978176aa3883d92f2f97ae1de745933 100644 (file)
@@ -10,13 +10,17 @@ Rectangle::Rectangle()
        addVertex(&m_Vertex4);
 }
 
-void Rectangle::cornerRadiusChanged() { markPathDirty(); }
+void Rectangle::cornerRadiusTLChanged() { markPathDirty(); }
+void Rectangle::cornerRadiusTRChanged() { markPathDirty(); }
+void Rectangle::cornerRadiusBLChanged() { markPathDirty(); }
+void Rectangle::cornerRadiusBRChanged() { markPathDirty(); }
 
 void Rectangle::update(ComponentDirt value)
 {
        if (hasDirt(value, ComponentDirt::Path))
        {
-               auto radius = cornerRadius();
+               auto radius = cornerRadiusTL();
+               auto link = linkCornerRadius();
 
                auto ox = -originX() * width();
                auto oy = -originY() * height();
@@ -27,15 +31,15 @@ void Rectangle::update(ComponentDirt value)
 
                m_Vertex2.x(ox + width());
                m_Vertex2.y(oy);
-               m_Vertex2.radius(radius);
+               m_Vertex2.radius(link ? radius : cornerRadiusTR());
 
                m_Vertex3.x(ox + width());
                m_Vertex3.y(oy + height());
-               m_Vertex3.radius(radius);
+               m_Vertex3.radius(link ? radius : cornerRadiusBR());
 
                m_Vertex4.x(ox);
                m_Vertex4.y(oy + height());
-               m_Vertex4.radius(radius);
+               m_Vertex4.radius(link ? radius : cornerRadiusBL());
        }
 
        Super::update(value);
index fc4652327e55db97e6de01ea7bbc8cdbdc7e1772..614ab0fd67ad29749ba65e824d35192f02caa825 100644 (file)
@@ -1,4 +1,6 @@
-// The onlu purpose of this file is to DEFINE the catch config so it can include main()
+// The only purpose of this file is to DEFINE the catch config so it can include
+// main()
 
-#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file
+#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this
+                          // in one cpp file
 #include "catch.hpp"
\ No newline at end of file
index 94ae9d2af6f5030889cdf33534eed3697f48ba5a..73dcca012c4ea01ce3da7592700275abaa4e423e 100644 (file)
@@ -21,7 +21,7 @@ TEST_CASE("rectangle path builds expected commands", "[path]")
        rectangle->y(0.0f);
        rectangle->width(100.0f);
        rectangle->height(200.0f);
-       rectangle->cornerRadius(0.0f);
+       rectangle->cornerRadiusTL(0.0f);
 
        artboard->addObject(artboard);
        artboard->addObject(shape);
@@ -37,13 +37,14 @@ TEST_CASE("rectangle path builds expected commands", "[path]")
        auto path =
            reinterpret_cast<rive::NoOpRenderPath*>(rectangle->commandPath());
 
-       REQUIRE(path->commands.size() == 6);
+       REQUIRE(path->commands.size() == 7);
        REQUIRE(path->commands[0].command == rive::NoOpPathCommandType::Reset);
        REQUIRE(path->commands[1].command == rive::NoOpPathCommandType::MoveTo);
        REQUIRE(path->commands[2].command == rive::NoOpPathCommandType::LineTo);
        REQUIRE(path->commands[3].command == rive::NoOpPathCommandType::LineTo);
        REQUIRE(path->commands[4].command == rive::NoOpPathCommandType::LineTo);
-       REQUIRE(path->commands[5].command == rive::NoOpPathCommandType::Close);
+       REQUIRE(path->commands[5].command == rive::NoOpPathCommandType::LineTo);
+       REQUIRE(path->commands[6].command == rive::NoOpPathCommandType::Close);
 
        delete artboard;
 }
@@ -58,7 +59,8 @@ TEST_CASE("rounded rectangle path builds expected commands", "[path]")
        rectangle->y(0.0f);
        rectangle->width(100.0f);
        rectangle->height(200.0f);
-       rectangle->cornerRadius(20.0f);
+       rectangle->cornerRadiusTL(20.0f);
+       rectangle->linkCornerRadius(true);
 
        artboard->addObject(artboard);
        artboard->addObject(shape);
@@ -84,7 +86,7 @@ TEST_CASE("rounded rectangle path builds expected commands", "[path]")
 
        // close
 
-       REQUIRE(path->commands.size() == 10);
+       REQUIRE(path->commands.size() == 11);
 
        // Init
        REQUIRE(path->commands[0].command == rive::NoOpPathCommandType::Reset);
@@ -105,7 +107,9 @@ TEST_CASE("rounded rectangle path builds expected commands", "[path]")
        REQUIRE(path->commands[7].command == rive::NoOpPathCommandType::LineTo);
        REQUIRE(path->commands[8].command == rive::NoOpPathCommandType::CubicTo);
 
-       REQUIRE(path->commands[9].command == rive::NoOpPathCommandType::Close);
+       REQUIRE(path->commands[9].command == rive::NoOpPathCommandType::LineTo);
+
+       REQUIRE(path->commands[10].command == rive::NoOpPathCommandType::Close);
 
        delete artboard;
 }
index 853bc701ddd11a8d3da8d2650b3788e46513b7fc..2f56c77e933f926f700e11bb7b68844cf770b7ee 100644 (file)
@@ -92,6 +92,8 @@ TEST_CASE("file with state machine be read", "[file]")
        REQUIRE(smi.getBool("Press")->name() == "Press");
        REQUIRE(smi.getBool("Hover") != nullptr);
        REQUIRE(smi.getBool("Press") != nullptr);
+       REQUIRE(smi.stateChangedCount() == 0);
+       REQUIRE(smi.currentAnimationCount() == 0);
 
        delete file;
        delete[] bytes;