up-to-date submodule(rive-cpp) accepted/tizen/unified/20210610.141338 submit/tizen/20210531.044041 submit/tizen/20210609.083248
authorHermet Park <chuneon.park@samsung.com>
Mon, 24 May 2021 09:29:39 +0000 (18:29 +0900)
committerHermet Park <chuneon.park@samsung.com>
Tue, 25 May 2021 03:46:31 +0000 (12:46 +0900)
Change-Id: If411149f38e668ba01613845e6a2753fb554ade5

193 files changed:
meson.build
submodule/.vscode/tasks.json [new file with mode: 0644]
submodule/dev/core_generator/lib/src/configuration.dart
submodule/dev/core_generator/lib/src/definition.dart
submodule/dev/defs/animation/blend_animation.json [new file with mode: 0644]
submodule/dev/defs/animation/blend_animation_1d.json [new file with mode: 0644]
submodule/dev/defs/animation/blend_animation_direct.json [new file with mode: 0644]
submodule/dev/defs/animation/blend_state.json [new file with mode: 0644]
submodule/dev/defs/animation/blend_state_1d.json [new file with mode: 0644]
submodule/dev/defs/animation/blend_state_direct.json [new file with mode: 0644]
submodule/dev/defs/animation/blend_state_transition.json [new file with mode: 0644]
submodule/dev/test.sh
submodule/dev/test/premake5.lua
submodule/include/animation/animation.hpp
submodule/include/animation/animation_state.hpp
submodule/include/animation/animation_state_instance.hpp [new file with mode: 0644]
submodule/include/animation/blend_animation.hpp [new file with mode: 0644]
submodule/include/animation/blend_animation_1d.hpp [new file with mode: 0644]
submodule/include/animation/blend_animation_direct.hpp [new file with mode: 0644]
submodule/include/animation/blend_state.hpp [new file with mode: 0644]
submodule/include/animation/blend_state_1d.hpp [new file with mode: 0644]
submodule/include/animation/blend_state_1d_instance.hpp [new file with mode: 0644]
submodule/include/animation/blend_state_direct.hpp [new file with mode: 0644]
submodule/include/animation/blend_state_direct_instance.hpp [new file with mode: 0644]
submodule/include/animation/blend_state_instance.hpp [new file with mode: 0644]
submodule/include/animation/blend_state_transition.hpp [new file with mode: 0644]
submodule/include/animation/cubic_interpolator.hpp
submodule/include/animation/keyframe.hpp
submodule/include/animation/layer_state.hpp
submodule/include/animation/state_instance.hpp [new file with mode: 0644]
submodule/include/animation/state_machine_instance.hpp
submodule/include/animation/state_transition.hpp
submodule/include/animation/system_state_instance.hpp [new file with mode: 0644]
submodule/include/artboard.hpp
submodule/include/backboard.hpp
submodule/include/bones/weight.hpp
submodule/include/core.hpp
submodule/include/generated/animation/animation_base.hpp
submodule/include/generated/animation/animation_state_base.hpp
submodule/include/generated/animation/any_state_base.hpp
submodule/include/generated/animation/blend_animation_1d_base.hpp [new file with mode: 0644]
submodule/include/generated/animation/blend_animation_base.hpp [new file with mode: 0644]
submodule/include/generated/animation/blend_animation_direct_base.hpp [new file with mode: 0644]
submodule/include/generated/animation/blend_state_1d_base.hpp [new file with mode: 0644]
submodule/include/generated/animation/blend_state_base.hpp [new file with mode: 0644]
submodule/include/generated/animation/blend_state_direct_base.hpp [new file with mode: 0644]
submodule/include/generated/animation/blend_state_transition_base.hpp [new file with mode: 0644]
submodule/include/generated/animation/cubic_interpolator_base.hpp
submodule/include/generated/animation/entry_state_base.hpp
submodule/include/generated/animation/exit_state_base.hpp
submodule/include/generated/animation/keyed_object_base.hpp
submodule/include/generated/animation/keyed_property_base.hpp
submodule/include/generated/animation/keyframe_base.hpp
submodule/include/generated/animation/keyframe_color_base.hpp
submodule/include/generated/animation/keyframe_double_base.hpp
submodule/include/generated/animation/keyframe_id_base.hpp
submodule/include/generated/animation/linear_animation_base.hpp
submodule/include/generated/animation/state_machine_base.hpp
submodule/include/generated/animation/state_machine_bool_base.hpp
submodule/include/generated/animation/state_machine_component_base.hpp
submodule/include/generated/animation/state_machine_layer_base.hpp
submodule/include/generated/animation/state_machine_layer_component_base.hpp
submodule/include/generated/animation/state_machine_number_base.hpp
submodule/include/generated/animation/state_machine_trigger_base.hpp
submodule/include/generated/animation/state_transition_base.hpp
submodule/include/generated/animation/transition_bool_condition_base.hpp
submodule/include/generated/animation/transition_condition_base.hpp
submodule/include/generated/animation/transition_number_condition_base.hpp
submodule/include/generated/animation/transition_trigger_condition_base.hpp
submodule/include/generated/animation/transition_value_condition_base.hpp
submodule/include/generated/artboard_base.hpp
submodule/include/generated/backboard_base.hpp
submodule/include/generated/bones/bone_base.hpp
submodule/include/generated/bones/cubic_weight_base.hpp
submodule/include/generated/bones/root_bone_base.hpp
submodule/include/generated/bones/skin_base.hpp
submodule/include/generated/bones/tendon_base.hpp
submodule/include/generated/bones/weight_base.hpp
submodule/include/generated/component_base.hpp
submodule/include/generated/core_registry.hpp
submodule/include/generated/draw_rules_base.hpp
submodule/include/generated/draw_target_base.hpp
submodule/include/generated/drawable_base.hpp
submodule/include/generated/node_base.hpp
submodule/include/generated/shapes/clipping_shape_base.hpp
submodule/include/generated/shapes/cubic_asymmetric_vertex_base.hpp
submodule/include/generated/shapes/cubic_detached_vertex_base.hpp
submodule/include/generated/shapes/cubic_mirrored_vertex_base.hpp
submodule/include/generated/shapes/ellipse_base.hpp
submodule/include/generated/shapes/paint/fill_base.hpp
submodule/include/generated/shapes/paint/gradient_stop_base.hpp
submodule/include/generated/shapes/paint/linear_gradient_base.hpp
submodule/include/generated/shapes/paint/radial_gradient_base.hpp
submodule/include/generated/shapes/paint/shape_paint_base.hpp
submodule/include/generated/shapes/paint/solid_color_base.hpp
submodule/include/generated/shapes/paint/stroke_base.hpp
submodule/include/generated/shapes/paint/trim_path_base.hpp
submodule/include/generated/shapes/parametric_path_base.hpp
submodule/include/generated/shapes/path_base.hpp
submodule/include/generated/shapes/path_vertex_base.hpp
submodule/include/generated/shapes/points_path_base.hpp
submodule/include/generated/shapes/polygon_base.hpp
submodule/include/generated/shapes/rectangle_base.hpp
submodule/include/generated/shapes/shape_base.hpp
submodule/include/generated/shapes/star_base.hpp
submodule/include/generated/shapes/straight_vertex_base.hpp
submodule/include/generated/shapes/triangle_base.hpp
submodule/include/generated/transform_component_base.hpp
submodule/include/importers/layer_state_importer.hpp
submodule/include/shapes/paint/gradient_stop.hpp
submodule/include/shapes/paint/linear_gradient.hpp
submodule/include/shapes/paint/radial_gradient.hpp
submodule/include/shapes/paint/solid_color.hpp
submodule/include/shapes/path.hpp
submodule/include/shapes/path_composer.hpp
submodule/include/shapes/path_vertex.hpp
submodule/skia/recorder/src/extractor.cpp
submodule/skia/viewer/src/main.cpp
submodule/src/animation/animation_state.cpp
submodule/src/animation/animation_state_instance.cpp [new file with mode: 0644]
submodule/src/animation/blend_animation.cpp [new file with mode: 0644]
submodule/src/animation/blend_animation_1d.cpp [new file with mode: 0644]
submodule/src/animation/blend_animation_direct.cpp [new file with mode: 0644]
submodule/src/animation/blend_state.cpp [new file with mode: 0644]
submodule/src/animation/blend_state_1d.cpp [new file with mode: 0644]
submodule/src/animation/blend_state_1d_instance.cpp [new file with mode: 0644]
submodule/src/animation/blend_state_direct.cpp [new file with mode: 0644]
submodule/src/animation/blend_state_direct_instance.cpp [new file with mode: 0644]
submodule/src/animation/blend_state_transition.cpp [new file with mode: 0644]
submodule/src/animation/layer_state.cpp
submodule/src/animation/state_instance.cpp [new file with mode: 0644]
submodule/src/animation/state_machine.cpp
submodule/src/animation/state_machine_instance.cpp
submodule/src/animation/state_transition.cpp
submodule/src/animation/system_state_instance.cpp [new file with mode: 0644]
submodule/src/file.cpp
submodule/src/generated/animation/animation_base.cpp [new file with mode: 0644]
submodule/src/generated/animation/animation_state_base.cpp [new file with mode: 0644]
submodule/src/generated/animation/any_state_base.cpp [new file with mode: 0644]
submodule/src/generated/animation/cubic_interpolator_base.cpp [new file with mode: 0644]
submodule/src/generated/animation/entry_state_base.cpp [new file with mode: 0644]
submodule/src/generated/animation/exit_state_base.cpp [new file with mode: 0644]
submodule/src/generated/animation/keyed_object_base.cpp [new file with mode: 0644]
submodule/src/generated/animation/keyed_property_base.cpp [new file with mode: 0644]
submodule/src/generated/animation/keyframe_color_base.cpp [new file with mode: 0644]
submodule/src/generated/animation/keyframe_double_base.cpp [new file with mode: 0644]
submodule/src/generated/animation/keyframe_id_base.cpp [new file with mode: 0644]
submodule/src/generated/animation/linear_animation_base.cpp [new file with mode: 0644]
submodule/src/generated/animation/state_machine_base.cpp [new file with mode: 0644]
submodule/src/generated/animation/state_machine_bool_base.cpp [new file with mode: 0644]
submodule/src/generated/animation/state_machine_layer_base.cpp [new file with mode: 0644]
submodule/src/generated/animation/state_machine_number_base.cpp [new file with mode: 0644]
submodule/src/generated/animation/state_machine_trigger_base.cpp [new file with mode: 0644]
submodule/src/generated/animation/state_transition_base.cpp [new file with mode: 0644]
submodule/src/generated/animation/transition_bool_condition_base.cpp [new file with mode: 0644]
submodule/src/generated/animation/transition_number_condition_base.cpp [new file with mode: 0644]
submodule/src/generated/animation/transition_trigger_condition_base.cpp [new file with mode: 0644]
submodule/src/generated/artboard_base.cpp [new file with mode: 0644]
submodule/src/generated/backboard_base.cpp [new file with mode: 0644]
submodule/src/generated/bones/bone_base.cpp [new file with mode: 0644]
submodule/src/generated/bones/cubic_weight_base.cpp [new file with mode: 0644]
submodule/src/generated/bones/root_bone_base.cpp [new file with mode: 0644]
submodule/src/generated/bones/skin_base.cpp [new file with mode: 0644]
submodule/src/generated/bones/tendon_base.cpp [new file with mode: 0644]
submodule/src/generated/bones/weight_base.cpp [new file with mode: 0644]
submodule/src/generated/draw_rules_base.cpp [new file with mode: 0644]
submodule/src/generated/draw_target_base.cpp [new file with mode: 0644]
submodule/src/generated/node_base.cpp [new file with mode: 0644]
submodule/src/generated/shapes/clipping_shape_base.cpp [new file with mode: 0644]
submodule/src/generated/shapes/cubic_asymmetric_vertex_base.cpp [new file with mode: 0644]
submodule/src/generated/shapes/cubic_detached_vertex_base.cpp [new file with mode: 0644]
submodule/src/generated/shapes/cubic_mirrored_vertex_base.cpp [new file with mode: 0644]
submodule/src/generated/shapes/ellipse_base.cpp [new file with mode: 0644]
submodule/src/generated/shapes/paint/fill_base.cpp [new file with mode: 0644]
submodule/src/generated/shapes/paint/gradient_stop_base.cpp [new file with mode: 0644]
submodule/src/generated/shapes/paint/linear_gradient_base.cpp [new file with mode: 0644]
submodule/src/generated/shapes/paint/radial_gradient_base.cpp [new file with mode: 0644]
submodule/src/generated/shapes/paint/solid_color_base.cpp [new file with mode: 0644]
submodule/src/generated/shapes/paint/stroke_base.cpp [new file with mode: 0644]
submodule/src/generated/shapes/paint/trim_path_base.cpp [new file with mode: 0644]
submodule/src/generated/shapes/points_path_base.cpp [new file with mode: 0644]
submodule/src/generated/shapes/polygon_base.cpp [new file with mode: 0644]
submodule/src/generated/shapes/rectangle_base.cpp [new file with mode: 0644]
submodule/src/generated/shapes/shape_base.cpp [new file with mode: 0644]
submodule/src/generated/shapes/star_base.cpp [new file with mode: 0644]
submodule/src/generated/shapes/straight_vertex_base.cpp [new file with mode: 0644]
submodule/src/generated/shapes/triangle_base.cpp [new file with mode: 0644]
submodule/src/importers/layer_state_importer.cpp
submodule/src/shapes/paint/trim_path.cpp
submodule/src/shapes/path.cpp
submodule/test/assets/blend_test.riv [new file with mode: 0644]
submodule/test/assets/multiple_state_machines.riv [new file with mode: 0644]
submodule/test/state_machine_test.cpp

index d53229b..f4c53dd 100644 (file)
@@ -230,6 +230,17 @@ rive_cpp_src = [
    'submodule/src/math/aabb.cpp',
    'submodule/src/math/vec2d.cpp',
    'submodule/src/math/mat2d.cpp',
+   'submodule/src/animation/animation_state.cpp',
+   'submodule/src/animation/animation_state_instance.cpp',
+   'submodule/src/animation/blend_animation.cpp',
+   'submodule/src/animation/blend_animation_1d.cpp',
+   'submodule/src/animation/blend_animation_direct.cpp',
+   'submodule/src/animation/blend_state.cpp',
+   'submodule/src/animation/blend_state_1d.cpp',
+   'submodule/src/animation/blend_state_1d_instance.cpp',
+   'submodule/src/animation/blend_state_direct.cpp',
+   'submodule/src/animation/blend_state_direct_instance.cpp',
+   'submodule/src/animation/blend_state_transition.cpp',
    'submodule/src/animation/cubic_interpolator.cpp',
    'submodule/src/animation/keyed_object.cpp',
    'submodule/src/animation/keyed_property.cpp',
@@ -240,12 +251,14 @@ rive_cpp_src = [
    'submodule/src/animation/linear_animation.cpp',
    'submodule/src/animation/linear_animation_instance.cpp',
    'submodule/src/animation/layer_state.cpp',
+   'submodule/src/animation/state_instance.cpp',
    'submodule/src/animation/state_machine.cpp',
    'submodule/src/animation/state_machine_input.cpp',
    'submodule/src/animation/state_machine_input_instance.cpp',
    'submodule/src/animation/state_machine_instance.cpp',
    'submodule/src/animation/state_machine_layer.cpp',
    'submodule/src/animation/state_transition.cpp',
+   'submodule/src/animation/system_state_instance.cpp',
    'submodule/src/animation/transition_bool_condition.cpp',
    'submodule/src/animation/transition_condition.cpp',
    'submodule/src/animation/transition_number_condition.cpp',
@@ -309,6 +322,57 @@ rive_cpp_src = [
    'submodule/src/layout.cpp',
    'submodule/src/node.cpp',
    'submodule/src/transform_component.cpp',
+   'submodule/src/generated/artboard_base.cpp',
+   'submodule/src/generated/backboard_base.cpp',
+   'submodule/src/generated/draw_rules_base.cpp',
+   'submodule/src/generated/draw_target_base.cpp',
+   'submodule/src/generated/node_base.cpp',
+   'submodule/src/generated/animation/animation_base.cpp',
+   'submodule/src/generated/animation/linear_animation_base.cpp',
+   'submodule/src/generated/animation/animation_state_base.cpp',
+   'submodule/src/generated/animation/state_machine_base.cpp',
+   'submodule/src/generated/animation/any_state_base.cpp',
+   'submodule/src/generated/animation/state_machine_bool_base.cpp',
+   'submodule/src/generated/animation/cubic_interpolator_base.cpp',
+   'submodule/src/generated/animation/state_machine_layer_base.cpp',
+   'submodule/src/generated/animation/entry_state_base.cpp',
+   'submodule/src/generated/animation/state_machine_number_base.cpp',
+   'submodule/src/generated/animation/exit_state_base.cpp',
+   'submodule/src/generated/animation/state_machine_trigger_base.cpp',
+   'submodule/src/generated/animation/keyed_object_base.cpp',
+   'submodule/src/generated/animation/state_transition_base.cpp',
+   'submodule/src/generated/animation/keyed_property_base.cpp',
+   'submodule/src/generated/animation/transition_bool_condition_base.cpp',
+   'submodule/src/generated/animation/keyframe_color_base.cpp',
+   'submodule/src/generated/animation/transition_number_condition_base.cpp',
+   'submodule/src/generated/animation/keyframe_double_base.cpp',
+   'submodule/src/generated/animation/transition_trigger_condition_base.cpp',
+   'submodule/src/generated/animation/keyframe_id_base.cpp',
+   'submodule/src/generated/bones/bone_base.cpp',
+   'submodule/src/generated/bones/cubic_weight_base.cpp',
+   'submodule/src/generated/bones/root_bone_base.cpp',
+   'submodule/src/generated/bones/skin_base.cpp',
+   'submodule/src/generated/bones/tendon_base.cpp',
+   'submodule/src/generated/bones/weight_base.cpp',
+   'submodule/src/generated/shapes/clipping_shape_base.cpp',
+   'submodule/src/generated/shapes/cubic_asymmetric_vertex_base.cpp',
+   'submodule/src/generated/shapes/cubic_detached_vertex_base.cpp',
+   'submodule/src/generated/shapes/cubic_mirrored_vertex_base.cpp',
+   'submodule/src/generated/shapes/ellipse_base.cpp',
+   'submodule/src/generated/shapes/points_path_base.cpp',
+   'submodule/src/generated/shapes/polygon_base.cpp',
+   'submodule/src/generated/shapes/rectangle_base.cpp',
+   'submodule/src/generated/shapes/shape_base.cpp',
+   'submodule/src/generated/shapes/star_base.cpp',
+   'submodule/src/generated/shapes/straight_vertex_base.cpp',
+   'submodule/src/generated/shapes/triangle_base.cpp',
+   'submodule/src/generated/shapes/paint/fill_base.cpp',
+   'submodule/src/generated/shapes/paint/gradient_stop_base.cpp',
+   'submodule/src/generated/shapes/paint/linear_gradient_base.cpp',
+   'submodule/src/generated/shapes/paint/radial_gradient_base.cpp',
+   'submodule/src/generated/shapes/paint/solid_color_base.cpp',
+   'submodule/src/generated/shapes/paint/stroke_base.cpp',
+   'submodule/src/generated/shapes/paint/trim_path_base.cpp',
 ]
 
 rive_cpp_dep = declare_dependency(
diff --git a/submodule/.vscode/tasks.json b/submodule/.vscode/tasks.json
new file mode 100644 (file)
index 0000000..c62cb09
--- /dev/null
@@ -0,0 +1,36 @@
+{
+    // See https://go.microsoft.com/fwlink/?LinkId=733558
+    // for the documentation about the tasks.json format
+    "version": "2.0.0",
+    "tasks": [{
+            "label": "run tests",
+            "type": "shell",
+            "command": "cd dev && ./test.sh",
+            "group": "test",
+            "presentation": {
+                "reveal": "always",
+                "panel": "new"
+            }
+        },
+        {
+            "label": "gen assembly",
+            "type": "shell",
+            "command": "mkdir -p ${workspaceFolder}/build/bin/assembly/${fileDirname}/ && clang++ -O3 -g -std=c++17 -o ${workspaceFolder}/build/bin/assembly/${fileDirname}/${fileBasenameNoExtension}.S -Wall -fno-exceptions -fno-rtti -I${workspaceFolder}/include -S ${file}",
+            "group": "test",
+            "presentation": {
+                "reveal": "silent",
+                "panel": "new"
+            }
+        },
+        {
+            "label": "disassemble",
+            "command": "${command:disasexpl.show}",
+            "dependsOn": ["gen assembly"],
+            "problemMatcher": [],
+            "presentation": {
+                "reveal": "silent",
+                "panel": "new"
+            }
+        }
+    ]
+}
\ No newline at end of file
index 14b8b67..93450f4 100644 (file)
@@ -1,3 +1,4 @@
 String defsPath = "./defs/";
 String generatedHppPath = "../include/generated/";
 String concreteHppPath = "../include/";
+String generatedCppPath = "../src/generated/";
index d1a2042..7c712b1 100644 (file)
@@ -94,7 +94,7 @@ class Definition {
 
   String get localCodeFilename => '${stripExtension(_filename)}_base.hpp';
   String get concreteCodeFilename => '${stripExtension(_filename)}.hpp';
-  String get codeFilename => 'lib/src/generated/$localCodeFilename';
+  String get localCppCodeFilename => '${stripExtension(_filename)}_base.cpp';
 
   /// Generates cpp header code based on the Definition
   Future<void> generateCode() async {
@@ -190,7 +190,23 @@ class Definition {
       }
     }
 
+    if (!_isAbstract) {
+      code.writeln('Core* clone() const override;');
+    }
+
     if (properties.isNotEmpty || _extensionOf == null) {
+      code.writeln('void copy(const ${_name}Base& object) {');
+      for (final property in properties) {
+        code.writeln('m_${property.capitalizedName} = '
+            'object.m_${property.capitalizedName};');
+      }
+      if (_extensionOf != null) {
+        code.writeln('${_extensionOf.name}::'
+            'copy(object); ');
+      }
+      code.writeln('}');
+      code.writeln();
+
       code.writeln('bool deserialize(uint16_t propertyKey, '
           'BinaryReader& reader) override {');
 
@@ -243,6 +259,23 @@ class Definition {
           await _formatter.formatAndGuard(_name, concreteCode.toString());
       concreteFile.writeAsStringSync(formattedCode, flush: true);
     }
+    if (!_isAbstract) {
+      StringBuffer cppCode = StringBuffer();
+      cppCode.writeln('#include "generated/$localCodeFilename"');
+      cppCode.writeln('#include "$concreteCodeFilename"');
+      cppCode.writeln();
+      cppCode.writeln('using namespace rive;');
+      cppCode.writeln();
+      cppCode.writeln('Core* ${_name}Base::clone() const { '
+          'auto cloned = new $_name(); '
+          'cloned->copy(*this); '
+          'return cloned; '
+          '}');
+      var cppFile = File('$generatedCppPath$localCppCodeFilename');
+      cppFile.createSync(recursive: true);
+      var formattedCode = await _formatter.format(cppCode.toString());
+      cppFile.writeAsStringSync(formattedCode, flush: true);
+    }
   }
 
   @override
diff --git a/submodule/dev/defs/animation/blend_animation.json b/submodule/dev/defs/animation/blend_animation.json
new file mode 100644 (file)
index 0000000..86ccdb5
--- /dev/null
@@ -0,0 +1,43 @@
+{
+  "name": "BlendAnimation",
+  "key": {
+    "int": 74,
+    "string": "blendanimation"
+  },
+  "abstract": true,
+  "properties": {
+    "blendStateId": {
+      "type": "Id",
+      "typeRuntime": "uint",
+      "initialValue": "Core.missingId",
+      "initialValueRuntime": "-1",
+      "key": {
+        "int": 170,
+        "string": "blendstateid"
+      },
+      "description": "Id of the BlendState that this BlendAnimation belongs to.",
+      "runtime": false
+    },
+    "animationId": {
+      "type": "Id",
+      "typeRuntime": "uint",
+      "initialValue": "Core.missingId",
+      "initialValueRuntime": "-1",
+      "key": {
+        "int": 165,
+        "string": "animationid"
+      },
+      "description": "Id of the animation this BlendAnimation references."
+    },
+    "animationOrder": {
+      "type": "FractionalIndex",
+      "initialValue": "FractionalIndex.invalid",
+      "key": {
+        "int": 169,
+        "string": "animationorder"
+      },
+      "description": "Order value for sorting animations in blend states.",
+      "runtime": false
+    }
+  }
+}
\ No newline at end of file
diff --git a/submodule/dev/defs/animation/blend_animation_1d.json b/submodule/dev/defs/animation/blend_animation_1d.json
new file mode 100644 (file)
index 0000000..651acc5
--- /dev/null
@@ -0,0 +1,18 @@
+{
+  "name": "BlendAnimation1D",
+  "key": {
+    "int": 75,
+    "string": "blendanimation1d"
+  },
+  "extends": "animation/blend_animation.json",
+  "properties": {
+    "value": {
+      "type": "double",
+      "initialValue": "0",
+      "key": {
+        "int": 166,
+        "string": "value"
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/submodule/dev/defs/animation/blend_animation_direct.json b/submodule/dev/defs/animation/blend_animation_direct.json
new file mode 100644 (file)
index 0000000..af3601b
--- /dev/null
@@ -0,0 +1,21 @@
+{
+  "name": "BlendAnimationDirect",
+  "key": {
+    "int": 77,
+    "string": "blendanimationdirect"
+  },
+  "extends": "animation/blend_animation.json",
+  "properties": {
+    "inputId": {
+      "type": "Id",
+      "typeRuntime": "uint",
+      "initialValue": "Core.missingId",
+      "initialValueRuntime": "-1",
+      "key": {
+        "int": 168,
+        "string": "inputid"
+      },
+      "description": "Id of the input that drives the direct mix value for this animation."
+    }
+  }
+}
\ No newline at end of file
diff --git a/submodule/dev/defs/animation/blend_state.json b/submodule/dev/defs/animation/blend_state.json
new file mode 100644 (file)
index 0000000..635675c
--- /dev/null
@@ -0,0 +1,9 @@
+{
+  "name": "BlendState",
+  "key": {
+    "int": 72,
+    "string": "blendstate"
+  },
+  "abstract": true,
+  "extends": "animation/layer_state.json"
+}
\ No newline at end of file
diff --git a/submodule/dev/defs/animation/blend_state_1d.json b/submodule/dev/defs/animation/blend_state_1d.json
new file mode 100644 (file)
index 0000000..f575bbc
--- /dev/null
@@ -0,0 +1,22 @@
+{
+  "name": "BlendState1D",
+  "key": {
+    "int": 76,
+    "string": "blendstate1d"
+  },
+  "extends": "animation/blend_state.json",
+  "generic": "animation/blend_animation_1d.json",
+  "properties": {
+    "inputId": {
+      "type": "Id",
+      "typeRuntime": "uint",
+      "initialValue": "Core.missingId",
+      "initialValueRuntime": "-1",
+      "key": {
+        "int": 167,
+        "string": "inputid"
+      },
+      "description": "Id of the input that drives the mix value for this blend state."
+    }
+  }
+}
\ No newline at end of file
diff --git a/submodule/dev/defs/animation/blend_state_direct.json b/submodule/dev/defs/animation/blend_state_direct.json
new file mode 100644 (file)
index 0000000..869dc90
--- /dev/null
@@ -0,0 +1,9 @@
+{
+  "name": "BlendStateDirect",
+  "key": {
+    "int": 73,
+    "string": "blendstatedirect"
+  },
+  "extends": "animation/blend_state.json",
+  "generic": "animation/blend_animation_direct.json"
+}
\ No newline at end of file
diff --git a/submodule/dev/defs/animation/blend_state_transition.json b/submodule/dev/defs/animation/blend_state_transition.json
new file mode 100644 (file)
index 0000000..45a0425
--- /dev/null
@@ -0,0 +1,21 @@
+{
+  "name": "BlendStateTransition",
+  "key": {
+    "int": 78,
+    "string": "blendstatetransition"
+  },
+  "extends": "animation/state_transition.json",
+  "properties": {
+    "exitBlendAnimationId": {
+      "type": "Id",
+      "typeRuntime": "uint",
+      "initialValue": "Core.missingId",
+      "initialValueRuntime": "-1",
+      "key": {
+        "int": 171,
+        "string": "exitblendanimationid"
+      },
+      "description": "Id of the state the blend state animation used for exit time calculation."
+    }
+  }
+}
\ No newline at end of file
index 4f7acb9..4ca0d90 100755 (executable)
@@ -19,6 +19,11 @@ then
   echo Will perform memory checks...
   UTILITY='valgrind --leak-check=full'
   shift
+elif [ "$OPTION" = "debug" ]
+then
+  echo Starting debugger...
+  UTILITY='lldb'
+  shift
 fi
 
 premake5 gmake2 || exit 1
index 827187e..5fcf6b5 100644 (file)
@@ -1,23 +1,20 @@
 -- require "lfs"
-
 -- Clean Function --
 newaction {
-    trigger     = "clean",
+    trigger = "clean",
     description = "clean the build",
-    execute     = function ()
-       print("clean the build...")
-       os.rmdir("build")
-       os.remove("Makefile")
-       -- no wildcards in os.remove, so use shell
-       os.execute("rm *.make")
-       print("build cleaned")
+    execute = function()
+        print("clean the build...")
+        os.rmdir("build")
+        os.remove("Makefile")
+        -- no wildcards in os.remove, so use shell
+        os.execute("rm *.make")
+        print("build cleaned")
     end
- }
+}
 
 workspace "rive_tests"
-configurations {
-    "debug"
-}
+configurations {"debug"}
 
 project("tests")
 kind "ConsoleApp"
@@ -26,29 +23,19 @@ cppdialect "C++17"
 targetdir "build/bin/%{cfg.buildcfg}"
 objdir "build/obj/%{cfg.buildcfg}"
 
-buildoptions {
-    "-Wall", 
-    "-fno-exceptions", 
-    "-fno-rtti"
-}
+buildoptions {"-Wall", "-fno-exceptions", "-fno-rtti"}
 
-includedirs {
-    "./include",
-    "../../include"
-}
+includedirs {"./include", "../../include"}
 
-files {
-    "../../src/**.cpp", -- the Rive runtime source
-    "../../test/**.cpp" -- the tests
-    
+files {"../../src/**.cpp", -- the Rive runtime source
+"../../test/**.cpp" -- the tests
 }
 
-defines { "TESTING" }
+defines {"TESTING", "ENABLE_QUERY_FLAT_VERTICES"}
 
 filter "configurations:debug"
-    defines { "DEBUG" }
-    symbols "On"
-
+defines {"DEBUG"}
+symbols "On"
 
 --[[
 
@@ -183,4 +170,4 @@ for cppFile in getFilesByExtension(".cpp", "../../rive/test/") do
     test_precompiled(cppFile)
 end
 
---]]
\ No newline at end of file
+--]]
index b7abf6a..b08a8d1 100644 (file)
@@ -5,15 +5,6 @@ namespace rive
 {
        class Animation : public AnimationBase
        {
-       public:
-               StatusCode onAddedDirty(CoreContext* context) override
-               {
-                       return StatusCode::Ok;
-               }
-               StatusCode onAddedClean(CoreContext* context) override
-               {
-                       return StatusCode::Ok;
-               }
        };
 } // namespace rive
 
index 93a539e..a6328e3 100644 (file)
@@ -13,10 +13,11 @@ namespace rive
                friend class StateMachineLayerImporter;
 
        private:
-               LinearAnimation* m_Animation;
+               LinearAnimation* m_Animation = nullptr;
 
        public:
                const LinearAnimation* animation() const { return m_Animation; }
+               StateInstance* makeInstance() const override;
        };
 } // namespace rive
 
diff --git a/submodule/include/animation/animation_state_instance.hpp b/submodule/include/animation/animation_state_instance.hpp
new file mode 100644 (file)
index 0000000..ddcba1d
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef _RIVE_ANIMATION_STATE_INSTANCE_HPP_
+#define _RIVE_ANIMATION_STATE_INSTANCE_HPP_
+
+#include <string>
+#include "animation/state_instance.hpp"
+#include "animation/linear_animation_instance.hpp"
+
+namespace rive
+{
+       class AnimationState;
+
+       /// Represents an instance of an animation state.
+       class AnimationStateInstance : public StateInstance
+       {
+       private:
+               LinearAnimationInstance m_AnimationInstance;
+               bool m_KeepGoing;
+
+       public:
+               AnimationStateInstance(const AnimationState* animationState);
+
+               void advance(float seconds, SMIInput** inputs) override;
+               void apply(Artboard* artboard, float mix) override;
+
+               bool keepGoing() const override;
+
+               const LinearAnimationInstance* animationInstance() const
+               {
+                       return &m_AnimationInstance;
+               }
+
+               LinearAnimationInstance* animationInstance()
+               {
+                       return &m_AnimationInstance;
+               }
+       };
+} // namespace rive
+#endif
\ No newline at end of file
diff --git a/submodule/include/animation/blend_animation.hpp b/submodule/include/animation/blend_animation.hpp
new file mode 100644 (file)
index 0000000..576c67b
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef _RIVE_BLEND_ANIMATION_HPP_
+#define _RIVE_BLEND_ANIMATION_HPP_
+#include "generated/animation/blend_animation_base.hpp"
+namespace rive
+{
+       class LinearAnimation;
+       class BlendAnimation : public BlendAnimationBase
+       {
+       private:
+               LinearAnimation* m_Animation = nullptr;
+
+       public:
+               const LinearAnimation* animation() const { return m_Animation; }
+               StatusCode import(ImportStack& importStack) override;
+       };
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/submodule/include/animation/blend_animation_1d.hpp b/submodule/include/animation/blend_animation_1d.hpp
new file mode 100644 (file)
index 0000000..62d3121
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef _RIVE_BLEND_ANIMATION1_D_HPP_
+#define _RIVE_BLEND_ANIMATION1_D_HPP_
+#include "generated/animation/blend_animation_1d_base.hpp"
+#include <stdio.h>
+namespace rive
+{
+       class BlendAnimation1D : public BlendAnimation1DBase
+       {
+       public:
+               StatusCode onAddedDirty(CoreContext* context) override;
+               StatusCode onAddedClean(CoreContext* context) override;
+       };
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/submodule/include/animation/blend_animation_direct.hpp b/submodule/include/animation/blend_animation_direct.hpp
new file mode 100644 (file)
index 0000000..910a45f
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef _RIVE_BLEND_ANIMATION_DIRECT_HPP_
+#define _RIVE_BLEND_ANIMATION_DIRECT_HPP_
+#include "generated/animation/blend_animation_direct_base.hpp"
+#include <stdio.h>
+namespace rive
+{
+       class BlendAnimationDirect : public BlendAnimationDirectBase
+       {
+       public:
+               StatusCode onAddedDirty(CoreContext* context) override;
+               StatusCode onAddedClean(CoreContext* context) override;
+               StatusCode import(ImportStack& importStack) override;
+       };
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/submodule/include/animation/blend_state.hpp b/submodule/include/animation/blend_state.hpp
new file mode 100644 (file)
index 0000000..9fc11c2
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef _RIVE_BLEND_STATE_HPP_
+#define _RIVE_BLEND_STATE_HPP_
+#include "generated/animation/blend_state_base.hpp"
+#include <stdio.h>
+#include <vector>
+#include <algorithm>
+
+namespace rive
+{
+       class BlendAnimation;
+       class LayerStateImporter;
+
+       class BlendState : public BlendStateBase
+       {
+               friend class LayerStateImporter;
+
+       private:
+               std::vector<BlendAnimation*> m_Animations;
+               void addAnimation(BlendAnimation* animation);
+
+       public:
+               inline const std::vector<BlendAnimation*>& animations() const
+               {
+                       return m_Animations;
+               }
+
+#ifdef TESTING
+               size_t animationCount() { return m_Animations.size(); }
+               BlendAnimation* animation(size_t index) { return m_Animations[index]; }
+#endif
+       };
+} // namespace rive
+
+#endif
diff --git a/submodule/include/animation/blend_state_1d.hpp b/submodule/include/animation/blend_state_1d.hpp
new file mode 100644 (file)
index 0000000..eac98d6
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef _RIVE_BLEND_STATE1_D_HPP_
+#define _RIVE_BLEND_STATE1_D_HPP_
+#include "generated/animation/blend_state_1d_base.hpp"
+
+namespace rive
+{
+       class BlendState1D : public BlendState1DBase
+       {
+       public:
+               StatusCode import(ImportStack& importStack) override;
+
+               StateInstance* makeInstance() const override;
+       };
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/submodule/include/animation/blend_state_1d_instance.hpp b/submodule/include/animation/blend_state_1d_instance.hpp
new file mode 100644 (file)
index 0000000..df9d7a8
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef _RIVE_BLEND_STATE_1D_INSTANCE_HPP_
+#define _RIVE_BLEND_STATE_1D_INSTANCE_HPP_
+
+#include "animation/blend_state_instance.hpp"
+#include "animation/blend_state_1d.hpp"
+#include "animation/blend_animation_1d.hpp"
+
+namespace rive
+{
+       class BlendState1DInstance
+           : public BlendStateInstance<BlendState1D, BlendAnimation1D>
+       {
+       private:
+               BlendStateAnimationInstance<BlendAnimation1D>* m_From = nullptr;
+               BlendStateAnimationInstance<BlendAnimation1D>* m_To = nullptr;
+               int animationIndex(float value);
+
+       public:
+               BlendState1DInstance(const BlendState1D* blendState);
+               void advance(float seconds, SMIInput** inputs) override;
+       };
+} // namespace rive
+#endif
\ No newline at end of file
diff --git a/submodule/include/animation/blend_state_direct.hpp b/submodule/include/animation/blend_state_direct.hpp
new file mode 100644 (file)
index 0000000..9e64e5f
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef _RIVE_BLEND_STATE_DIRECT_HPP_
+#define _RIVE_BLEND_STATE_DIRECT_HPP_
+#include "generated/animation/blend_state_direct_base.hpp"
+#include <stdio.h>
+namespace rive
+{
+       class BlendStateDirect : public BlendStateDirectBase
+       {
+       public:
+               StateInstance* makeInstance() const override;
+       };
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/submodule/include/animation/blend_state_direct_instance.hpp b/submodule/include/animation/blend_state_direct_instance.hpp
new file mode 100644 (file)
index 0000000..289adeb
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef _RIVE_BLEND_STATE_DIRECT_INSTANCE_HPP_
+#define _RIVE_BLEND_STATE_DIRECT_INSTANCE_HPP_
+
+#include "animation/blend_state_instance.hpp"
+#include "animation/blend_state_direct.hpp"
+#include "animation/blend_animation_direct.hpp"
+
+namespace rive
+{
+       class BlendStateDirectInstance
+           : public BlendStateInstance<BlendStateDirect, BlendAnimationDirect>
+       {
+       public:
+               BlendStateDirectInstance(const BlendStateDirect* blendState);
+               void advance(float seconds, SMIInput** inputs) override;
+       };
+} // namespace rive
+#endif
\ No newline at end of file
diff --git a/submodule/include/animation/blend_state_instance.hpp b/submodule/include/animation/blend_state_instance.hpp
new file mode 100644 (file)
index 0000000..7ed231a
--- /dev/null
@@ -0,0 +1,95 @@
+#ifndef _RIVE_BLEND_STATE_INSTANCE_HPP_
+#define _RIVE_BLEND_STATE_INSTANCE_HPP_
+
+#include <string>
+#include <vector>
+#include "animation/state_instance.hpp"
+#include "animation/blend_state.hpp"
+#include "animation/linear_animation_instance.hpp"
+
+namespace rive
+{
+       class AnimationState;
+
+       template <class K, class T> class BlendStateInstance;
+       template <class T> class BlendStateAnimationInstance
+       {
+               template <class A, class B> friend class BlendStateInstance;
+
+       private:
+               const T* m_BlendAnimation;
+               LinearAnimationInstance m_AnimationInstance;
+               float m_Mix = 0.0f;
+
+       public:
+               const T* blendAnimation() const { return m_BlendAnimation; }
+               const LinearAnimationInstance* animationInstance() const
+               {
+                       return &m_AnimationInstance;
+               }
+
+               BlendStateAnimationInstance(const T* blendAnimation) :
+                   m_BlendAnimation(blendAnimation),
+                   m_AnimationInstance(blendAnimation->animation())
+               {
+               }
+
+               void mix(float value) { m_Mix = value; }
+       };
+
+       template <class K, class T> class BlendStateInstance : public StateInstance
+       {
+       protected:
+               std::vector<BlendStateAnimationInstance<T>> m_AnimationInstances;
+               bool m_KeepGoing = true;
+
+       public:
+               BlendStateInstance(const K* blendState) : StateInstance(blendState)
+               {
+                       for (auto blendAnimation : blendState->animations())
+                       {
+                               m_AnimationInstances.emplace_back(
+                                   BlendStateAnimationInstance<T>(
+                                       static_cast<T*>(blendAnimation)));
+                       }
+               }
+
+               bool keepGoing() const override { return m_KeepGoing; }
+
+               void advance(float seconds, SMIInput** inputs) override
+               {
+                       m_KeepGoing = false;
+                       for (auto& animation : m_AnimationInstances)
+                       {
+                               if (animation.m_AnimationInstance.advance(seconds))
+                               {
+                                       m_KeepGoing = true;
+                               }
+                       }
+               }
+
+               void apply(Artboard* artboard, float mix) override
+               {
+                       for (auto& animation : m_AnimationInstances)
+                       {
+                               float m = mix * animation.m_Mix;
+                               animation.m_AnimationInstance.apply(artboard, m);
+                       }
+               }
+
+               // Find the animationInstance that corresponds to the blendAnimation.
+               const LinearAnimationInstance*
+               animationInstance(const BlendAnimation* blendAnimation) const
+               {
+                       for (auto& animation : m_AnimationInstances)
+                       {
+                               if (animation.m_BlendAnimation == blendAnimation)
+                               {
+                                       return animation.animationInstance();
+                               }
+                       }
+                       return nullptr;
+               }
+       };
+} // namespace rive
+#endif
\ No newline at end of file
diff --git a/submodule/include/animation/blend_state_transition.hpp b/submodule/include/animation/blend_state_transition.hpp
new file mode 100644 (file)
index 0000000..08a49eb
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef _RIVE_BLEND_STATE_TRANSITION_HPP_
+#define _RIVE_BLEND_STATE_TRANSITION_HPP_
+#include "generated/animation/blend_state_transition_base.hpp"
+#include <stdio.h>
+namespace rive
+{
+       class BlendAnimation;
+       class LayerStateImporter;
+       class BlendStateTransition : public BlendStateTransitionBase
+       {
+               friend class LayerStateImporter;
+
+       private:
+               BlendAnimation* m_ExitBlendAnimation = nullptr;
+
+       public:
+               BlendAnimation* exitBlendAnimation() const
+               {
+                       return m_ExitBlendAnimation;
+               }
+
+               const LinearAnimationInstance*
+               exitTimeAnimationInstance(const StateInstance* from) const override;
+
+               const LinearAnimation*
+               exitTimeAnimation(const LayerState* from) const override;
+       };
+
+} // namespace rive
+
+#endif
\ No newline at end of file
index 595b737..5ecb856 100644 (file)
@@ -13,10 +13,6 @@ namespace rive
                float getT(float x) const;
 
        public:
-               StatusCode onAddedClean(CoreContext* context) override
-               {
-                       return StatusCode::Ok;
-               }
                StatusCode onAddedDirty(CoreContext* context) override;
 
                /// Convert a linear interpolation factor to an eased one.
index 1ab0c86..6c86edf 100644 (file)
@@ -21,18 +21,13 @@ namespace rive
                void computeSeconds(int fps);
 
                StatusCode onAddedDirty(CoreContext* context) override;
-               StatusCode onAddedClean(CoreContext* context) override
-               {
-                       return StatusCode::Ok;
-               }
-
                virtual void apply(Core* object, int propertyKey, float mix) = 0;
                virtual void applyInterpolation(Core* object,
                                                int propertyKey,
                                                float seconds,
                                                const KeyFrame* nextFrame,
                                                float mix) = 0;
-                                                                               
+
                StatusCode import(ImportStack& importStack) override;
        };
 } // namespace rive
index 0e0f13c..932147b 100644 (file)
@@ -9,6 +9,7 @@ namespace rive
        class StateTransition;
        class LayerStateImporter;
        class StateMachineLayerImporter;
+       class StateInstance;
 
        class LayerState : public LayerStateBase
        {
@@ -35,6 +36,10 @@ namespace rive
                        }
                        return nullptr;
                }
+
+               /// Make an instance of this state that can be advanced and applied by
+               /// the state machine when it is active or being transitioned from.
+               virtual StateInstance* makeInstance() const;
        };
 } // namespace rive
 
diff --git a/submodule/include/animation/state_instance.hpp b/submodule/include/animation/state_instance.hpp
new file mode 100644 (file)
index 0000000..d939c84
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef _RIVE_STATE_INSTANCE_HPP_
+#define _RIVE_STATE_INSTANCE_HPP_
+
+#include <string>
+#include <stddef.h>
+
+namespace rive
+{
+       class LayerState;
+       class SMIInput;
+       class Artboard;
+
+       /// Represents an instance of a state tracked by the State Machine.
+       class StateInstance
+       {
+       private:
+               const LayerState* m_LayerState;
+
+       public:
+               StateInstance(const LayerState* layerState);
+               virtual ~StateInstance();
+               virtual void advance(float seconds, SMIInput** inputs) = 0;
+               virtual void apply(Artboard* artboard, float mix) = 0;
+
+               /// Returns true when the State Machine needs to keep advancing this
+               /// state.
+               virtual bool keepGoing() const = 0;
+
+               const LayerState* state() const;
+       };
+} // namespace rive
+#endif
\ No newline at end of file
index 953d454..d81c5d0 100644 (file)
@@ -38,14 +38,14 @@ namespace rive
 
                // Advance the state machine by the specified time. Returns true if the
                // state machine will continue to animate after this advance.
-               bool advance(float seconds);
-
-               // Applies the animations of the StateMachine to an artboard.
-               void apply(Artboard* artboard) const;
+               bool advance(Artboard* artboard, float seconds);
 
                // Returns true when the StateMachineInstance has more data to process.
                bool needsAdvance() const;
 
+               // Returns a pointer to the instance's stateMachine
+               const StateMachine* stateMachine() const { return m_Machine; }
+
                size_t inputCount() const { return m_InputCount; }
                SMIInput* input(size_t index) const;
 
@@ -54,7 +54,8 @@ namespace rive
                SMITrigger* getTrigger(std::string name) const;
 
                const size_t currentAnimationCount() const;
-               const LinearAnimationInstance* currentAnimationByIndex(size_t index) const;
+               const LinearAnimationInstance*
+               currentAnimationByIndex(size_t index) const;
 
                // The number of state changes that occurred across all layers on the
                // previous advance.
index 140cab8..af082d7 100644 (file)
@@ -11,6 +11,18 @@ namespace rive
        class StateMachineLayerImporter;
        class StateTransitionImporter;
        class TransitionCondition;
+       class StateInstance;
+       class SMIInput;
+       class LinearAnimation;
+       class LinearAnimationInstance;
+
+       enum class AllowTransition : unsigned char
+       {
+               no,
+               waitingForExit,
+               yes
+       };
+
        class StateTransition : public StateTransitionBase
        {
                friend class StateMachineLayerImporter;
@@ -41,6 +53,12 @@ namespace rive
                               StateTransitionFlags::Disabled;
                }
 
+               /// Returns AllowTransition::yes when this transition can be taken from
+               /// stateFrom with the given inputs.
+               AllowTransition allowed(StateInstance* stateFrom,
+                                       SMIInput** inputs,
+                                       bool ignoreTriggers) const;
+
                /// Whether the animation is held at exit or if it keeps advancing
                /// during mixing.
                bool pauseOnExit() const
@@ -74,11 +92,27 @@ namespace rive
                /// AnimationState.
                float mixTime(const LayerState* stateFrom) const;
 
-               /// Exit time in seconds. Specify relativeToWorkArea to use the work
-               /// area start as the origin. Otherwise time 0 of the animation is the
-               /// origin.
+               /// Computes the exit time in seconds of the stateFrom. Set absolute to
+               /// true if you want the returned time to be relative to the entire
+               /// animation. Set absolute to false if you want it relative to the work
+               /// area.
                float exitTimeSeconds(const LayerState* stateFrom,
-                                     bool relativeToWorkArea) const;
+                                     bool absolute = false) const;
+
+               /// Provide the animation instance to use for computing percentage
+               /// durations for exit time.
+               virtual const LinearAnimationInstance*
+               exitTimeAnimationInstance(const StateInstance* from) const;
+
+               /// Provide the animation to use for computing percentage durations for
+               /// exit time.
+               virtual const LinearAnimation*
+               exitTimeAnimation(const LayerState* from) const;
+
+               /// Retruns true when we need to hold the exit time, also applies the
+               /// correct time to the animation instance in the stateFrom, when
+               /// applicable (when it's an AnimationState).
+               bool applyExitCondition(StateInstance* stateFrom) const;
        };
 } // namespace rive
 
diff --git a/submodule/include/animation/system_state_instance.hpp b/submodule/include/animation/system_state_instance.hpp
new file mode 100644 (file)
index 0000000..2e8b26e
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef _RIVE_SYSTEM_STATE_INSTANCE_HPP_
+#define _RIVE_SYSTEM_STATE_INSTANCE_HPP_
+
+#include <string>
+#include "animation/state_instance.hpp"
+
+namespace rive
+{
+       /// Represents an instance of a system state machine. Basically a
+       /// placeholder that may have meaning to the state machine itself, or is
+       /// just a no-op state (perhaps an unknown to this runtime state-type).
+       class SystemStateInstance : public StateInstance
+       {
+       public:
+               SystemStateInstance(const LayerState* layerState);
+
+               void advance(float seconds, SMIInput** inputs) override;
+               void apply(Artboard* artboard, float mix) override;
+
+               bool keepGoing() const override;
+       };
+} // namespace rive
+#endif
\ No newline at end of file
index 3c8201d..34075c4 100644 (file)
@@ -51,10 +51,6 @@ namespace rive
 
                Core* resolve(int id) const override;
 
-               StatusCode onAddedClean(CoreContext* context) override
-               {
-                       return StatusCode::Ok;
-               }
                void onComponentDirty(Component* component);
 
                /// Update components that depend on each other in DAG order.
index f8a537a..3f743ac 100644 (file)
@@ -5,9 +5,6 @@ namespace rive
 {
        class Backboard : public BackboardBase
        {
-       public:
-               StatusCode onAddedDirty(CoreContext* context) { return StatusCode::Ok; }
-               StatusCode onAddedClean(CoreContext* context) { return StatusCode::Ok; }
        };
 } // namespace rive
 
index a6937ca..8aef07d 100644 (file)
@@ -14,10 +14,6 @@ namespace rive
        public:
                Vec2D& translation() { return m_Translation; }
 
-               StatusCode onAddedClean(CoreContext* context) override
-               {
-                       return StatusCode::Ok;
-               }
                StatusCode onAddedDirty(CoreContext* context) override;
 
                static void deform(float x,
index 6ae1f5f..04a1c08 100644 (file)
@@ -19,14 +19,19 @@ namespace rive
                virtual bool deserialize(uint16_t propertyKey,
                                         BinaryReader& reader) = 0;
 
-               template <typename T> bool is() const { return isTypeOf(T::typeKey); }
-               template <typename T> T* as()
+               template <typename T> inline bool is() const
+               {
+                       return isTypeOf(T::typeKey);
+               }
+               template <typename T> inline T* as()
                {
                        assert(is<T>());
                        return reinterpret_cast<T*>(this);
                }
 
-               template <typename T> const T* as() const
+               virtual Core* clone() const { return nullptr; }
+
+               template <typename T> inline const T* as() const
                {
                        assert(is<T>());
                        return reinterpret_cast<const T*>(this);
@@ -37,13 +42,19 @@ namespace rive
                /// to look up objects referenced by id, but not assume that they in
                /// turn have resolved their references yet. Called during
                /// load/instance.
-               virtual StatusCode onAddedDirty(CoreContext* context) = 0;
+               virtual StatusCode onAddedDirty(CoreContext* context)
+               {
+                       return StatusCode::Ok;
+               }
 
                /// Called when all the objects in the context have had onAddedDirty
                /// called. This is an opportunity to reference things referenced by
                /// dependencies. (A path should be able to find a Shape somewhere in
                /// its hierarchy, which may be multiple levels up).
-               virtual StatusCode onAddedClean(CoreContext* context) = 0;
+               virtual StatusCode onAddedClean(CoreContext* context)
+               {
+                       return StatusCode::Ok;
+               }
 
                virtual StatusCode import(ImportStack& importStack)
                {
index 8faa68e..f5dd178 100644 (file)
@@ -44,6 +44,9 @@ namespace rive
                        nameChanged();
                }
 
+               Core* clone() const override;
+               void copy(const AnimationBase& object) { m_Name = object.m_Name; }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index f56bbd4..d7565fd 100644 (file)
@@ -45,6 +45,13 @@ namespace rive
                        animationIdChanged();
                }
 
+               Core* clone() const override;
+               void copy(const AnimationStateBase& object)
+               {
+                       m_AnimationId = object.m_AnimationId;
+                       LayerState::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 6fc6815..00df09c 100644 (file)
@@ -28,6 +28,8 @@ namespace rive
 
                uint16_t coreType() const override { return typeKey; }
 
+               Core* clone() const override;
+
        protected:
        };
 } // namespace rive
diff --git a/submodule/include/generated/animation/blend_animation_1d_base.hpp b/submodule/include/generated/animation/blend_animation_1d_base.hpp
new file mode 100644 (file)
index 0000000..a1e7b6c
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef _RIVE_BLEND_ANIMATION1_DBASE_HPP_
+#define _RIVE_BLEND_ANIMATION1_DBASE_HPP_
+#include "animation/blend_animation.hpp"
+#include "core/field_types/core_double_type.hpp"
+namespace rive
+{
+       class BlendAnimation1DBase : public BlendAnimation
+       {
+       protected:
+               typedef BlendAnimation Super;
+
+       public:
+               static const uint16_t typeKey = 75;
+
+               /// Helper to quickly determine if a core object extends another without
+               /// RTTI at runtime.
+               bool isTypeOf(uint16_t typeKey) const override
+               {
+                       switch (typeKey)
+                       {
+                               case BlendAnimation1DBase::typeKey:
+                               case BlendAnimationBase::typeKey:
+                                       return true;
+                               default:
+                                       return false;
+                       }
+               }
+
+               uint16_t coreType() const override { return typeKey; }
+
+               static const uint16_t valuePropertyKey = 166;
+
+       private:
+               float m_Value = 0.0f;
+       public:
+               inline float value() const { return m_Value; }
+               void value(float value)
+               {
+                       if (m_Value == value)
+                       {
+                               return;
+                       }
+                       m_Value = value;
+                       valueChanged();
+               }
+
+               bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
+               {
+                       switch (propertyKey)
+                       {
+                               case valuePropertyKey:
+                                       m_Value = CoreDoubleType::deserialize(reader);
+                                       return true;
+                       }
+                       return BlendAnimation::deserialize(propertyKey, reader);
+               }
+
+       protected:
+               virtual void valueChanged() {}
+       };
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/submodule/include/generated/animation/blend_animation_base.hpp b/submodule/include/generated/animation/blend_animation_base.hpp
new file mode 100644 (file)
index 0000000..e89521f
--- /dev/null
@@ -0,0 +1,62 @@
+#ifndef _RIVE_BLEND_ANIMATION_BASE_HPP_
+#define _RIVE_BLEND_ANIMATION_BASE_HPP_
+#include "core.hpp"
+#include "core/field_types/core_uint_type.hpp"
+namespace rive
+{
+       class BlendAnimationBase : public Core
+       {
+       protected:
+               typedef Core Super;
+
+       public:
+               static const uint16_t typeKey = 74;
+
+               /// Helper to quickly determine if a core object extends another without
+               /// RTTI at runtime.
+               bool isTypeOf(uint16_t typeKey) const override
+               {
+                       switch (typeKey)
+                       {
+                               case BlendAnimationBase::typeKey:
+                                       return true;
+                               default:
+                                       return false;
+                       }
+               }
+
+               uint16_t coreType() const override { return typeKey; }
+
+               static const uint16_t animationIdPropertyKey = 165;
+
+       private:
+               int m_AnimationId = -1;
+       public:
+               inline int animationId() const { return m_AnimationId; }
+               void animationId(int value)
+               {
+                       if (m_AnimationId == value)
+                       {
+                               return;
+                       }
+                       m_AnimationId = value;
+                       animationIdChanged();
+               }
+
+               bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
+               {
+                       switch (propertyKey)
+                       {
+                               case animationIdPropertyKey:
+                                       m_AnimationId = CoreUintType::deserialize(reader);
+                                       return true;
+                       }
+                       return false;
+               }
+
+       protected:
+               virtual void animationIdChanged() {}
+       };
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/submodule/include/generated/animation/blend_animation_direct_base.hpp b/submodule/include/generated/animation/blend_animation_direct_base.hpp
new file mode 100644 (file)
index 0000000..671db13
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef _RIVE_BLEND_ANIMATION_DIRECT_BASE_HPP_
+#define _RIVE_BLEND_ANIMATION_DIRECT_BASE_HPP_
+#include "animation/blend_animation.hpp"
+#include "core/field_types/core_uint_type.hpp"
+namespace rive
+{
+       class BlendAnimationDirectBase : public BlendAnimation
+       {
+       protected:
+               typedef BlendAnimation Super;
+
+       public:
+               static const uint16_t typeKey = 77;
+
+               /// Helper to quickly determine if a core object extends another without
+               /// RTTI at runtime.
+               bool isTypeOf(uint16_t typeKey) const override
+               {
+                       switch (typeKey)
+                       {
+                               case BlendAnimationDirectBase::typeKey:
+                               case BlendAnimationBase::typeKey:
+                                       return true;
+                               default:
+                                       return false;
+                       }
+               }
+
+               uint16_t coreType() const override { return typeKey; }
+
+               static const uint16_t inputIdPropertyKey = 168;
+
+       private:
+               int m_InputId = -1;
+       public:
+               inline int inputId() const { return m_InputId; }
+               void inputId(int value)
+               {
+                       if (m_InputId == value)
+                       {
+                               return;
+                       }
+                       m_InputId = value;
+                       inputIdChanged();
+               }
+
+               bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
+               {
+                       switch (propertyKey)
+                       {
+                               case inputIdPropertyKey:
+                                       m_InputId = CoreUintType::deserialize(reader);
+                                       return true;
+                       }
+                       return BlendAnimation::deserialize(propertyKey, reader);
+               }
+
+       protected:
+               virtual void inputIdChanged() {}
+       };
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/submodule/include/generated/animation/blend_state_1d_base.hpp b/submodule/include/generated/animation/blend_state_1d_base.hpp
new file mode 100644 (file)
index 0000000..49560ee
--- /dev/null
@@ -0,0 +1,65 @@
+#ifndef _RIVE_BLEND_STATE1_DBASE_HPP_
+#define _RIVE_BLEND_STATE1_DBASE_HPP_
+#include "animation/blend_state.hpp"
+#include "core/field_types/core_uint_type.hpp"
+namespace rive
+{
+       class BlendState1DBase : public BlendState
+       {
+       protected:
+               typedef BlendState Super;
+
+       public:
+               static const uint16_t typeKey = 76;
+
+               /// Helper to quickly determine if a core object extends another without
+               /// RTTI at runtime.
+               bool isTypeOf(uint16_t typeKey) const override
+               {
+                       switch (typeKey)
+                       {
+                               case BlendState1DBase::typeKey:
+                               case BlendStateBase::typeKey:
+                               case LayerStateBase::typeKey:
+                               case StateMachineLayerComponentBase::typeKey:
+                                       return true;
+                               default:
+                                       return false;
+                       }
+               }
+
+               uint16_t coreType() const override { return typeKey; }
+
+               static const uint16_t inputIdPropertyKey = 167;
+
+       private:
+               int m_InputId = -1;
+       public:
+               inline int inputId() const { return m_InputId; }
+               void inputId(int value)
+               {
+                       if (m_InputId == value)
+                       {
+                               return;
+                       }
+                       m_InputId = value;
+                       inputIdChanged();
+               }
+
+               bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
+               {
+                       switch (propertyKey)
+                       {
+                               case inputIdPropertyKey:
+                                       m_InputId = CoreUintType::deserialize(reader);
+                                       return true;
+                       }
+                       return BlendState::deserialize(propertyKey, reader);
+               }
+
+       protected:
+               virtual void inputIdChanged() {}
+       };
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/submodule/include/generated/animation/blend_state_base.hpp b/submodule/include/generated/animation/blend_state_base.hpp
new file mode 100644 (file)
index 0000000..7cdf3b9
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef _RIVE_BLEND_STATE_BASE_HPP_
+#define _RIVE_BLEND_STATE_BASE_HPP_
+#include "animation/layer_state.hpp"
+namespace rive
+{
+       class BlendStateBase : public LayerState
+       {
+       protected:
+               typedef LayerState Super;
+
+       public:
+               static const uint16_t typeKey = 72;
+
+               /// Helper to quickly determine if a core object extends another without
+               /// RTTI at runtime.
+               bool isTypeOf(uint16_t typeKey) const override
+               {
+                       switch (typeKey)
+                       {
+                               case BlendStateBase::typeKey:
+                               case LayerStateBase::typeKey:
+                               case StateMachineLayerComponentBase::typeKey:
+                                       return true;
+                               default:
+                                       return false;
+                       }
+               }
+
+               uint16_t coreType() const override { return typeKey; }
+
+       protected:
+       };
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/submodule/include/generated/animation/blend_state_direct_base.hpp b/submodule/include/generated/animation/blend_state_direct_base.hpp
new file mode 100644 (file)
index 0000000..cb4939b
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef _RIVE_BLEND_STATE_DIRECT_BASE_HPP_
+#define _RIVE_BLEND_STATE_DIRECT_BASE_HPP_
+#include "animation/blend_state.hpp"
+namespace rive
+{
+       class BlendStateDirectBase : public BlendState
+       {
+       protected:
+               typedef BlendState Super;
+
+       public:
+               static const uint16_t typeKey = 73;
+
+               /// Helper to quickly determine if a core object extends another without
+               /// RTTI at runtime.
+               bool isTypeOf(uint16_t typeKey) const override
+               {
+                       switch (typeKey)
+                       {
+                               case BlendStateDirectBase::typeKey:
+                               case BlendStateBase::typeKey:
+                               case LayerStateBase::typeKey:
+                               case StateMachineLayerComponentBase::typeKey:
+                                       return true;
+                               default:
+                                       return false;
+                       }
+               }
+
+               uint16_t coreType() const override { return typeKey; }
+
+       protected:
+       };
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/submodule/include/generated/animation/blend_state_transition_base.hpp b/submodule/include/generated/animation/blend_state_transition_base.hpp
new file mode 100644 (file)
index 0000000..9988763
--- /dev/null
@@ -0,0 +1,67 @@
+#ifndef _RIVE_BLEND_STATE_TRANSITION_BASE_HPP_
+#define _RIVE_BLEND_STATE_TRANSITION_BASE_HPP_
+#include "animation/state_transition.hpp"
+#include "core/field_types/core_uint_type.hpp"
+namespace rive
+{
+       class BlendStateTransitionBase : public StateTransition
+       {
+       protected:
+               typedef StateTransition Super;
+
+       public:
+               static const uint16_t typeKey = 78;
+
+               /// Helper to quickly determine if a core object extends another without
+               /// RTTI at runtime.
+               bool isTypeOf(uint16_t typeKey) const override
+               {
+                       switch (typeKey)
+                       {
+                               case BlendStateTransitionBase::typeKey:
+                               case StateTransitionBase::typeKey:
+                               case StateMachineLayerComponentBase::typeKey:
+                                       return true;
+                               default:
+                                       return false;
+                       }
+               }
+
+               uint16_t coreType() const override { return typeKey; }
+
+               static const uint16_t exitBlendAnimationIdPropertyKey = 171;
+
+       private:
+               int m_ExitBlendAnimationId = -1;
+       public:
+               inline int exitBlendAnimationId() const
+               {
+                       return m_ExitBlendAnimationId;
+               }
+               void exitBlendAnimationId(int value)
+               {
+                       if (m_ExitBlendAnimationId == value)
+                       {
+                               return;
+                       }
+                       m_ExitBlendAnimationId = value;
+                       exitBlendAnimationIdChanged();
+               }
+
+               bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
+               {
+                       switch (propertyKey)
+                       {
+                               case exitBlendAnimationIdPropertyKey:
+                                       m_ExitBlendAnimationId = CoreUintType::deserialize(reader);
+                                       return true;
+                       }
+                       return StateTransition::deserialize(propertyKey, reader);
+               }
+
+       protected:
+               virtual void exitBlendAnimationIdChanged() {}
+       };
+} // namespace rive
+
+#endif
\ No newline at end of file
index 1373d5f..cfe772f 100644 (file)
@@ -82,6 +82,15 @@ namespace rive
                        y2Changed();
                }
 
+               Core* clone() const override;
+               void copy(const CubicInterpolatorBase& object)
+               {
+                       m_X1 = object.m_X1;
+                       m_Y1 = object.m_Y1;
+                       m_X2 = object.m_X2;
+                       m_Y2 = object.m_Y2;
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index e8c5b3e..5a13d8c 100644 (file)
@@ -28,6 +28,8 @@ namespace rive
 
                uint16_t coreType() const override { return typeKey; }
 
+               Core* clone() const override;
+
        protected:
        };
 } // namespace rive
index 9b116c6..35d4b96 100644 (file)
@@ -28,6 +28,8 @@ namespace rive
 
                uint16_t coreType() const override { return typeKey; }
 
+               Core* clone() const override;
+
        protected:
        };
 } // namespace rive
index aa4f9c8..483422a 100644 (file)
@@ -43,6 +43,12 @@ namespace rive
                        objectIdChanged();
                }
 
+               Core* clone() const override;
+               void copy(const KeyedObjectBase& object)
+               {
+                       m_ObjectId = object.m_ObjectId;
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 0cf9517..f555a96 100644 (file)
@@ -43,6 +43,12 @@ namespace rive
                        propertyKeyChanged();
                }
 
+               Core* clone() const override;
+               void copy(const KeyedPropertyBase& object)
+               {
+                       m_PropertyKey = object.m_PropertyKey;
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 0481366..38e2206 100644 (file)
@@ -69,6 +69,13 @@ namespace rive
                        interpolatorIdChanged();
                }
 
+               void copy(const KeyFrameBase& object)
+               {
+                       m_Frame = object.m_Frame;
+                       m_InterpolationType = object.m_InterpolationType;
+                       m_InterpolatorId = object.m_InterpolatorId;
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 6d13842..56c5561 100644 (file)
@@ -44,6 +44,13 @@ namespace rive
                        valueChanged();
                }
 
+               Core* clone() const override;
+               void copy(const KeyFrameColorBase& object)
+               {
+                       m_Value = object.m_Value;
+                       KeyFrame::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 147b056..b6b0c2f 100644 (file)
@@ -44,6 +44,13 @@ namespace rive
                        valueChanged();
                }
 
+               Core* clone() const override;
+               void copy(const KeyFrameDoubleBase& object)
+               {
+                       m_Value = object.m_Value;
+                       KeyFrame::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 0b5b241..fb0f872 100644 (file)
@@ -44,6 +44,13 @@ namespace rive
                        valueChanged();
                }
 
+               Core* clone() const override;
+               void copy(const KeyFrameIdBase& object)
+               {
+                       m_Value = object.m_Value;
+                       KeyFrame::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 50e76f0..2cc606c 100644 (file)
@@ -124,6 +124,19 @@ namespace rive
                        enableWorkAreaChanged();
                }
 
+               Core* clone() const override;
+               void copy(const LinearAnimationBase& object)
+               {
+                       m_Fps = object.m_Fps;
+                       m_Duration = object.m_Duration;
+                       m_Speed = object.m_Speed;
+                       m_LoopValue = object.m_LoopValue;
+                       m_WorkStart = object.m_WorkStart;
+                       m_WorkEnd = object.m_WorkEnd;
+                       m_EnableWorkArea = object.m_EnableWorkArea;
+                       Animation::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 8385354..521ab32 100644 (file)
@@ -27,6 +27,8 @@ namespace rive
 
                uint16_t coreType() const override { return typeKey; }
 
+               Core* clone() const override;
+
        protected:
        };
 } // namespace rive
index cb85b95..2e1847a 100644 (file)
@@ -45,6 +45,13 @@ namespace rive
                        valueChanged();
                }
 
+               Core* clone() const override;
+               void copy(const StateMachineBoolBase& object)
+               {
+                       m_Value = object.m_Value;
+                       StateMachineInput::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index f8f8ae0..5c7606a 100644 (file)
@@ -44,6 +44,11 @@ namespace rive
                        nameChanged();
                }
 
+               void copy(const StateMachineComponentBase& object)
+               {
+                       m_Name = object.m_Name;
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 59daa16..d31605a 100644 (file)
@@ -27,6 +27,8 @@ namespace rive
 
                uint16_t coreType() const override { return typeKey; }
 
+               Core* clone() const override;
+
        protected:
        };
 } // namespace rive
index bb397be..b332be9 100644 (file)
@@ -26,6 +26,8 @@ namespace rive
 
                uint16_t coreType() const override { return typeKey; }
 
+               void copy(const StateMachineLayerComponentBase& object) {}
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 199e5c3..c4f2193 100644 (file)
@@ -45,6 +45,13 @@ namespace rive
                        valueChanged();
                }
 
+               Core* clone() const override;
+               void copy(const StateMachineNumberBase& object)
+               {
+                       m_Value = object.m_Value;
+                       StateMachineInput::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 226815d..4d2d27f 100644 (file)
@@ -28,6 +28,8 @@ namespace rive
 
                uint16_t coreType() const override { return typeKey; }
 
+               Core* clone() const override;
+
        protected:
        };
 } // namespace rive
index 09d7083..b7d519a 100644 (file)
@@ -83,6 +83,16 @@ namespace rive
                        exitTimeChanged();
                }
 
+               Core* clone() const override;
+               void copy(const StateTransitionBase& object)
+               {
+                       m_StateToId = object.m_StateToId;
+                       m_Flags = object.m_Flags;
+                       m_Duration = object.m_Duration;
+                       m_ExitTime = object.m_ExitTime;
+                       StateMachineLayerComponent::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 3cc0156..933bbf4 100644 (file)
@@ -28,6 +28,8 @@ namespace rive
 
                uint16_t coreType() const override { return typeKey; }
 
+               Core* clone() const override;
+
        protected:
        };
 } // namespace rive
index dd67d06..2b44ce7 100644 (file)
@@ -43,6 +43,11 @@ namespace rive
                        inputIdChanged();
                }
 
+               void copy(const TransitionConditionBase& object)
+               {
+                       m_InputId = object.m_InputId;
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index e2a7800..d7877d0 100644 (file)
@@ -45,6 +45,13 @@ namespace rive
                        valueChanged();
                }
 
+               Core* clone() const override;
+               void copy(const TransitionNumberConditionBase& object)
+               {
+                       m_Value = object.m_Value;
+                       TransitionValueCondition::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index bffc6a4..06bc7de 100644 (file)
@@ -27,6 +27,8 @@ namespace rive
 
                uint16_t coreType() const override { return typeKey; }
 
+               Core* clone() const override;
+
        protected:
        };
 } // namespace rive
index 229daaf..38caa6a 100644 (file)
@@ -44,6 +44,12 @@ namespace rive
                        opValueChanged();
                }
 
+               void copy(const TransitionValueConditionBase& object)
+               {
+                       m_OpValue = object.m_OpValue;
+                       TransitionCondition::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 4521f13..14b5b5e 100644 (file)
@@ -110,6 +110,18 @@ namespace rive
                        originYChanged();
                }
 
+               Core* clone() const override;
+               void copy(const ArtboardBase& object)
+               {
+                       m_Width = object.m_Width;
+                       m_Height = object.m_Height;
+                       m_X = object.m_X;
+                       m_Y = object.m_Y;
+                       m_OriginX = object.m_OriginX;
+                       m_OriginY = object.m_OriginY;
+                       ContainerComponent::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 3d95815..28d95d7 100644 (file)
@@ -26,6 +26,9 @@ namespace rive
 
                uint16_t coreType() const override { return typeKey; }
 
+               Core* clone() const override;
+               void copy(const BackboardBase& object) {}
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 9d92a4d..8ebf1c9 100644 (file)
@@ -47,6 +47,13 @@ namespace rive
                        lengthChanged();
                }
 
+               Core* clone() const override;
+               void copy(const BoneBase& object)
+               {
+                       m_Length = object.m_Length;
+                       SkeletalComponent::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 3669429..fbc0954 100644 (file)
@@ -84,6 +84,16 @@ namespace rive
                        outIndicesChanged();
                }
 
+               Core* clone() const override;
+               void copy(const CubicWeightBase& object)
+               {
+                       m_InValues = object.m_InValues;
+                       m_InIndices = object.m_InIndices;
+                       m_OutValues = object.m_OutValues;
+                       m_OutIndices = object.m_OutIndices;
+                       Weight::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index bb0dba1..464d165 100644 (file)
@@ -61,6 +61,14 @@ namespace rive
                        yChanged();
                }
 
+               Core* clone() const override;
+               void copy(const RootBoneBase& object)
+               {
+                       m_X = object.m_X;
+                       m_Y = object.m_Y;
+                       Bone::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index d802325..7a5aa99 100644 (file)
@@ -110,6 +110,18 @@ namespace rive
                        tyChanged();
                }
 
+               Core* clone() const override;
+               void copy(const SkinBase& object)
+               {
+                       m_Xx = object.m_Xx;
+                       m_Yx = object.m_Yx;
+                       m_Xy = object.m_Xy;
+                       m_Yy = object.m_Yy;
+                       m_Tx = object.m_Tx;
+                       m_Ty = object.m_Ty;
+                       ContainerComponent::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 415e706..164b720 100644 (file)
@@ -123,6 +123,19 @@ namespace rive
                        tyChanged();
                }
 
+               Core* clone() const override;
+               void copy(const TendonBase& object)
+               {
+                       m_BoneId = object.m_BoneId;
+                       m_Xx = object.m_Xx;
+                       m_Yx = object.m_Yx;
+                       m_Xy = object.m_Xy;
+                       m_Yy = object.m_Yy;
+                       m_Tx = object.m_Tx;
+                       m_Ty = object.m_Ty;
+                       Component::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 108ccb7..d3b9ef6 100644 (file)
@@ -57,6 +57,14 @@ namespace rive
                        indicesChanged();
                }
 
+               Core* clone() const override;
+               void copy(const WeightBase& object)
+               {
+                       m_Values = object.m_Values;
+                       m_Indices = object.m_Indices;
+                       Component::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 49681b4..14cc189 100644 (file)
@@ -58,6 +58,12 @@ namespace rive
                        parentIdChanged();
                }
 
+               void copy(const ComponentBase& object)
+               {
+                       m_Name = object.m_Name;
+                       m_ParentId = object.m_ParentId;
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index a872112..24a84cf 100644 (file)
@@ -3,6 +3,13 @@
 #include "animation/animation.hpp"
 #include "animation/animation_state.hpp"
 #include "animation/any_state.hpp"
+#include "animation/blend_animation.hpp"
+#include "animation/blend_animation_1d.hpp"
+#include "animation/blend_animation_direct.hpp"
+#include "animation/blend_state.hpp"
+#include "animation/blend_state_1d.hpp"
+#include "animation/blend_state_direct.hpp"
+#include "animation/blend_state_transition.hpp"
 #include "animation/cubic_interpolator.hpp"
 #include "animation/entry_state.hpp"
 #include "animation/exit_state.hpp"
@@ -83,6 +90,8 @@ namespace rive
                                        return new AnimationState();
                                case KeyedObjectBase::typeKey:
                                        return new KeyedObject();
+                               case BlendAnimationDirectBase::typeKey:
+                                       return new BlendAnimationDirect();
                                case StateMachineNumberBase::typeKey:
                                        return new StateMachineNumber();
                                case TransitionTriggerConditionBase::typeKey:
@@ -115,12 +124,20 @@ namespace rive
                                        return new LinearAnimation();
                                case StateMachineTriggerBase::typeKey:
                                        return new StateMachineTrigger();
+                               case BlendStateDirectBase::typeKey:
+                                       return new BlendStateDirect();
                                case ExitStateBase::typeKey:
                                        return new ExitState();
+                               case BlendState1DBase::typeKey:
+                                       return new BlendState1D();
                                case TransitionBoolConditionBase::typeKey:
                                        return new TransitionBoolCondition();
+                               case BlendStateTransitionBase::typeKey:
+                                       return new BlendStateTransition();
                                case StateMachineBoolBase::typeKey:
                                        return new StateMachineBool();
+                               case BlendAnimation1DBase::typeKey:
+                                       return new BlendAnimation1D();
                                case LinearGradientBase::typeKey:
                                        return new LinearGradient();
                                case RadialGradientBase::typeKey:
@@ -216,6 +233,12 @@ namespace rive
                                case KeyedObjectBase::objectIdPropertyKey:
                                        object->as<KeyedObjectBase>()->objectId(value);
                                        break;
+                               case BlendAnimationBase::animationIdPropertyKey:
+                                       object->as<BlendAnimationBase>()->animationId(value);
+                                       break;
+                               case BlendAnimationDirectBase::inputIdPropertyKey:
+                                       object->as<BlendAnimationDirectBase>()->inputId(value);
+                                       break;
                                case TransitionConditionBase::inputIdPropertyKey:
                                        object->as<TransitionConditionBase>()->inputId(value);
                                        break;
@@ -264,6 +287,13 @@ namespace rive
                                case LinearAnimationBase::workEndPropertyKey:
                                        object->as<LinearAnimationBase>()->workEnd(value);
                                        break;
+                               case BlendState1DBase::inputIdPropertyKey:
+                                       object->as<BlendState1DBase>()->inputId(value);
+                                       break;
+                               case BlendStateTransitionBase::exitBlendAnimationIdPropertyKey:
+                                       object->as<BlendStateTransitionBase>()
+                                           ->exitBlendAnimationId(value);
+                                       break;
                                case StrokeBase::capPropertyKey:
                                        object->as<StrokeBase>()->cap(value);
                                        break;
@@ -348,6 +378,9 @@ namespace rive
                                case LinearAnimationBase::speedPropertyKey:
                                        object->as<LinearAnimationBase>()->speed(value);
                                        break;
+                               case BlendAnimation1DBase::valuePropertyKey:
+                                       object->as<BlendAnimation1DBase>()->value(value);
+                                       break;
                                case LinearGradientBase::startXPropertyKey:
                                        object->as<LinearGradientBase>()->startX(value);
                                        break;
@@ -596,6 +629,10 @@ namespace rive
                                        return object->as<AnimationStateBase>()->animationId();
                                case KeyedObjectBase::objectIdPropertyKey:
                                        return object->as<KeyedObjectBase>()->objectId();
+                               case BlendAnimationBase::animationIdPropertyKey:
+                                       return object->as<BlendAnimationBase>()->animationId();
+                               case BlendAnimationDirectBase::inputIdPropertyKey:
+                                       return object->as<BlendAnimationDirectBase>()->inputId();
                                case TransitionConditionBase::inputIdPropertyKey:
                                        return object->as<TransitionConditionBase>()->inputId();
                                case KeyedPropertyBase::propertyKeyPropertyKey:
@@ -629,6 +666,11 @@ namespace rive
                                        return object->as<LinearAnimationBase>()->workStart();
                                case LinearAnimationBase::workEndPropertyKey:
                                        return object->as<LinearAnimationBase>()->workEnd();
+                               case BlendState1DBase::inputIdPropertyKey:
+                                       return object->as<BlendState1DBase>()->inputId();
+                               case BlendStateTransitionBase::exitBlendAnimationIdPropertyKey:
+                                       return object->as<BlendStateTransitionBase>()
+                                           ->exitBlendAnimationId();
                                case StrokeBase::capPropertyKey:
                                        return object->as<StrokeBase>()->cap();
                                case StrokeBase::joinPropertyKey:
@@ -688,6 +730,8 @@ namespace rive
                                        return object->as<KeyFrameDoubleBase>()->value();
                                case LinearAnimationBase::speedPropertyKey:
                                        return object->as<LinearAnimationBase>()->speed();
+                               case BlendAnimation1DBase::valuePropertyKey:
+                                       return object->as<BlendAnimation1DBase>()->value();
                                case LinearGradientBase::startXPropertyKey:
                                        return object->as<LinearGradientBase>()->startX();
                                case LinearGradientBase::startYPropertyKey:
@@ -858,6 +902,8 @@ namespace rive
                                case DrawTargetBase::placementValuePropertyKey:
                                case AnimationStateBase::animationIdPropertyKey:
                                case KeyedObjectBase::objectIdPropertyKey:
+                               case BlendAnimationBase::animationIdPropertyKey:
+                               case BlendAnimationDirectBase::inputIdPropertyKey:
                                case TransitionConditionBase::inputIdPropertyKey:
                                case KeyedPropertyBase::propertyKeyPropertyKey:
                                case KeyFrameBase::framePropertyKey:
@@ -874,6 +920,8 @@ namespace rive
                                case LinearAnimationBase::loopValuePropertyKey:
                                case LinearAnimationBase::workStartPropertyKey:
                                case LinearAnimationBase::workEndPropertyKey:
+                               case BlendState1DBase::inputIdPropertyKey:
+                               case BlendStateTransitionBase::exitBlendAnimationIdPropertyKey:
                                case StrokeBase::capPropertyKey:
                                case StrokeBase::joinPropertyKey:
                                case TrimPathBase::modeValuePropertyKey:
@@ -901,6 +949,7 @@ namespace rive
                                case CubicInterpolatorBase::y2PropertyKey:
                                case KeyFrameDoubleBase::valuePropertyKey:
                                case LinearAnimationBase::speedPropertyKey:
+                               case BlendAnimation1DBase::valuePropertyKey:
                                case LinearGradientBase::startXPropertyKey:
                                case LinearGradientBase::startYPropertyKey:
                                case LinearGradientBase::endXPropertyKey:
index 90f1f64..03cc549 100644 (file)
@@ -45,6 +45,13 @@ namespace rive
                        drawTargetIdChanged();
                }
 
+               Core* clone() const override;
+               void copy(const DrawRulesBase& object)
+               {
+                       m_DrawTargetId = object.m_DrawTargetId;
+                       ContainerComponent::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 3d54329..433ba2b 100644 (file)
@@ -57,6 +57,14 @@ namespace rive
                        placementValueChanged();
                }
 
+               Core* clone() const override;
+               void copy(const DrawTargetBase& object)
+               {
+                       m_DrawableId = object.m_DrawableId;
+                       m_PlacementValue = object.m_PlacementValue;
+                       Component::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 5845776..0d2d3c8 100644 (file)
@@ -60,6 +60,13 @@ namespace rive
                        drawableFlagsChanged();
                }
 
+               void copy(const DrawableBase& object)
+               {
+                       m_BlendModeValue = object.m_BlendModeValue;
+                       m_DrawableFlags = object.m_DrawableFlags;
+                       Node::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 39c52c8..c35e3ce 100644 (file)
@@ -59,6 +59,14 @@ namespace rive
                        yChanged();
                }
 
+               Core* clone() const override;
+               void copy(const NodeBase& object)
+               {
+                       m_X = object.m_X;
+                       m_Y = object.m_Y;
+                       TransformComponent::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index dc5cd7d..b20053d 100644 (file)
@@ -71,6 +71,15 @@ namespace rive
                        isVisibleChanged();
                }
 
+               Core* clone() const override;
+               void copy(const ClippingShapeBase& object)
+               {
+                       m_SourceId = object.m_SourceId;
+                       m_FillRule = object.m_FillRule;
+                       m_IsVisible = object.m_IsVisible;
+                       Component::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index b28cc6a..5321542 100644 (file)
@@ -73,6 +73,15 @@ namespace rive
                        outDistanceChanged();
                }
 
+               Core* clone() const override;
+               void copy(const CubicAsymmetricVertexBase& object)
+               {
+                       m_Rotation = object.m_Rotation;
+                       m_InDistance = object.m_InDistance;
+                       m_OutDistance = object.m_OutDistance;
+                       CubicVertex::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 4b59696..9a22ce4 100644 (file)
@@ -86,6 +86,16 @@ namespace rive
                        outDistanceChanged();
                }
 
+               Core* clone() const override;
+               void copy(const CubicDetachedVertexBase& object)
+               {
+                       m_InRotation = object.m_InRotation;
+                       m_InDistance = object.m_InDistance;
+                       m_OutRotation = object.m_OutRotation;
+                       m_OutDistance = object.m_OutDistance;
+                       CubicVertex::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index aeb3fe5..6fbd49e 100644 (file)
@@ -60,6 +60,14 @@ namespace rive
                        distanceChanged();
                }
 
+               Core* clone() const override;
+               void copy(const CubicMirroredVertexBase& object)
+               {
+                       m_Rotation = object.m_Rotation;
+                       m_Distance = object.m_Distance;
+                       CubicVertex::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 484277b..3c17515 100644 (file)
@@ -32,6 +32,8 @@ namespace rive
 
                uint16_t coreType() const override { return typeKey; }
 
+               Core* clone() const override;
+
        protected:
        };
 } // namespace rive
index 9d5138c..06fbebf 100644 (file)
@@ -46,6 +46,13 @@ namespace rive
                        fillRuleChanged();
                }
 
+               Core* clone() const override;
+               void copy(const FillBase& object)
+               {
+                       m_FillRule = object.m_FillRule;
+                       ShapePaint::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 41222dd..97757ae 100644 (file)
@@ -58,6 +58,14 @@ namespace rive
                        positionChanged();
                }
 
+               Core* clone() const override;
+               void copy(const GradientStopBase& object)
+               {
+                       m_ColorValue = object.m_ColorValue;
+                       m_Position = object.m_Position;
+                       Component::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 197c4db..4182afb 100644 (file)
@@ -97,6 +97,17 @@ namespace rive
                        opacityChanged();
                }
 
+               Core* clone() const override;
+               void copy(const LinearGradientBase& object)
+               {
+                       m_StartX = object.m_StartX;
+                       m_StartY = object.m_StartY;
+                       m_EndX = object.m_EndX;
+                       m_EndY = object.m_EndY;
+                       m_Opacity = object.m_Opacity;
+                       ContainerComponent::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index c0b8233..ae53822 100644 (file)
@@ -29,6 +29,8 @@ namespace rive
 
                uint16_t coreType() const override { return typeKey; }
 
+               Core* clone() const override;
+
        protected:
        };
 } // namespace rive
index 5179f11..ca4e5fa 100644 (file)
@@ -45,6 +45,12 @@ namespace rive
                        isVisibleChanged();
                }
 
+               void copy(const ShapePaintBase& object)
+               {
+                       m_IsVisible = object.m_IsVisible;
+                       ContainerComponent::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index c05c918..f233265 100644 (file)
@@ -44,6 +44,13 @@ namespace rive
                        colorValueChanged();
                }
 
+               Core* clone() const override;
+               void copy(const SolidColorBase& object)
+               {
+                       m_ColorValue = object.m_ColorValue;
+                       Component::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 236e96d..8615ffa 100644 (file)
@@ -90,6 +90,16 @@ namespace rive
                        transformAffectsStrokeChanged();
                }
 
+               Core* clone() const override;
+               void copy(const StrokeBase& object)
+               {
+                       m_Thickness = object.m_Thickness;
+                       m_Cap = object.m_Cap;
+                       m_Join = object.m_Join;
+                       m_TransformAffectsStroke = object.m_TransformAffectsStroke;
+                       ShapePaint::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 6274e8d..3c52eeb 100644 (file)
@@ -84,6 +84,16 @@ namespace rive
                        modeValueChanged();
                }
 
+               Core* clone() const override;
+               void copy(const TrimPathBase& object)
+               {
+                       m_Start = object.m_Start;
+                       m_End = object.m_End;
+                       m_Offset = object.m_Offset;
+                       m_ModeValue = object.m_ModeValue;
+                       Component::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 7f955ce..dfb8a93 100644 (file)
@@ -87,6 +87,15 @@ namespace rive
                        originYChanged();
                }
 
+               void copy(const ParametricPathBase& object)
+               {
+                       m_Width = object.m_Width;
+                       m_Height = object.m_Height;
+                       m_OriginX = object.m_OriginX;
+                       m_OriginY = object.m_OriginY;
+                       Path::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index b881932..49475b1 100644 (file)
@@ -47,6 +47,12 @@ namespace rive
                        pathFlagsChanged();
                }
 
+               void copy(const PathBase& object)
+               {
+                       m_PathFlags = object.m_PathFlags;
+                       Node::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 80b929b..3502557 100644 (file)
@@ -58,6 +58,13 @@ namespace rive
                        yChanged();
                }
 
+               void copy(const PathVertexBase& object)
+               {
+                       m_X = object.m_X;
+                       m_Y = object.m_Y;
+                       ContainerComponent::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 2359c89..f852e11 100644 (file)
@@ -48,6 +48,13 @@ namespace rive
                        isClosedChanged();
                }
 
+               Core* clone() const override;
+               void copy(const PointsPathBase& object)
+               {
+                       m_IsClosed = object.m_IsClosed;
+                       Path::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 9adb1aa..a0795f8 100644 (file)
@@ -63,6 +63,14 @@ namespace rive
                        cornerRadiusChanged();
                }
 
+               Core* clone() const override;
+               void copy(const PolygonBase& object)
+               {
+                       m_Points = object.m_Points;
+                       m_CornerRadius = object.m_CornerRadius;
+                       ParametricPath::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 59591c7..3c69947 100644 (file)
@@ -102,6 +102,17 @@ namespace rive
                        cornerRadiusBRChanged();
                }
 
+               Core* clone() const override;
+               void copy(const RectangleBase& object)
+               {
+                       m_LinkCornerRadius = object.m_LinkCornerRadius;
+                       m_CornerRadiusTL = object.m_CornerRadiusTL;
+                       m_CornerRadiusTR = object.m_CornerRadiusTR;
+                       m_CornerRadiusBL = object.m_CornerRadiusBL;
+                       m_CornerRadiusBR = object.m_CornerRadiusBR;
+                       ParametricPath::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index f5d6d28..f77705d 100644 (file)
@@ -31,6 +31,8 @@ namespace rive
 
                uint16_t coreType() const override { return typeKey; }
 
+               Core* clone() const override;
+
        protected:
        };
 } // namespace rive
index 1051458..b04e871 100644 (file)
@@ -50,6 +50,13 @@ namespace rive
                        innerRadiusChanged();
                }
 
+               Core* clone() const override;
+               void copy(const StarBase& object)
+               {
+                       m_InnerRadius = object.m_InnerRadius;
+                       Polygon::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index 242accc..7db29d7 100644 (file)
@@ -46,6 +46,13 @@ namespace rive
                        radiusChanged();
                }
 
+               Core* clone() const override;
+               void copy(const StraightVertexBase& object)
+               {
+                       m_Radius = object.m_Radius;
+                       PathVertex::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index a2075d1..4102b1b 100644 (file)
@@ -32,6 +32,8 @@ namespace rive
 
                uint16_t coreType() const override { return typeKey; }
 
+               Core* clone() const override;
+
        protected:
        };
 } // namespace rive
index d84c421..9f19f69 100644 (file)
@@ -84,6 +84,15 @@ namespace rive
                        opacityChanged();
                }
 
+               void copy(const TransformComponentBase& object)
+               {
+                       m_Rotation = object.m_Rotation;
+                       m_ScaleX = object.m_ScaleX;
+                       m_ScaleY = object.m_ScaleY;
+                       m_Opacity = object.m_Opacity;
+                       ContainerComponent::copy(object);
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
index e88dfd3..6212e82 100644 (file)
@@ -7,6 +7,7 @@ namespace rive
 {
        class LayerState;
        class StateTransition;
+       class BlendAnimation;
 
        class LayerStateImporter : public ImportStackObject
        {
@@ -16,6 +17,7 @@ namespace rive
        public:
                LayerStateImporter(LayerState* state);
                void addTransition(StateTransition* transition);
+               bool addBlendAnimation(BlendAnimation* animation);
                StatusCode resolve() override;
        };
 } // namespace rive
index be3990d..c1d0130 100644 (file)
@@ -7,10 +7,6 @@ namespace rive
        {
        public:
                StatusCode onAddedDirty(CoreContext* context) override;
-               StatusCode onAddedClean(CoreContext* context) override
-               {
-                       return StatusCode::Ok;
-               }
 
        protected:
                void colorValueChanged() override;
index 645e829..c85a891 100644 (file)
@@ -17,10 +17,6 @@ namespace rive
 
        public:
                StatusCode onAddedDirty(CoreContext* context) override;
-               StatusCode onAddedClean(CoreContext* context) override
-               {
-                       return StatusCode::Ok;
-               }
                void addStop(GradientStop* stop);
                void update(ComponentDirt value) override;
                void markGradientDirty();
index 8f019a6..a121344 100644 (file)
@@ -6,10 +6,6 @@ namespace rive
        class RadialGradient : public RadialGradientBase
        {
        public:
-               StatusCode onAddedClean(CoreContext* context) override
-               {
-                       return StatusCode::Ok;
-               }
                void makeGradient(const Vec2D& start, const Vec2D& end) override;
        };
 } // namespace rive
index 0eb31b1..3c489e8 100644 (file)
@@ -8,10 +8,6 @@ namespace rive
        {
        public:
                StatusCode onAddedDirty(CoreContext* context) override;
-               StatusCode onAddedClean(CoreContext* context) override
-               {
-                       return StatusCode::Ok;
-               }
 
        protected:
                void renderOpacityChanged() override;
index 367c1c7..a4b3567 100644 (file)
@@ -10,6 +10,28 @@ namespace rive
        class CommandPath;
        class PathVertex;
 
+#ifdef ENABLE_QUERY_FLAT_VERTICES
+       /// Optionally compiled in for tools that need to compute per frame world
+       /// transformed path vertices. These should not be used at runtime as it's
+       /// not optimized for performance (it does a lot of memory allocation).
+
+       /// A flattened path is composed of only linear
+       /// and cubic vertices. No corner vertices and it's entirely in world space.
+       /// This is helpful for getting a close to identical representation of the
+       /// vertices used to issue the high level path draw commands.
+       class FlattenedPath
+       {
+       private:
+               std::vector<PathVertex*> m_Vertices;
+
+       public:
+               ~FlattenedPath();
+
+               const std::vector<PathVertex*>& vertices() const { return m_Vertices; }
+               void addVertex(PathVertex* vertex, const Mat2D& transform);
+       };
+#endif
+
        class Path : public PathBase
        {
        protected:
@@ -32,6 +54,10 @@ namespace rive
                virtual void markPathDirty();
                virtual bool isPathClosed() const { return true; }
                void onDirty(ComponentDirt dirt) override;
+#ifdef ENABLE_QUERY_FLAT_VERTICES
+               FlattenedPath* makeFlat(bool transformToParent);
+#endif
+
 #ifdef TESTING
                std::vector<PathVertex*>& vertices() { return m_Vertices; }
 #endif
index 03ae5aa..385ba41 100644 (file)
@@ -17,10 +17,6 @@ namespace rive
                PathComposer(Shape* shape);
                ~PathComposer();
                Shape* shape() const { return m_Shape; }
-               StatusCode onAddedClean(CoreContext* context) override
-               {
-                       return StatusCode::Ok;
-               }
                void buildDependencies() override;
                void update(ComponentDirt value) override;
 
index 157952a..a1b5d97 100644 (file)
@@ -15,10 +15,6 @@ namespace rive
 
        public:
                StatusCode onAddedDirty(CoreContext* context) override;
-               StatusCode onAddedClean(CoreContext* context) override
-               {
-                       return StatusCode::Ok;
-               }
                template <typename T> T* weight() { return m_Weight->as<T>(); }
                virtual void deform(Mat2D& worldTransform, float* boneTransforms);
                bool hasWeight() { return m_Weight != nullptr; }
index a840a0d..9160c28 100644 (file)
@@ -5,7 +5,7 @@ inline int valueOrDefault(int value, int default_value)
        return value <= 0 ? default_value : value;
 }
 
-void scale(int* value, int targetValue, int* otherValue)
+inline void scale(int* value, int targetValue, int* otherValue)
 {
        if (*value != targetValue)
        {
@@ -40,7 +40,7 @@ RiveFrameExtractor::RiveFrameExtractor(const char* path,
            _width, _height, kRGBA_8888_SkColorType, kPremul_SkAlphaType));
        rasterCanvas = rasterSurface->getCanvas();
        _fps = valueOrDefault(fps, animation->fps());
-       ifps = 1.0 / fps;
+       ifps = 1.0 / _fps;
 };
 
 RiveFrameExtractor::~RiveFrameExtractor()
index e41ec5d..25f61a0 100644 (file)
@@ -253,8 +253,7 @@ int main()
                        }
                        else if (stateMachineInstance != nullptr)
                        {
-                               stateMachineInstance->advance(elapsed);
-                               stateMachineInstance->apply(artboard);
+                               stateMachineInstance->advance(artboard, elapsed);
                        }
                        artboard->advance(elapsed);
 
index 997e93d..ccb6811 100644 (file)
@@ -1,6 +1,18 @@
 #include "animation/animation_state.hpp"
 #include "animation/linear_animation.hpp"
+#include "animation/animation_state_instance.hpp"
+#include "animation/system_state_instance.hpp"
 #include "core_context.hpp"
 #include "artboard.hpp"
 
 using namespace rive;
+
+StateInstance* AnimationState::makeInstance() const
+{
+       if (animation() == nullptr)
+       {
+               // Failed to load at runtime/some new type we don't understand.
+               return new SystemStateInstance(this);
+       }
+       return new AnimationStateInstance(this);
+}
\ No newline at end of file
diff --git a/submodule/src/animation/animation_state_instance.cpp b/submodule/src/animation/animation_state_instance.cpp
new file mode 100644 (file)
index 0000000..b349b63
--- /dev/null
@@ -0,0 +1,23 @@
+#include "animation/animation_state_instance.hpp"
+#include "animation/animation_state.hpp"
+
+using namespace rive;
+
+AnimationStateInstance::AnimationStateInstance(const AnimationState* state) :
+    StateInstance(state),
+    m_AnimationInstance(state->animation()),
+    m_KeepGoing(true)
+{
+}
+
+void AnimationStateInstance::advance(float seconds, SMIInput** inputs)
+{
+       m_KeepGoing = m_AnimationInstance.advance(seconds);
+}
+
+void AnimationStateInstance::apply(Artboard* artboard, float mix)
+{
+       m_AnimationInstance.apply(artboard, mix);
+}
+
+bool AnimationStateInstance::keepGoing() const { return m_KeepGoing; }
\ No newline at end of file
diff --git a/submodule/src/animation/blend_animation.cpp b/submodule/src/animation/blend_animation.cpp
new file mode 100644 (file)
index 0000000..43c4dc7
--- /dev/null
@@ -0,0 +1,37 @@
+#include "artboard.hpp"
+#include "animation/blend_animation.hpp"
+#include "animation/layer_state.hpp"
+#include "importers/layer_state_importer.hpp"
+#include "importers/artboard_importer.hpp"
+
+using namespace rive;
+
+StatusCode BlendAnimation::import(ImportStack& importStack)
+{
+       auto importer =
+           importStack.latest<LayerStateImporter>(LayerStateBase::typeKey);
+       if (importer == nullptr)
+       {
+               return StatusCode::MissingObject;
+       }
+       else if (!importer->addBlendAnimation(this))
+       {
+               return StatusCode::InvalidObject;
+       }
+
+       auto artboardImporter =
+           importStack.latest<ArtboardImporter>(ArtboardBase::typeKey);
+       if (artboardImporter == nullptr)
+       {
+               return StatusCode::MissingObject;
+       }
+
+       auto artboard = artboardImporter->artboard();
+       auto animationCount = artboard->animationCount();
+       if (animationId() >= 0 && animationId() < animationCount)
+       {
+               m_Animation = artboardImporter->artboard()->animation(animationId());
+       }
+
+       return Super::import(importStack);
+}
\ No newline at end of file
diff --git a/submodule/src/animation/blend_animation_1d.cpp b/submodule/src/animation/blend_animation_1d.cpp
new file mode 100644 (file)
index 0000000..607f142
--- /dev/null
@@ -0,0 +1,13 @@
+#include "animation/blend_animation_1d.hpp"
+
+using namespace rive;
+
+StatusCode BlendAnimation1D::onAddedDirty(CoreContext* context)
+{
+       return StatusCode::Ok;
+}
+
+StatusCode BlendAnimation1D::onAddedClean(CoreContext* context)
+{
+       return StatusCode::Ok;
+}
\ No newline at end of file
diff --git a/submodule/src/animation/blend_animation_direct.cpp b/submodule/src/animation/blend_animation_direct.cpp
new file mode 100644 (file)
index 0000000..5013f1e
--- /dev/null
@@ -0,0 +1,39 @@
+#include "animation/blend_animation_direct.hpp"
+#include "animation/state_machine.hpp"
+#include "animation/state_machine_number.hpp"
+#include "importers/state_machine_importer.hpp"
+
+using namespace rive;
+
+StatusCode BlendAnimationDirect::onAddedDirty(CoreContext* context)
+{
+       return StatusCode::Ok;
+}
+
+StatusCode BlendAnimationDirect::onAddedClean(CoreContext* context)
+{
+       return StatusCode::Ok;
+}
+
+StatusCode BlendAnimationDirect::import(ImportStack& importStack)
+{
+       auto stateMachineImporter =
+           importStack.latest<StateMachineImporter>(StateMachine::typeKey);
+       if (stateMachineImporter == nullptr)
+       {
+               return StatusCode::MissingObject;
+       }
+
+       // Make sure the inputId doesn't overflow the input buffer.
+       if (inputId() < 0 ||
+           inputId() >= stateMachineImporter->stateMachine()->inputCount())
+       {
+               return StatusCode::InvalidObject;
+       }
+       auto input = stateMachineImporter->stateMachine()->input((size_t)inputId());
+       if (input == nullptr || !input->is<StateMachineNumber>())
+       {
+               return StatusCode::InvalidObject;
+       }
+       return Super::import(importStack);
+}
\ No newline at end of file
diff --git a/submodule/src/animation/blend_state.cpp b/submodule/src/animation/blend_state.cpp
new file mode 100644 (file)
index 0000000..4c94967
--- /dev/null
@@ -0,0 +1,11 @@
+#include "animation/blend_state.hpp"
+
+using namespace rive;
+
+void BlendState::addAnimation(BlendAnimation* animation)
+{
+       // Assert it's not already contained.
+       assert(std::find(m_Animations.begin(), m_Animations.end(), animation) ==
+              m_Animations.end());
+       m_Animations.push_back(animation);
+}
\ No newline at end of file
diff --git a/submodule/src/animation/blend_state_1d.cpp b/submodule/src/animation/blend_state_1d.cpp
new file mode 100644 (file)
index 0000000..965a0d1
--- /dev/null
@@ -0,0 +1,35 @@
+#include "animation/blend_state_1d.hpp"
+#include "animation/state_machine.hpp"
+#include "animation/state_machine_number.hpp"
+#include "animation/blend_state_1d_instance.hpp"
+#include "importers/state_machine_importer.hpp"
+
+using namespace rive;
+
+StateInstance* BlendState1D::makeInstance() const
+{
+       return new BlendState1DInstance(this);
+}
+
+StatusCode BlendState1D::import(ImportStack& importStack)
+{
+       auto stateMachineImporter =
+           importStack.latest<StateMachineImporter>(StateMachine::typeKey);
+       if (stateMachineImporter == nullptr)
+       {
+               return StatusCode::MissingObject;
+       }
+
+       // Make sure the inputId doesn't overflow the input buffer.
+       if (inputId() < 0 ||
+           inputId() >= stateMachineImporter->stateMachine()->inputCount())
+       {
+               return StatusCode::InvalidObject;
+       }
+       auto input = stateMachineImporter->stateMachine()->input((size_t)inputId());
+       if (input == nullptr || !input->is<StateMachineNumber>())
+       {
+               return StatusCode::InvalidObject;
+       }
+       return Super::import(importStack);
+}
\ No newline at end of file
diff --git a/submodule/src/animation/blend_state_1d_instance.cpp b/submodule/src/animation/blend_state_1d_instance.cpp
new file mode 100644 (file)
index 0000000..d14b3d4
--- /dev/null
@@ -0,0 +1,90 @@
+#include "animation/blend_state_1d_instance.hpp"
+#include "animation/state_machine_input_instance.hpp"
+
+using namespace rive;
+
+BlendState1DInstance::BlendState1DInstance(const BlendState1D* blendState) :
+    BlendStateInstance<BlendState1D, BlendAnimation1D>(blendState)
+{
+}
+
+int BlendState1DInstance::animationIndex(float value)
+{
+       int idx = 0;
+       int mid = 0;
+       float closestValue = 0;
+       int start = 0;
+       int end = static_cast<int>(m_AnimationInstances.size()) - 1;
+
+       while (start <= end)
+       {
+               mid = (start + end) >> 1;
+               closestValue = m_AnimationInstances[mid].blendAnimation()->value();
+               if (closestValue < value)
+               {
+                       start = mid + 1;
+               }
+               else if (closestValue > value)
+               {
+                       end = mid - 1;
+               }
+               else
+               {
+                       idx = start = mid;
+                       break;
+               }
+
+               idx = start;
+       }
+       return idx;
+}
+
+void BlendState1DInstance::advance(float seconds, SMIInput** inputs)
+{
+       BlendStateInstance<BlendState1D, BlendAnimation1D>::advance(seconds,
+                                                                   inputs);
+
+       auto inputInstance = inputs[state()->as<BlendState1D>()->inputId()];
+
+       auto numberInput = reinterpret_cast<const SMINumber*>(inputInstance);
+       auto value = numberInput->value();
+       int index = animationIndex(value);
+       auto animationsCount = static_cast<int>(m_AnimationInstances.size());
+       m_To = index >= 0 && index < animationsCount ? &m_AnimationInstances[index]
+                                                    : nullptr;
+       m_From = index - 1 >= 0 && index - 1 < animationsCount
+                    ? &m_AnimationInstances[index - 1]
+                    : nullptr;
+
+       float mix, mixFrom;
+       auto toValue = m_To == nullptr ? 0.0f : m_To->blendAnimation()->value();
+       auto fromValue =
+           m_From == nullptr ? 0.0f : m_From->blendAnimation()->value();
+
+       if (m_To == nullptr || m_From == nullptr || toValue == fromValue)
+       {
+               mix = mixFrom = 1.0f;
+       }
+       else
+       {
+               mix = (value - fromValue) / (toValue - fromValue);
+               mixFrom = 1.0f - mix;
+       }
+
+       for (auto& animation : m_AnimationInstances)
+       {
+               auto animationValue = animation.blendAnimation()->value();
+               if (m_To != nullptr && animationValue == toValue)
+               {
+                       animation.mix(mix);
+               }
+               else if (m_From != nullptr && animationValue == fromValue)
+               {
+                       animation.mix(mixFrom);
+               }
+               else
+               {
+                       animation.mix(0.0f);
+               }
+       }
+}
diff --git a/submodule/src/animation/blend_state_direct.cpp b/submodule/src/animation/blend_state_direct.cpp
new file mode 100644 (file)
index 0000000..67356b2
--- /dev/null
@@ -0,0 +1,12 @@
+#include "animation/blend_state_direct.hpp"
+#include "animation/state_machine.hpp"
+#include "animation/state_machine_number.hpp"
+#include "animation/blend_state_direct_instance.hpp"
+#include "importers/state_machine_importer.hpp"
+
+using namespace rive;
+
+StateInstance* BlendStateDirect::makeInstance() const
+{
+       return new BlendStateDirectInstance(this);
+}
\ No newline at end of file
diff --git a/submodule/src/animation/blend_state_direct_instance.cpp b/submodule/src/animation/blend_state_direct_instance.cpp
new file mode 100644 (file)
index 0000000..1e2bef8
--- /dev/null
@@ -0,0 +1,24 @@
+#include "animation/blend_state_direct_instance.hpp"
+#include "animation/state_machine_input_instance.hpp"
+
+using namespace rive;
+
+BlendStateDirectInstance::BlendStateDirectInstance(
+    const BlendStateDirect* blendState) :
+    BlendStateInstance<BlendStateDirect, BlendAnimationDirect>(blendState)
+{
+}
+
+void BlendStateDirectInstance::advance(float seconds, SMIInput** inputs)
+{
+       BlendStateInstance<BlendStateDirect, BlendAnimationDirect>::advance(seconds,
+                                                                           inputs);
+       for (auto& animation : m_AnimationInstances)
+       {
+               auto inputInstance = inputs[animation.blendAnimation()->inputId()];
+
+               auto numberInput = reinterpret_cast<const SMINumber*>(inputInstance);
+               auto value = numberInput->value();
+               animation.mix(std::min(1.0f, std::max(0.0f, value / 100.0f)));
+       }
+}
diff --git a/submodule/src/animation/blend_state_transition.cpp b/submodule/src/animation/blend_state_transition.cpp
new file mode 100644 (file)
index 0000000..de3d807
--- /dev/null
@@ -0,0 +1,41 @@
+#include "artboard.hpp"
+#include "animation/blend_state_transition.hpp"
+#include "animation/blend_state_instance.hpp"
+#include "animation/blend_state_1d.hpp"
+#include "animation/blend_state_direct.hpp"
+#include "animation/blend_state_1d_instance.hpp"
+#include "animation/blend_state_direct_instance.hpp"
+
+using namespace rive;
+
+const LinearAnimationInstance*
+BlendStateTransition::exitTimeAnimationInstance(const StateInstance* from) const
+{
+       if (from != nullptr)
+       {
+               switch (from->state()->coreType())
+               {
+                       case BlendState1D::typeKey:
+
+                               return static_cast<const BlendState1DInstance*>(from)
+                                   ->animationInstance(m_ExitBlendAnimation);
+
+                       case BlendStateDirect::typeKey:
+
+                               return static_cast<const BlendStateDirectInstance*>(from)
+                                   ->animationInstance(m_ExitBlendAnimation);
+               }
+       }
+
+       return nullptr;
+}
+
+const LinearAnimation*
+BlendStateTransition::exitTimeAnimation(const LayerState* from) const
+{
+       if (m_ExitBlendAnimation != nullptr)
+       {
+               return m_ExitBlendAnimation->animation();
+       }
+       return nullptr;
+}
\ No newline at end of file
index e21d33e..a40de0e 100644 (file)
@@ -4,8 +4,7 @@
 #include "importers/state_machine_layer_importer.hpp"
 #include "generated/animation/state_machine_layer_base.hpp"
 #include "animation/state_transition.hpp"
-
-using namespace rive;
+#include "animation/system_state_instance.hpp"
 
 using namespace rive;
 
@@ -58,4 +57,9 @@ StatusCode LayerState::import(ImportStack& importStack)
 void LayerState::addTransition(StateTransition* transition)
 {
        m_Transitions.push_back(transition);
+}
+
+StateInstance* LayerState::makeInstance() const
+{
+       return new SystemStateInstance(this);
 }
\ No newline at end of file
diff --git a/submodule/src/animation/state_instance.cpp b/submodule/src/animation/state_instance.cpp
new file mode 100644 (file)
index 0000000..dc9e49e
--- /dev/null
@@ -0,0 +1,11 @@
+#include "animation/state_instance.hpp"
+using namespace rive;
+
+StateInstance::StateInstance(const LayerState* layerState) :
+    m_LayerState(layerState)
+{
+}
+
+StateInstance::~StateInstance() {}
+
+const LayerState* StateInstance::state() const { return m_LayerState; }
\ No newline at end of file
index 4d28625..6103052 100644 (file)
@@ -115,7 +115,6 @@ const StateMachineLayer* StateMachine::layer(std::string name) const
 
 const StateMachineLayer* StateMachine::layer(size_t index) const
 {
-
        if (index >= 0 && index < m_Layers.size())
        {
                return m_Layers[index];
index d84309c..98b3ea3 100644 (file)
@@ -11,6 +11,8 @@
 #include "animation/state_transition.hpp"
 #include "animation/transition_condition.hpp"
 #include "animation/animation_state.hpp"
+#include "animation/state_instance.hpp"
+#include "animation/animation_state_instance.hpp"
 
 using namespace rive;
 
@@ -21,60 +23,84 @@ namespace rive
        private:
                static const int maxIterations = 100;
                const StateMachineLayer* m_Layer = nullptr;
-               const LayerState* m_CurrentState = nullptr;
-               const LayerState* m_StateFrom = nullptr;
+
+               StateInstance* m_AnyStateInstance = nullptr;
+               StateInstance* m_CurrentState = nullptr;
+               StateInstance* m_StateFrom = nullptr;
+
+               // const LayerState* m_CurrentState = nullptr;
+               // const LayerState* m_StateFrom = nullptr;
                const StateTransition* m_Transition = nullptr;
 
                bool m_HoldAnimationFrom = false;
-               LinearAnimationInstance* m_AnimationInstance = nullptr;
-               LinearAnimationInstance* m_AnimationInstanceFrom = nullptr;
+               // LinearAnimationInstance* m_AnimationInstance = nullptr;
+               // LinearAnimationInstance* m_AnimationInstanceFrom = nullptr;
                float m_Mix = 1.0f;
-               bool m_stateChangedOnAdvance = false;
+               float m_MixFrom = 1.0f;
+               bool m_StateChangedOnAdvance = false;
+
+               bool m_WaitingForExit = false;
+               /// Used to ensure a specific animation is applied on the next apply.
+               const LinearAnimation* m_HoldAnimation = nullptr;
+               float m_HoldTime = 0.0f;
 
        public:
+               ~StateMachineLayerInstance()
+               {
+                       delete m_AnyStateInstance;
+                       delete m_CurrentState;
+                       delete m_StateFrom;
+               }
+
                void init(const StateMachineLayer* layer)
                {
+                       assert(m_Layer == nullptr);
+                       m_AnyStateInstance = layer->anyState()->makeInstance();
                        m_Layer = layer;
-                       m_CurrentState = m_Layer->entryState();
+                       changeState(m_Layer->entryState());
                }
 
-               bool advance(float seconds, SMIInput** inputs, size_t inputCount)
+               void updateMix(float seconds)
                {
-                       bool keepGoing = false;
-                       m_stateChangedOnAdvance = false;
-
-                       if (m_AnimationInstance != nullptr)
-                       {
-                               keepGoing = m_AnimationInstance->advance(seconds);
-                       }
-
                        if (m_Transition != nullptr && m_StateFrom != nullptr &&
                            m_Transition->duration() != 0)
                        {
-                               m_Mix =
-                                   std::min(1.0f,
-                                            std::max(0.0f,
-                                                     (m_Mix + seconds / m_Transition->mixTime(
-                                                                            m_StateFrom))));
+                               m_Mix = std::min(
+                                   1.0f,
+                                   std::max(0.0f,
+                                            (m_Mix + seconds / m_Transition->mixTime(
+                                                                   m_StateFrom->state()))));
                        }
                        else
                        {
                                m_Mix = 1.0f;
                        }
+               }
 
-                       if (m_AnimationInstanceFrom != nullptr && m_Mix < 1.0f &&
-                           !m_HoldAnimationFrom)
+               bool advance(Artboard* artboard,
+                            float seconds,
+                            SMIInput** inputs,
+                            size_t inputCount)
+               {
+                       m_StateChangedOnAdvance = false;
+
+                       if (m_CurrentState != nullptr)
                        {
-                               m_AnimationInstanceFrom->advance(seconds);
+                               m_CurrentState->advance(seconds, inputs);
                        }
 
-                       for (int i = 0; updateState(inputs); i++)
+                       updateMix(seconds);
+
+                       if (m_StateFrom != nullptr && m_Mix < 1.0f && !m_HoldAnimationFrom)
                        {
-                               // Reset inputs between updates.
-                               for (size_t i = 0; i < inputCount; i++)
-                               {
-                                       inputs[i]->advanced();
-                               }
+                               // This didn't advance during our updateState, but it should now
+                               // that we realize we need to mix it in.
+                               m_StateFrom->advance(seconds, inputs);
+                       }
+
+                       for (int i = 0; updateState(inputs, i != 0); i++)
+                       {
+                               apply(artboard);
 
                                if (i == maxIterations)
                                {
@@ -82,167 +108,162 @@ namespace rive
                                        return false;
                                }
                        }
-                       return m_Mix != 1.0f || keepGoing;
+
+                       apply(artboard);
+
+                       return m_Mix != 1.0f || m_WaitingForExit ||
+                              (m_CurrentState != nullptr && m_CurrentState->keepGoing());
                }
 
-               bool updateState(SMIInput** inputs)
+               bool isTransitioning()
                {
-                       if (tryChangeState(m_Layer->anyState(), inputs))
+                       return m_Transition != nullptr && m_StateFrom != nullptr &&
+                              m_Transition->duration() != 0 && m_Mix < 1.0f;
+               }
+
+               bool updateState(SMIInput** inputs, bool ignoreTriggers)
+               {
+                       // Don't allow changing state while a transition is taking place
+                       // (we're mixing one state onto another).
+                       if (isTransitioning())
+                       {
+                               return false;
+                       }
+
+                       m_WaitingForExit = false;
+
+                       if (tryChangeState(m_AnyStateInstance, inputs, ignoreTriggers))
                        {
                                return true;
                        }
 
-                       return tryChangeState(m_CurrentState, inputs);
+                       return tryChangeState(m_CurrentState, inputs, ignoreTriggers);
                }
 
                bool changeState(const LayerState* stateTo)
                {
-                       if (m_CurrentState == stateTo)
+                       if ((m_CurrentState == nullptr
+                                ? nullptr
+                                : m_CurrentState->state()) == stateTo)
                        {
                                return false;
                        }
-                       m_CurrentState = stateTo;
-                       m_stateChangedOnAdvance = true;
+                       m_CurrentState =
+                           stateTo == nullptr ? nullptr : stateTo->makeInstance();
                        return true;
                }
 
-               bool tryChangeState(const LayerState* stateFrom, SMIInput** inputs)
+               bool tryChangeState(StateInstance* stateFromInstance,
+                                   SMIInput** inputs,
+                                   bool ignoreTriggers)
                {
-                       if (stateFrom == nullptr)
+                       if (stateFromInstance == nullptr)
                        {
                                return false;
                        }
+                       auto stateFrom = stateFromInstance->state();
+                       auto outState = m_CurrentState;
                        for (size_t i = 0, length = stateFrom->transitionCount();
                             i < length;
                             i++)
                        {
                                auto transition = stateFrom->transition(i);
-
-                               if (transition->isDisabled())
-                               {
-                                       continue;
-                               }
-
-                               bool valid = true;
-                               for (size_t j = 0, length = transition->conditionCount();
-                                    j < length;
-                                    j++)
-                               {
-                                       auto condition = transition->condition(j);
-                                       auto input = inputs[condition->inputId()];
-                                       if (!condition->evaluate(input))
-                                       {
-                                               valid = false;
-                                               break;
-                                       }
-                               }
-
-                               // If all the conditions evaluated to true, make sure the exit
-                               // time (when set) is also valid.
-                               if (valid && stateFrom->is<AnimationState>() &&
-                                   transition->enableExitTime())
-                               {
-                                       auto fromAnimation =
-                                           stateFrom->as<AnimationState>()->animation();
-                                       assert(fromAnimation != nullptr);
-                                       auto lastTime = m_AnimationInstance->lastTotalTime();
-                                       auto time = m_AnimationInstance->totalTime();
-                                       auto exitTime =
-                                           transition->exitTimeSeconds(stateFrom, true);
-                                       if (exitTime < fromAnimation->durationSeconds())
-                                       {
-                                               // Get exit time relative to the loop lastTime was in.
-                                               exitTime +=
-                                                   (int)(lastTime / fromAnimation->durationSeconds()) *
-                                                   fromAnimation->durationSeconds();
-                                       }
-                                       if (time < exitTime)
-                                       {
-                                               valid = false;
-                                       }
-                               }
-
-                               // Try to change the state...
-                               if (valid && changeState(transition->stateTo()))
+                               auto allowed = transition->allowed(
+                                   stateFromInstance, inputs, ignoreTriggers);
+                               if (allowed == AllowTransition::yes &&
+                                   changeState(transition->stateTo()))
                                {
+                                       m_StateChangedOnAdvance = true;
                                        // state actually has changed
                                        m_Transition = transition;
-                                       m_StateFrom = stateFrom;
+                                       if (m_StateFrom != m_AnyStateInstance)
+                                       {
+                                               // Old state from is done.
+                                               delete m_StateFrom;
+                                       }
+                                       m_StateFrom = outState;
 
                                        // If we had an exit time and wanted to pause on exit, make
-                                       // sure to hold the exit time.
-                                       if (transition->pauseOnExit() &&
-                                           transition->enableExitTime() &&
-                                           m_AnimationInstance != nullptr)
+                                       // sure to hold the exit time. Delegate this to the
+                                       // transition by telling it that it was completed.
+                                       if (outState != nullptr &&
+                                           transition->applyExitCondition(outState))
                                        {
-                                               m_AnimationInstance->time(
-                                                   transition->exitTimeSeconds(stateFrom, false));
+                                               // Make sure we apply this state. This only returns true
+                                               // when it's an animation state instance.
+                                               auto instance = static_cast<AnimationStateInstance*>(
+                                                                   stateFromInstance)
+                                                                   ->animationInstance();
+
+                                               m_HoldAnimation = instance->animation();
+                                               m_HoldTime = instance->time();
                                        }
+                                       m_MixFrom = m_Mix;
 
+                                       // Keep mixing last animation that was mixed in.
                                        if (m_Mix != 0.0f)
                                        {
                                                m_HoldAnimationFrom = transition->pauseOnExit();
-                                               delete m_AnimationInstanceFrom;
-                                               m_AnimationInstanceFrom = m_AnimationInstance;
-
-                                               // Don't let it get deleted as we've passed it on to the
-                                               // from.
-                                               m_AnimationInstance = nullptr;
                                        }
-                                       else
+                                       if (m_StateFrom != nullptr &&
+                                           m_StateFrom->state()->is<AnimationState>() &&
+                                           m_CurrentState != nullptr)
                                        {
-                                               delete m_AnimationInstance;
-                                               m_AnimationInstance = nullptr;
-                                       }
-                                       if (m_CurrentState->is<AnimationState>())
-                                       {
-                                               auto animationState =
-                                                   m_CurrentState->as<AnimationState>();
-                                               auto spilledTime =
-                                                   m_AnimationInstanceFrom == nullptr
-                                                       ? 0
-                                                       : m_AnimationInstanceFrom->spilledTime();
-                                               if (animationState->animation() != nullptr)
-                                               {
-                                                       m_AnimationInstance = new LinearAnimationInstance(
-                                                           animationState->animation());
-                                                       m_AnimationInstance->advance(spilledTime);
-                                               }
-                                               m_Mix = 0.0f;
+                                               auto instance = static_cast<AnimationStateInstance*>(
+                                                                   stateFromInstance)
+                                                                   ->animationInstance();
+
+                                               auto spilledTime = instance->spilledTime();
+                                               m_CurrentState->advance(spilledTime, inputs);
                                        }
+                                       m_Mix = 0.0f;
+                                       updateMix(0.0f);
+                                       m_WaitingForExit = false;
                                        return true;
                                }
+                               else if (allowed == AllowTransition::waitingForExit)
+                               {
+                                       m_WaitingForExit = true;
+                               }
                        }
                        return false;
                }
 
-               void apply(Artboard* artboard) const
+               void apply(Artboard* artboard)
                {
-                       if (m_AnimationInstanceFrom != nullptr && m_Mix < 1.0f)
+                       if (m_HoldAnimation != nullptr)
                        {
-                               m_AnimationInstanceFrom->animation()->apply(
-                                   artboard, m_AnimationInstanceFrom->time(), 1.0 - m_Mix);
+                               m_HoldAnimation->apply(artboard, m_HoldTime, m_MixFrom);
+                               m_HoldAnimation = nullptr;
                        }
-                       if (m_AnimationInstance != nullptr)
+
+                       if (m_StateFrom != nullptr && m_Mix < 1.0f)
+                       {
+                               m_StateFrom->apply(artboard, m_MixFrom);
+                       }
+                       if (m_CurrentState != nullptr)
                        {
-                               m_AnimationInstance->animation()->apply(
-                                   artboard, m_AnimationInstance->time(), m_Mix);
+                               m_CurrentState->apply(artboard, m_Mix);
                        }
                }
 
-               bool stateChangedOnAdvance() const
-               {
-                       return m_stateChangedOnAdvance;
-               }
+               bool stateChangedOnAdvance() const { return m_StateChangedOnAdvance; }
 
-               const LayerState* currentState() 
+               const LayerState* currentState()
                {
-                       return m_CurrentState;
+                       return m_CurrentState == nullptr ? nullptr
+                                                        : m_CurrentState->state();
                }
 
                const LinearAnimationInstance* currentAnimation() const
                {
-                       return m_AnimationInstance;
+                       if (m_CurrentState == nullptr ||
+                           !m_CurrentState->state()->is<AnimationState>())
+                       {
+                               return nullptr;
+                       }
+                       return static_cast<AnimationStateInstance*>(m_CurrentState)
+                           ->animationInstance();
                }
        };
 } // namespace rive
@@ -299,12 +320,13 @@ StateMachineInstance::~StateMachineInstance()
        delete[] m_Layers;
 }
 
-bool StateMachineInstance::advance(float seconds)
+bool StateMachineInstance::advance(Artboard* artboard, float seconds)
 {
        m_NeedsAdvance = false;
        for (int i = 0; i < m_LayerCount; i++)
        {
-               if (m_Layers[i].advance(seconds, m_InputInstances, m_InputCount))
+               if (m_Layers[i].advance(
+                       artboard, seconds, m_InputInstances, m_InputCount))
                {
                        m_NeedsAdvance = true;
                }
@@ -318,14 +340,6 @@ bool StateMachineInstance::advance(float seconds)
        return m_NeedsAdvance;
 }
 
-void StateMachineInstance::apply(Artboard* artboard) const
-{
-       for (int i = 0; i < m_LayerCount; i++)
-       {
-               m_Layers[i].apply(artboard);
-       }
-}
-
 void StateMachineInstance::markNeedsAdvance() { m_NeedsAdvance = true; }
 bool StateMachineInstance::needsAdvance() const { return m_NeedsAdvance; }
 
@@ -384,7 +398,7 @@ size_t StateMachineInstance::stateChangedCount() const
                if (m_Layers[i].stateChangedOnAdvance())
                {
                        count++;
-               } 
+               }
        }
        return count;
 }
@@ -401,7 +415,7 @@ const LayerState* StateMachineInstance::stateChangedByIndex(size_t index) const
                                return m_Layers[i].currentState();
                        }
                        count++;
-               } 
+               }
        }
        return nullptr;
 }
@@ -411,7 +425,7 @@ const size_t StateMachineInstance::currentAnimationCount() const
        size_t count = 0;
        for (int i = 0; i < m_LayerCount; i++)
        {
-               if(m_Layers[i].currentAnimation() != nullptr)
+               if (m_Layers[i].currentAnimation() != nullptr)
                {
                        count++;
                }
@@ -419,7 +433,8 @@ const size_t StateMachineInstance::currentAnimationCount() const
        return count;
 }
 
-const LinearAnimationInstance* StateMachineInstance::currentAnimationByIndex(size_t index) const
+const LinearAnimationInstance*
+StateMachineInstance::currentAnimationByIndex(size_t index) const
 {
        size_t count = 0;
        for (int i = 0; i < m_LayerCount; i++)
@@ -431,7 +446,7 @@ const LinearAnimationInstance* StateMachineInstance::currentAnimationByIndex(siz
                                return m_Layers[i].currentAnimation();
                        }
                        count++;
-               } 
+               }
        }
        return nullptr;
 }
\ No newline at end of file
index b1a882d..227cf2d 100644 (file)
@@ -5,6 +5,10 @@
 #include "animation/transition_condition.hpp"
 #include "animation/animation_state.hpp"
 #include "animation/linear_animation.hpp"
+#include "animation/state_machine_input_instance.hpp"
+#include "animation/state_machine_trigger.hpp"
+#include "animation/animation_state_instance.hpp"
+#include "animation/transition_trigger_condition.hpp"
 
 using namespace rive;
 
@@ -86,30 +90,103 @@ float StateTransition::mixTime(const LayerState* stateFrom) const
 }
 
 float StateTransition::exitTimeSeconds(const LayerState* stateFrom,
-                                       bool relativeToWorkArea) const
+                                       bool absolute) const
 {
-       auto exitValue = exitTime();
-       if (exitValue == 0)
+       if ((transitionFlags() & StateTransitionFlags::ExitTimeIsPercentage) ==
+           StateTransitionFlags::ExitTimeIsPercentage)
        {
-               return 0;
+               float animationDuration = 0.0f;
+               float start = 0.0f;
+
+               auto exitAnimation = exitTimeAnimation(stateFrom);
+               if (exitAnimation != nullptr)
+               {
+                       start = absolute ? exitAnimation->startSeconds() : 0.0f;
+                       animationDuration = exitAnimation->durationSeconds();
+               }
+
+               return start + exitTime() / 100.0f * animationDuration;
        }
+       return exitTime() / 1000.0f;
+}
 
-       float animationDuration = 0.0f;
-       float animationOrigin = 0.0f;
-       if (stateFrom->is<AnimationState>())
+const LinearAnimationInstance*
+StateTransition::exitTimeAnimationInstance(const StateInstance* from) const
+{
+       return from != nullptr && from->state()->is<AnimationState>()
+                  ? static_cast<const AnimationStateInstance*>(from)
+                        ->animationInstance()
+                  : nullptr;
+}
+
+const LinearAnimation*
+StateTransition::exitTimeAnimation(const LayerState* from) const
+{
+       return from != nullptr && from->is<AnimationState>()
+                  ? from->as<AnimationState>()->animation()
+                  : nullptr;
+}
+
+AllowTransition StateTransition::allowed(StateInstance* stateFrom,
+                                         SMIInput** inputs,
+                                         bool ignoreTriggers) const
+{
+       if (isDisabled())
        {
-               auto animation = stateFrom->as<AnimationState>()->animation();
-               animationDuration = animation->durationSeconds();
-               animationOrigin = relativeToWorkArea ? 0 : animation->startSeconds();
+               return AllowTransition::no;
        }
 
-       if ((transitionFlags() & StateTransitionFlags::ExitTimeIsPercentage) ==
-           StateTransitionFlags::ExitTimeIsPercentage)
+       for (auto condition : m_Conditions)
        {
-               return animationOrigin + exitValue / 100.0f * animationDuration;
+               // N.B. state machine instance sanitizes these for us...
+               auto input = inputs[condition->inputId()];
+
+               if ((ignoreTriggers && condition->is<TransitionTriggerCondition>()) ||
+                   !condition->evaluate(input))
+               {
+                       return AllowTransition::no;
+               }
        }
-       else
+
+       if (enableExitTime())
        {
-               return animationOrigin + exitValue / 1000.0f;
+               auto exitAnimation = exitTimeAnimationInstance(stateFrom);
+               if (exitAnimation != nullptr)
+               {
+                       // Exit time is specified in a value less than a single loop, so we
+                       // want to allow exiting regardless of which loop we're on. To do
+                       // that we bring the exit time up to the loop our lastTime is at.
+                       auto lastTime = exitAnimation->lastTotalTime();
+                       auto time = exitAnimation->totalTime();
+                       auto exitTime = exitTimeSeconds(stateFrom->state());
+                       auto animationFrom = exitAnimation->animation();
+                       auto duration = animationFrom->durationSeconds();
+                       if (exitTime < duration)
+                       {
+                               // Get exit time relative to the loop lastTime was in.
+                               exitTime += std::floor(lastTime / duration) * duration;
+                       }
+
+                       if (time < exitTime)
+                       {
+                               return AllowTransition::waitingForExit;
+                       }
+               }
        }
+       return AllowTransition::yes;
 }
+
+bool StateTransition::applyExitCondition(StateInstance* from) const
+{
+       // Hold exit time when the user has set to pauseOnExit on this condition
+       // (only valid when exiting from an Animation).
+       bool useExitTime = enableExitTime() &&
+                          (from != nullptr && from->state()->is<AnimationState>());
+       if (pauseOnExit() && useExitTime)
+       {
+               static_cast<AnimationStateInstance*>(from)->animationInstance()->time(
+                   exitTimeSeconds(from->state(), true));
+               return true;
+       }
+       return useExitTime;
+}
\ No newline at end of file
diff --git a/submodule/src/animation/system_state_instance.cpp b/submodule/src/animation/system_state_instance.cpp
new file mode 100644 (file)
index 0000000..54c5d0e
--- /dev/null
@@ -0,0 +1,12 @@
+#include "animation/system_state_instance.hpp"
+using namespace rive;
+
+SystemStateInstance::SystemStateInstance(const LayerState* layerState) :
+    StateInstance(layerState)
+{
+}
+
+void SystemStateInstance::advance(float seconds, SMIInput** inputs) {}
+void SystemStateInstance::apply(Artboard* artboard, float mix) {}
+
+bool SystemStateInstance::keepGoing() const { return false; }
\ No newline at end of file
index 1525353..cbe17a5 100644 (file)
 #include "importers/state_machine_layer_importer.hpp"
 #include "importers/layer_state_importer.hpp"
 #include "importers/state_transition_importer.hpp"
+#include "animation/blend_state_transition.hpp"
 #include "animation/any_state.hpp"
 #include "animation/entry_state.hpp"
 #include "animation/exit_state.hpp"
 #include "animation/animation_state.hpp"
+#include "animation/blend_state_1d.hpp"
+#include "animation/blend_state_direct.hpp"
 
 // Default namespace for Rive Cpp code
 using namespace rive;
@@ -194,12 +197,16 @@ ImportResult File::read(BinaryReader& reader, const RuntimeHeader& header)
                        case ExitState::typeKey:
                        case AnyState::typeKey:
                        case AnimationState::typeKey:
+                       case BlendState1D::typeKey:
+                       case BlendStateDirect::typeKey:
                                stackObject = new LayerStateImporter(object->as<LayerState>());
                                stackType = LayerState::typeKey;
                                break;
                        case StateTransition::typeKey:
+                       case BlendStateTransition::typeKey:
                                stackObject =
                                    new StateTransitionImporter(object->as<StateTransition>());
+                               stackType = StateTransition::typeKey;
                                break;
                }
                if (importStack.makeLatest(stackType, stackObject) != StatusCode::Ok)
diff --git a/submodule/src/generated/animation/animation_base.cpp b/submodule/src/generated/animation/animation_base.cpp
new file mode 100644 (file)
index 0000000..f91d78d
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/animation/animation_base.hpp"
+#include "animation/animation.hpp"
+
+using namespace rive;
+
+Core* AnimationBase::clone() const
+{
+       auto cloned = new Animation();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/animation/animation_state_base.cpp b/submodule/src/generated/animation/animation_state_base.cpp
new file mode 100644 (file)
index 0000000..1ca34dc
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/animation/animation_state_base.hpp"
+#include "animation/animation_state.hpp"
+
+using namespace rive;
+
+Core* AnimationStateBase::clone() const
+{
+       auto cloned = new AnimationState();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/animation/any_state_base.cpp b/submodule/src/generated/animation/any_state_base.cpp
new file mode 100644 (file)
index 0000000..f94d5d1
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/animation/any_state_base.hpp"
+#include "animation/any_state.hpp"
+
+using namespace rive;
+
+Core* AnyStateBase::clone() const
+{
+       auto cloned = new AnyState();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/animation/cubic_interpolator_base.cpp b/submodule/src/generated/animation/cubic_interpolator_base.cpp
new file mode 100644 (file)
index 0000000..86b7683
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/animation/cubic_interpolator_base.hpp"
+#include "animation/cubic_interpolator.hpp"
+
+using namespace rive;
+
+Core* CubicInterpolatorBase::clone() const
+{
+       auto cloned = new CubicInterpolator();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/animation/entry_state_base.cpp b/submodule/src/generated/animation/entry_state_base.cpp
new file mode 100644 (file)
index 0000000..5f5ec14
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/animation/entry_state_base.hpp"
+#include "animation/entry_state.hpp"
+
+using namespace rive;
+
+Core* EntryStateBase::clone() const
+{
+       auto cloned = new EntryState();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/animation/exit_state_base.cpp b/submodule/src/generated/animation/exit_state_base.cpp
new file mode 100644 (file)
index 0000000..56c790c
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/animation/exit_state_base.hpp"
+#include "animation/exit_state.hpp"
+
+using namespace rive;
+
+Core* ExitStateBase::clone() const
+{
+       auto cloned = new ExitState();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/animation/keyed_object_base.cpp b/submodule/src/generated/animation/keyed_object_base.cpp
new file mode 100644 (file)
index 0000000..e428590
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/animation/keyed_object_base.hpp"
+#include "animation/keyed_object.hpp"
+
+using namespace rive;
+
+Core* KeyedObjectBase::clone() const
+{
+       auto cloned = new KeyedObject();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/animation/keyed_property_base.cpp b/submodule/src/generated/animation/keyed_property_base.cpp
new file mode 100644 (file)
index 0000000..6c1c3d7
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/animation/keyed_property_base.hpp"
+#include "animation/keyed_property.hpp"
+
+using namespace rive;
+
+Core* KeyedPropertyBase::clone() const
+{
+       auto cloned = new KeyedProperty();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/animation/keyframe_color_base.cpp b/submodule/src/generated/animation/keyframe_color_base.cpp
new file mode 100644 (file)
index 0000000..1fa4fab
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/animation/keyframe_color_base.hpp"
+#include "animation/keyframe_color.hpp"
+
+using namespace rive;
+
+Core* KeyFrameColorBase::clone() const
+{
+       auto cloned = new KeyFrameColor();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/animation/keyframe_double_base.cpp b/submodule/src/generated/animation/keyframe_double_base.cpp
new file mode 100644 (file)
index 0000000..9d5975d
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/animation/keyframe_double_base.hpp"
+#include "animation/keyframe_double.hpp"
+
+using namespace rive;
+
+Core* KeyFrameDoubleBase::clone() const
+{
+       auto cloned = new KeyFrameDouble();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/animation/keyframe_id_base.cpp b/submodule/src/generated/animation/keyframe_id_base.cpp
new file mode 100644 (file)
index 0000000..87815bc
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/animation/keyframe_id_base.hpp"
+#include "animation/keyframe_id.hpp"
+
+using namespace rive;
+
+Core* KeyFrameIdBase::clone() const
+{
+       auto cloned = new KeyFrameId();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/animation/linear_animation_base.cpp b/submodule/src/generated/animation/linear_animation_base.cpp
new file mode 100644 (file)
index 0000000..0024799
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/animation/linear_animation_base.hpp"
+#include "animation/linear_animation.hpp"
+
+using namespace rive;
+
+Core* LinearAnimationBase::clone() const
+{
+       auto cloned = new LinearAnimation();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/animation/state_machine_base.cpp b/submodule/src/generated/animation/state_machine_base.cpp
new file mode 100644 (file)
index 0000000..3eb6814
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/animation/state_machine_base.hpp"
+#include "animation/state_machine.hpp"
+
+using namespace rive;
+
+Core* StateMachineBase::clone() const
+{
+       auto cloned = new StateMachine();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/animation/state_machine_bool_base.cpp b/submodule/src/generated/animation/state_machine_bool_base.cpp
new file mode 100644 (file)
index 0000000..65f8d0d
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/animation/state_machine_bool_base.hpp"
+#include "animation/state_machine_bool.hpp"
+
+using namespace rive;
+
+Core* StateMachineBoolBase::clone() const
+{
+       auto cloned = new StateMachineBool();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/animation/state_machine_layer_base.cpp b/submodule/src/generated/animation/state_machine_layer_base.cpp
new file mode 100644 (file)
index 0000000..907e9ca
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/animation/state_machine_layer_base.hpp"
+#include "animation/state_machine_layer.hpp"
+
+using namespace rive;
+
+Core* StateMachineLayerBase::clone() const
+{
+       auto cloned = new StateMachineLayer();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/animation/state_machine_number_base.cpp b/submodule/src/generated/animation/state_machine_number_base.cpp
new file mode 100644 (file)
index 0000000..d1f9a25
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/animation/state_machine_number_base.hpp"
+#include "animation/state_machine_number.hpp"
+
+using namespace rive;
+
+Core* StateMachineNumberBase::clone() const
+{
+       auto cloned = new StateMachineNumber();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/animation/state_machine_trigger_base.cpp b/submodule/src/generated/animation/state_machine_trigger_base.cpp
new file mode 100644 (file)
index 0000000..aae23cc
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/animation/state_machine_trigger_base.hpp"
+#include "animation/state_machine_trigger.hpp"
+
+using namespace rive;
+
+Core* StateMachineTriggerBase::clone() const
+{
+       auto cloned = new StateMachineTrigger();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/animation/state_transition_base.cpp b/submodule/src/generated/animation/state_transition_base.cpp
new file mode 100644 (file)
index 0000000..3ba27d5
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/animation/state_transition_base.hpp"
+#include "animation/state_transition.hpp"
+
+using namespace rive;
+
+Core* StateTransitionBase::clone() const
+{
+       auto cloned = new StateTransition();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/animation/transition_bool_condition_base.cpp b/submodule/src/generated/animation/transition_bool_condition_base.cpp
new file mode 100644 (file)
index 0000000..1d61d07
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/animation/transition_bool_condition_base.hpp"
+#include "animation/transition_bool_condition.hpp"
+
+using namespace rive;
+
+Core* TransitionBoolConditionBase::clone() const
+{
+       auto cloned = new TransitionBoolCondition();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/animation/transition_number_condition_base.cpp b/submodule/src/generated/animation/transition_number_condition_base.cpp
new file mode 100644 (file)
index 0000000..bbca747
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/animation/transition_number_condition_base.hpp"
+#include "animation/transition_number_condition.hpp"
+
+using namespace rive;
+
+Core* TransitionNumberConditionBase::clone() const
+{
+       auto cloned = new TransitionNumberCondition();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/animation/transition_trigger_condition_base.cpp b/submodule/src/generated/animation/transition_trigger_condition_base.cpp
new file mode 100644 (file)
index 0000000..5ef1634
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/animation/transition_trigger_condition_base.hpp"
+#include "animation/transition_trigger_condition.hpp"
+
+using namespace rive;
+
+Core* TransitionTriggerConditionBase::clone() const
+{
+       auto cloned = new TransitionTriggerCondition();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/artboard_base.cpp b/submodule/src/generated/artboard_base.cpp
new file mode 100644 (file)
index 0000000..b5a34fd
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/artboard_base.hpp"
+#include "artboard.hpp"
+
+using namespace rive;
+
+Core* ArtboardBase::clone() const
+{
+       auto cloned = new Artboard();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/backboard_base.cpp b/submodule/src/generated/backboard_base.cpp
new file mode 100644 (file)
index 0000000..28a609f
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/backboard_base.hpp"
+#include "backboard.hpp"
+
+using namespace rive;
+
+Core* BackboardBase::clone() const
+{
+       auto cloned = new Backboard();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/bones/bone_base.cpp b/submodule/src/generated/bones/bone_base.cpp
new file mode 100644 (file)
index 0000000..4a10a1e
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/bones/bone_base.hpp"
+#include "bones/bone.hpp"
+
+using namespace rive;
+
+Core* BoneBase::clone() const
+{
+       auto cloned = new Bone();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/bones/cubic_weight_base.cpp b/submodule/src/generated/bones/cubic_weight_base.cpp
new file mode 100644 (file)
index 0000000..5b9ebb0
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/bones/cubic_weight_base.hpp"
+#include "bones/cubic_weight.hpp"
+
+using namespace rive;
+
+Core* CubicWeightBase::clone() const
+{
+       auto cloned = new CubicWeight();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/bones/root_bone_base.cpp b/submodule/src/generated/bones/root_bone_base.cpp
new file mode 100644 (file)
index 0000000..c88b526
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/bones/root_bone_base.hpp"
+#include "bones/root_bone.hpp"
+
+using namespace rive;
+
+Core* RootBoneBase::clone() const
+{
+       auto cloned = new RootBone();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/bones/skin_base.cpp b/submodule/src/generated/bones/skin_base.cpp
new file mode 100644 (file)
index 0000000..64d2d30
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/bones/skin_base.hpp"
+#include "bones/skin.hpp"
+
+using namespace rive;
+
+Core* SkinBase::clone() const
+{
+       auto cloned = new Skin();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/bones/tendon_base.cpp b/submodule/src/generated/bones/tendon_base.cpp
new file mode 100644 (file)
index 0000000..014854f
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/bones/tendon_base.hpp"
+#include "bones/tendon.hpp"
+
+using namespace rive;
+
+Core* TendonBase::clone() const
+{
+       auto cloned = new Tendon();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/bones/weight_base.cpp b/submodule/src/generated/bones/weight_base.cpp
new file mode 100644 (file)
index 0000000..e2347e3
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/bones/weight_base.hpp"
+#include "bones/weight.hpp"
+
+using namespace rive;
+
+Core* WeightBase::clone() const
+{
+       auto cloned = new Weight();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/draw_rules_base.cpp b/submodule/src/generated/draw_rules_base.cpp
new file mode 100644 (file)
index 0000000..8d3b56b
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/draw_rules_base.hpp"
+#include "draw_rules.hpp"
+
+using namespace rive;
+
+Core* DrawRulesBase::clone() const
+{
+       auto cloned = new DrawRules();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/draw_target_base.cpp b/submodule/src/generated/draw_target_base.cpp
new file mode 100644 (file)
index 0000000..4e8fb32
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/draw_target_base.hpp"
+#include "draw_target.hpp"
+
+using namespace rive;
+
+Core* DrawTargetBase::clone() const
+{
+       auto cloned = new DrawTarget();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/node_base.cpp b/submodule/src/generated/node_base.cpp
new file mode 100644 (file)
index 0000000..d78200d
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/node_base.hpp"
+#include "node.hpp"
+
+using namespace rive;
+
+Core* NodeBase::clone() const
+{
+       auto cloned = new Node();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/shapes/clipping_shape_base.cpp b/submodule/src/generated/shapes/clipping_shape_base.cpp
new file mode 100644 (file)
index 0000000..2a824a7
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/shapes/clipping_shape_base.hpp"
+#include "shapes/clipping_shape.hpp"
+
+using namespace rive;
+
+Core* ClippingShapeBase::clone() const
+{
+       auto cloned = new ClippingShape();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/shapes/cubic_asymmetric_vertex_base.cpp b/submodule/src/generated/shapes/cubic_asymmetric_vertex_base.cpp
new file mode 100644 (file)
index 0000000..e4e15e3
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/shapes/cubic_asymmetric_vertex_base.hpp"
+#include "shapes/cubic_asymmetric_vertex.hpp"
+
+using namespace rive;
+
+Core* CubicAsymmetricVertexBase::clone() const
+{
+       auto cloned = new CubicAsymmetricVertex();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/shapes/cubic_detached_vertex_base.cpp b/submodule/src/generated/shapes/cubic_detached_vertex_base.cpp
new file mode 100644 (file)
index 0000000..c261c9a
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/shapes/cubic_detached_vertex_base.hpp"
+#include "shapes/cubic_detached_vertex.hpp"
+
+using namespace rive;
+
+Core* CubicDetachedVertexBase::clone() const
+{
+       auto cloned = new CubicDetachedVertex();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/shapes/cubic_mirrored_vertex_base.cpp b/submodule/src/generated/shapes/cubic_mirrored_vertex_base.cpp
new file mode 100644 (file)
index 0000000..5042916
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/shapes/cubic_mirrored_vertex_base.hpp"
+#include "shapes/cubic_mirrored_vertex.hpp"
+
+using namespace rive;
+
+Core* CubicMirroredVertexBase::clone() const
+{
+       auto cloned = new CubicMirroredVertex();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/shapes/ellipse_base.cpp b/submodule/src/generated/shapes/ellipse_base.cpp
new file mode 100644 (file)
index 0000000..0bd28db
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/shapes/ellipse_base.hpp"
+#include "shapes/ellipse.hpp"
+
+using namespace rive;
+
+Core* EllipseBase::clone() const
+{
+       auto cloned = new Ellipse();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/shapes/paint/fill_base.cpp b/submodule/src/generated/shapes/paint/fill_base.cpp
new file mode 100644 (file)
index 0000000..50f77d1
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/shapes/paint/fill_base.hpp"
+#include "shapes/paint/fill.hpp"
+
+using namespace rive;
+
+Core* FillBase::clone() const
+{
+       auto cloned = new Fill();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/shapes/paint/gradient_stop_base.cpp b/submodule/src/generated/shapes/paint/gradient_stop_base.cpp
new file mode 100644 (file)
index 0000000..1675e99
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/shapes/paint/gradient_stop_base.hpp"
+#include "shapes/paint/gradient_stop.hpp"
+
+using namespace rive;
+
+Core* GradientStopBase::clone() const
+{
+       auto cloned = new GradientStop();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/shapes/paint/linear_gradient_base.cpp b/submodule/src/generated/shapes/paint/linear_gradient_base.cpp
new file mode 100644 (file)
index 0000000..ce52929
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/shapes/paint/linear_gradient_base.hpp"
+#include "shapes/paint/linear_gradient.hpp"
+
+using namespace rive;
+
+Core* LinearGradientBase::clone() const
+{
+       auto cloned = new LinearGradient();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/shapes/paint/radial_gradient_base.cpp b/submodule/src/generated/shapes/paint/radial_gradient_base.cpp
new file mode 100644 (file)
index 0000000..a337539
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/shapes/paint/radial_gradient_base.hpp"
+#include "shapes/paint/radial_gradient.hpp"
+
+using namespace rive;
+
+Core* RadialGradientBase::clone() const
+{
+       auto cloned = new RadialGradient();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/shapes/paint/solid_color_base.cpp b/submodule/src/generated/shapes/paint/solid_color_base.cpp
new file mode 100644 (file)
index 0000000..ea66a82
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/shapes/paint/solid_color_base.hpp"
+#include "shapes/paint/solid_color.hpp"
+
+using namespace rive;
+
+Core* SolidColorBase::clone() const
+{
+       auto cloned = new SolidColor();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/shapes/paint/stroke_base.cpp b/submodule/src/generated/shapes/paint/stroke_base.cpp
new file mode 100644 (file)
index 0000000..aa05103
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/shapes/paint/stroke_base.hpp"
+#include "shapes/paint/stroke.hpp"
+
+using namespace rive;
+
+Core* StrokeBase::clone() const
+{
+       auto cloned = new Stroke();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/shapes/paint/trim_path_base.cpp b/submodule/src/generated/shapes/paint/trim_path_base.cpp
new file mode 100644 (file)
index 0000000..a1c9c47
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/shapes/paint/trim_path_base.hpp"
+#include "shapes/paint/trim_path.hpp"
+
+using namespace rive;
+
+Core* TrimPathBase::clone() const
+{
+       auto cloned = new TrimPath();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/shapes/points_path_base.cpp b/submodule/src/generated/shapes/points_path_base.cpp
new file mode 100644 (file)
index 0000000..e6708f8
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/shapes/points_path_base.hpp"
+#include "shapes/points_path.hpp"
+
+using namespace rive;
+
+Core* PointsPathBase::clone() const
+{
+       auto cloned = new PointsPath();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/shapes/polygon_base.cpp b/submodule/src/generated/shapes/polygon_base.cpp
new file mode 100644 (file)
index 0000000..58dd71f
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/shapes/polygon_base.hpp"
+#include "shapes/polygon.hpp"
+
+using namespace rive;
+
+Core* PolygonBase::clone() const
+{
+       auto cloned = new Polygon();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/shapes/rectangle_base.cpp b/submodule/src/generated/shapes/rectangle_base.cpp
new file mode 100644 (file)
index 0000000..7a7c168
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/shapes/rectangle_base.hpp"
+#include "shapes/rectangle.hpp"
+
+using namespace rive;
+
+Core* RectangleBase::clone() const
+{
+       auto cloned = new Rectangle();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/shapes/shape_base.cpp b/submodule/src/generated/shapes/shape_base.cpp
new file mode 100644 (file)
index 0000000..e6d187b
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/shapes/shape_base.hpp"
+#include "shapes/shape.hpp"
+
+using namespace rive;
+
+Core* ShapeBase::clone() const
+{
+       auto cloned = new Shape();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/shapes/star_base.cpp b/submodule/src/generated/shapes/star_base.cpp
new file mode 100644 (file)
index 0000000..3e0e7f0
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/shapes/star_base.hpp"
+#include "shapes/star.hpp"
+
+using namespace rive;
+
+Core* StarBase::clone() const
+{
+       auto cloned = new Star();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/shapes/straight_vertex_base.cpp b/submodule/src/generated/shapes/straight_vertex_base.cpp
new file mode 100644 (file)
index 0000000..606b80c
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/shapes/straight_vertex_base.hpp"
+#include "shapes/straight_vertex.hpp"
+
+using namespace rive;
+
+Core* StraightVertexBase::clone() const
+{
+       auto cloned = new StraightVertex();
+       cloned->copy(*this);
+       return cloned;
+}
diff --git a/submodule/src/generated/shapes/triangle_base.cpp b/submodule/src/generated/shapes/triangle_base.cpp
new file mode 100644 (file)
index 0000000..d365815
--- /dev/null
@@ -0,0 +1,11 @@
+#include "generated/shapes/triangle_base.hpp"
+#include "shapes/triangle.hpp"
+
+using namespace rive;
+
+Core* TriangleBase::clone() const
+{
+       auto cloned = new Triangle();
+       cloned->copy(*this);
+       return cloned;
+}
index 6f3a7ce..8b3fa60 100644 (file)
@@ -1,6 +1,8 @@
 #include "importers/layer_state_importer.hpp"
 #include "animation/state_transition.hpp"
 #include "animation/layer_state.hpp"
+#include "animation/blend_state.hpp"
+#include "animation/blend_state_transition.hpp"
 
 using namespace rive;
 
@@ -10,4 +12,38 @@ void LayerStateImporter::addTransition(StateTransition* transition)
        m_State->addTransition(transition);
 }
 
-StatusCode LayerStateImporter::resolve() { return StatusCode::Ok; }
\ No newline at end of file
+bool LayerStateImporter::addBlendAnimation(BlendAnimation* animation)
+{
+       if (!m_State->is<BlendState>())
+       {
+               return false;
+       }
+       auto blendState = m_State->as<BlendState>();
+
+       blendState->addAnimation(animation);
+       return true;
+}
+
+StatusCode LayerStateImporter::resolve()
+{
+       if (m_State->is<BlendState>())
+       {
+               auto blendState = m_State->as<BlendState>();
+               for (auto transition : blendState->m_Transitions)
+               {
+                       if (!transition->is<BlendStateTransition>())
+                       {
+                               continue;
+                       }
+
+                       auto blendStateTransition = transition->as<BlendStateTransition>();
+                       auto exitId = blendStateTransition->exitBlendAnimationId();
+                       if (exitId >= 0 && exitId < blendState->m_Animations.size())
+                       {
+                               blendStateTransition->m_ExitBlendAnimation =
+                                   blendState->m_Animations[exitId];
+                       }
+               }
+       }
+       return StatusCode::Ok;
+}
\ No newline at end of file
index 7d25835..9132792 100644 (file)
@@ -26,8 +26,6 @@ RenderPath* TrimPath::effectPath(MetricsPath* source)
                return m_RenderPath;
        }
 
-       
-
        // Source is always a containing (shape) path.
        const std::vector<MetricsPath*>& subPaths = source->paths();
 
@@ -54,7 +52,7 @@ RenderPath* TrimPath::effectPath(MetricsPath* source)
                                endLength -= totalLength;
                        }
 
-                       int i = 0, subPathCount = (int) subPaths.size();
+                       int i = 0, subPathCount = (int)subPaths.size();
                        while (endLength > 0)
                        {
                                auto path = subPaths[i % subPathCount];
index 179c8b7..b3948ce 100644 (file)
@@ -2,6 +2,7 @@
 #include "math/circle_constant.hpp"
 #include "renderer.hpp"
 #include "shapes/cubic_vertex.hpp"
+#include "shapes/cubic_detached_vertex.hpp"
 #include "shapes/path_vertex.hpp"
 #include "shapes/shape.hpp"
 #include "shapes/straight_vertex.hpp"
@@ -305,3 +306,162 @@ void Path::update(ComponentDirt value)
        //      m_Shape->pathChanged();
        // }
 }
+
+#ifdef ENABLE_QUERY_FLAT_VERTICES
+
+class DisplayCubicVertex : public CubicVertex
+{
+public:
+       DisplayCubicVertex(const Vec2D& in,
+                          const Vec2D& out,
+                          const Vec2D& translation)
+
+       {
+               m_InPoint = in;
+               m_OutPoint = out;
+               m_InValid = true;
+               m_OutValid = true;
+               x(translation[0]);
+               y(translation[1]);
+       }
+
+       void computeIn() override {}
+       void computeOut() override {}
+};
+
+FlattenedPath* Path::makeFlat(bool transformToParent)
+{
+       if (m_Vertices.empty())
+       {
+               return nullptr;
+       }
+
+       // Path transform always puts the path into world space.
+       auto transform = pathTransform();
+
+       if (transformToParent && parent()->is<TransformComponent>())
+       {
+               // Put the transform in parent space.
+               auto world = parent()->as<TransformComponent>()->worldTransform();
+               Mat2D inverseWorld;
+               if (!Mat2D::invert(inverseWorld, world))
+               {
+                       Mat2D::identity(inverseWorld);
+               }
+               Mat2D::multiply(transform, inverseWorld, transform);
+       }
+
+       FlattenedPath* flat = new FlattenedPath();
+       auto length = m_Vertices.size();
+       PathVertex* previous = isPathClosed() ? m_Vertices[length - 1] : nullptr;
+       for (size_t i = 0; i < length; i++)
+       {
+               auto vertex = m_Vertices[i];
+
+               switch (vertex->coreType())
+               {
+                       case StraightVertex::typeKey:
+                       {
+                               auto point = *vertex->as<StraightVertex>();
+                               if (point.radius() > 0.0f &&
+                                   (isPathClosed() || (i != 0 && i != length - 1)))
+                               {
+                                       auto next = m_Vertices[(i + 1) % length];
+
+                                       Vec2D prevPoint =
+                                           previous->is<CubicVertex>()
+                                               ? previous->as<CubicVertex>()->renderOut()
+                                               : previous->renderTranslation();
+                                       Vec2D nextPoint = next->is<CubicVertex>()
+                                                             ? next->as<CubicVertex>()->renderIn()
+                                                             : next->renderTranslation();
+
+                                       Vec2D pos = point.renderTranslation();
+
+                                       Vec2D toPrev;
+                                       Vec2D::subtract(toPrev, prevPoint, pos);
+                                       auto toPrevLength = Vec2D::length(toPrev);
+                                       toPrev[0] /= toPrevLength;
+                                       toPrev[1] /= toPrevLength;
+
+                                       Vec2D toNext;
+                                       Vec2D::subtract(toNext, nextPoint, pos);
+                                       auto toNextLength = Vec2D::length(toNext);
+                                       toNext[0] /= toNextLength;
+                                       toNext[1] /= toNextLength;
+
+                                       auto renderRadius = std::min(
+                                           toPrevLength, std::min(toNextLength, point.radius()));
+                                       Vec2D translation;
+                                       Vec2D::scaleAndAdd(translation, pos, toPrev, renderRadius);
+
+                                       Vec2D out;
+                                       Vec2D::scaleAndAdd(
+                                           out, pos, toPrev, icircleConstant * renderRadius);
+                                       auto v1 =
+                                           new DisplayCubicVertex(translation, out, translation);
+                                       flat->addVertex(v1, transform);
+                                       delete v1;
+
+                                       Vec2D::scaleAndAdd(translation, pos, toNext, renderRadius);
+
+                                       Vec2D in;
+                                       Vec2D::scaleAndAdd(
+                                           in, pos, toNext, icircleConstant * renderRadius);
+                                       auto v2 =
+                                           new DisplayCubicVertex(in, translation, translation);
+
+                                       flat->addVertex(v2, transform);
+                                       delete v2;
+
+                                       previous = v2;
+                                       break;
+                               }
+                       }
+                       default:
+                               previous = vertex;
+                               flat->addVertex(previous, transform);
+                               break;
+               }
+       }
+       return flat;
+}
+
+void FlattenedPath::addVertex(PathVertex* vertex, const Mat2D& transform)
+{
+       // To make this easy and relatively clean we just transform the vertices.
+       // Requires the vertex to be passed in as a clone.
+       if (vertex->is<CubicVertex>())
+       {
+               auto cubic = vertex->as<CubicVertex>();
+
+               // Cubics need to be transformed so we create a Display version which
+               // has set in/out values.
+               Vec2D in, out, translation;
+               Vec2D::transform(in, cubic->renderIn(), transform);
+               Vec2D::transform(out, cubic->renderOut(), transform);
+               Vec2D::transform(translation, cubic->renderTranslation(), transform);
+
+               auto displayCubic = new DisplayCubicVertex(in, out, translation);
+               m_Vertices.push_back(displayCubic);
+       }
+       else
+       {
+               auto point = new PathVertex();
+               Vec2D translation;
+               Vec2D::transform(translation, vertex->renderTranslation(), transform);
+               point->x(translation[0]);
+               point->y(translation[1]);
+               m_Vertices.push_back(point);
+       }
+}
+
+FlattenedPath::~FlattenedPath()
+{
+       for (auto vertex : m_Vertices)
+       {
+               delete vertex;
+       }
+}
+
+#endif
diff --git a/submodule/test/assets/blend_test.riv b/submodule/test/assets/blend_test.riv
new file mode 100644 (file)
index 0000000..18f3f37
Binary files /dev/null and b/submodule/test/assets/blend_test.riv differ
diff --git a/submodule/test/assets/multiple_state_machines.riv b/submodule/test/assets/multiple_state_machines.riv
new file mode 100644 (file)
index 0000000..59f8cf3
Binary files /dev/null and b/submodule/test/assets/multiple_state_machines.riv differ
index 2f56c77..004d0ce 100644 (file)
@@ -8,6 +8,10 @@
 #include "animation/state_transition.hpp"
 #include "animation/state_machine_instance.hpp"
 #include "animation/state_machine_input_instance.hpp"
+#include "animation/blend_state_1d.hpp"
+#include "animation/blend_animation_1d.hpp"
+#include "animation/blend_state_direct.hpp"
+#include "animation/blend_state_transition.hpp"
 #include <cstdio>
 
 TEST_CASE("file with state machine be read", "[file]")
@@ -97,4 +101,120 @@ TEST_CASE("file with state machine be read", "[file]")
 
        delete file;
        delete[] bytes;
+}
+
+TEST_CASE("file with blend states loads correctly", "[file]")
+{
+       FILE* fp = fopen("../../test/assets/blend_test.riv", "r");
+       REQUIRE(fp != nullptr);
+
+       fseek(fp, 0, SEEK_END);
+       auto length = ftell(fp);
+       fseek(fp, 0, SEEK_SET);
+       uint8_t* bytes = new uint8_t[length];
+       REQUIRE(fread(bytes, 1, length, fp) == length);
+       auto reader = rive::BinaryReader(bytes, length);
+       rive::File* file = nullptr;
+       auto result = rive::File::import(reader, &file);
+
+       REQUIRE(result == rive::ImportResult::success);
+       REQUIRE(file != nullptr);
+       auto artboard = file->artboard();
+       REQUIRE(artboard != nullptr);
+       REQUIRE(artboard->animationCount() == 4);
+       REQUIRE(artboard->stateMachineCount() == 2);
+
+       auto stateMachine = artboard->stateMachine("blend");
+       REQUIRE(stateMachine != nullptr);
+
+       REQUIRE(stateMachine->layerCount() == 1);
+       auto layer = stateMachine->layer(0);
+       REQUIRE(layer->stateCount() == 5);
+
+       REQUIRE(layer->anyState() != nullptr);
+       REQUIRE(layer->entryState() != nullptr);
+       REQUIRE(layer->exitState() != nullptr);
+
+       REQUIRE(layer->state(1)->is<rive::BlendState1D>());
+       REQUIRE(layer->state(2)->is<rive::BlendState1D>());
+
+       auto blendStateA = layer->state(1)->as<rive::BlendState1D>();
+       auto blendStateB = layer->state(2)->as<rive::BlendState1D>();
+
+       REQUIRE(blendStateA->animationCount() == 3);
+       REQUIRE(blendStateB->animationCount() == 3);
+
+       auto animation = blendStateA->animation(0);
+       REQUIRE(animation->is<rive::BlendAnimation1D>());
+       auto animation1D = animation->as<rive::BlendAnimation1D>();
+       REQUIRE(animation1D->animation() != nullptr);
+       REQUIRE(animation1D->animation()->name() == "horizontal");
+       REQUIRE(animation1D->value() == 0.0f);
+
+       animation = blendStateA->animation(1);
+       REQUIRE(animation->is<rive::BlendAnimation1D>());
+       animation1D = animation->as<rive::BlendAnimation1D>();
+       REQUIRE(animation1D->animation() != nullptr);
+       REQUIRE(animation1D->animation()->name() == "vertical");
+       REQUIRE(animation1D->value() == 100.0f);
+
+       animation = blendStateA->animation(2);
+       REQUIRE(animation->is<rive::BlendAnimation1D>());
+       animation1D = animation->as<rive::BlendAnimation1D>();
+       REQUIRE(animation1D->animation() != nullptr);
+       REQUIRE(animation1D->animation()->name() == "rotate");
+       REQUIRE(animation1D->value() == 0.0f);
+
+       REQUIRE(blendStateA->transitionCount() == 1);
+       REQUIRE(blendStateA->transition(0)->is<rive::BlendStateTransition>());
+       REQUIRE(blendStateA->transition(0)
+                   ->as<rive::BlendStateTransition>()
+                   ->exitBlendAnimation() != nullptr);
+
+       delete file;
+       delete[] bytes;
+}
+
+TEST_CASE("animation state with no animation doesn't crash", "[file]")
+{
+       FILE* fp = fopen("../../test/assets/multiple_state_machines.riv", "r");
+       REQUIRE(fp != nullptr);
+
+       fseek(fp, 0, SEEK_END);
+       auto length = ftell(fp);
+       fseek(fp, 0, SEEK_SET);
+       uint8_t* bytes = new uint8_t[length];
+       REQUIRE(fread(bytes, 1, length, fp) == length);
+       auto reader = rive::BinaryReader(bytes, length);
+       rive::File* file = nullptr;
+       auto result = rive::File::import(reader, &file);
+
+       REQUIRE(result == rive::ImportResult::success);
+       REQUIRE(file != nullptr);
+       auto artboard = file->artboard();
+       REQUIRE(artboard != nullptr);
+       REQUIRE(artboard->animationCount() == 1);
+       REQUIRE(artboard->stateMachineCount() == 4);
+
+       auto stateMachine = artboard->stateMachine("two");
+       REQUIRE(stateMachine != nullptr);
+
+       REQUIRE(stateMachine->layerCount() == 1);
+       auto layer = stateMachine->layer(0);
+       REQUIRE(layer->stateCount() == 4);
+
+       REQUIRE(layer->anyState() != nullptr);
+       REQUIRE(layer->entryState() != nullptr);
+       REQUIRE(layer->exitState() != nullptr);
+
+       REQUIRE(layer->state(3)->is<rive::AnimationState>());
+
+       auto animationState = layer->state(3)->as<rive::AnimationState>();
+       REQUIRE(animationState->animation() == nullptr);
+
+       rive::StateMachineInstance smi(stateMachine);
+       smi.advance(artboard, 0.0f);
+
+       delete file;
+       delete[] bytes;
 }
\ No newline at end of file