diff --git a/lib/src/core/core.dart b/lib/src/core/core.dart index d01462b..d8a9d08 100644 --- a/lib/src/core/core.dart +++ b/lib/src/core/core.dart @@ -1,9 +1,15 @@ +import 'dart:collection'; + export 'package:rive/src/animation_list.dart'; export 'package:rive/src/state_machine_components.dart'; export 'package:rive/src/state_transition_conditions.dart'; export 'package:rive/src/container_children.dart'; export 'package:rive/src/runtime_artboard.dart'; export 'package:rive/src/generated/rive_core_context.dart'; +export 'package:rive/src/core/importers/artboard_importer.dart'; +export 'package:rive/src/core/importers/linear_animation_importer.dart'; +export 'package:rive/src/core/importers/keyed_object_importer.dart'; +export 'package:rive/src/core/importers/keyed_property_importer.dart'; typedef PropertyChangeCallback = void Function(dynamic from, dynamic to); typedef BatchAddCallback = void Function(); @@ -18,6 +24,7 @@ abstract class Core<T extends CoreContext> { void onAdded(); void onRemoved() {} void remove() => context?.removeObject(this); + bool import(ImportStack stack) => true; } abstract class CoreContext { @@ -30,3 +37,41 @@ abstract class CoreContext { void markNeedsAdvance(); void dirty(void Function() dirt); } + +// ignore: one_member_abstracts +abstract class ImportStackObject { + void resolve(); +} + +/// Interface to help objects that need to parent themselves to some other +/// object previously imported. +abstract class ImportHelper<T extends Core<CoreContext>> { + bool import(T object, ImportStack stack) => true; +} + +/// Stack to help the RiveFile locate latest ImportStackObject created of a +/// certain type. +class ImportStack { + final _latests = HashMap<int, ImportStackObject>(); + T latest<T extends ImportStackObject>(int coreType) { + var latest = _latests[coreType]; + if (latest is T) { + return latest; + } + return null; + } + + void makeLatest(int coreType, ImportStackObject importObject) { + if (importObject != null) { + _latests[coreType] = importObject; + } else { + _latests.remove(coreType); + } + } + + void resolve() { + for (final object in _latests.values) { + object.resolve(); + } + } +} diff --git a/lib/src/core/importers/artboard_importer.dart b/lib/src/core/importers/artboard_importer.dart new file mode 100644 index 0000000..171960b --- /dev/null +++ b/lib/src/core/importers/artboard_importer.dart @@ -0,0 +1,34 @@ +import 'package:rive/rive.dart'; +import 'package:rive/src/core/core.dart'; +import 'package:rive/src/rive_core/component.dart'; + +class ArtboardImporter extends ImportStackObject { + final RuntimeArtboard artboard; + ArtboardImporter(this.artboard); + + void addComponent(Core<CoreContext> object) => artboard.addObject(object); + + void addAnimation(LinearAnimation animation) { + artboard.addObject(animation); + animation.artboard = artboard; + } + + @override + void resolve() { + for (final object in artboard.objects.skip(1)) { + if (object is Component && object.parentId == null) { + object.parent = artboard; + } + object?.onAddedDirty(); + } + assert(!artboard.children.contains(artboard), + 'artboard should never contain itself as a child'); + for (final object in artboard.objects.toList(growable: false)) { + if (object == null) { + continue; + } + object.onAdded(); + } + artboard.clean(); + } +} diff --git a/lib/src/core/importers/keyed_object_importer.dart b/lib/src/core/importers/keyed_object_importer.dart new file mode 100644 index 0000000..06f97e6 --- /dev/null +++ b/lib/src/core/importers/keyed_object_importer.dart @@ -0,0 +1,17 @@ +import 'package:rive/src/core/core.dart'; +import 'package:rive/src/rive_core/animation/keyed_object.dart'; +import 'package:rive/src/rive_core/animation/keyed_property.dart'; + +class KeyedObjectImporter extends ImportStackObject { + final KeyedObject keyedObject; + + KeyedObjectImporter(this.keyedObject); + + void addKeyedProperty(KeyedProperty property) { + keyedObject.context.addObject(property); + keyedObject.internalAddKeyedProperty(property); + } + + @override + void resolve() {} +} diff --git a/lib/src/core/importers/keyed_property_importer.dart b/lib/src/core/importers/keyed_property_importer.dart new file mode 100644 index 0000000..515343d --- /dev/null +++ b/lib/src/core/importers/keyed_property_importer.dart @@ -0,0 +1,20 @@ +import 'package:rive/src/core/core.dart'; +import 'package:rive/src/rive_core/animation/keyed_property.dart'; +import 'package:rive/src/rive_core/animation/keyframe.dart'; +import 'package:rive/src/rive_core/animation/linear_animation.dart'; + +class KeyedPropertyImporter extends ImportStackObject { + final KeyedProperty keyedProperty; + final LinearAnimation animation; + + KeyedPropertyImporter(this.keyedProperty, this.animation); + + void addKeyFrame(KeyFrame keyFrame) { + keyedProperty.context.addObject(keyFrame); + keyedProperty.internalAddKeyFrame(keyFrame); + keyFrame.computeSeconds(animation); + } + + @override + void resolve() {} +} diff --git a/lib/src/core/importers/linear_animation_importer.dart b/lib/src/core/importers/linear_animation_importer.dart new file mode 100644 index 0000000..fc56910 --- /dev/null +++ b/lib/src/core/importers/linear_animation_importer.dart @@ -0,0 +1,24 @@ +import 'package:rive/rive.dart'; +import 'package:rive/src/core/core.dart'; +import 'package:rive/src/rive_core/animation/keyed_object.dart'; + +class LinearAnimationImporter extends ImportStackObject { + final LinearAnimation linearAnimation; + final keyedObjects = <KeyedObject>[]; + + LinearAnimationImporter(this.linearAnimation); + + void addKeyedObject(KeyedObject object) { + linearAnimation.context.addObject(object); + + keyedObjects.add(object); + linearAnimation.internalAddKeyedObject(object); + } + + @override + void resolve() { + for (final keyedObject in keyedObjects) { + keyedObject?.objectId ??= linearAnimation.artboard.id; + } + } +} diff --git a/lib/src/rive_core/animation/cubic_interpolator.dart b/lib/src/rive_core/animation/cubic_interpolator.dart index 430fb6b..85935a5 100644 --- a/lib/src/rive_core/animation/cubic_interpolator.dart +++ b/lib/src/rive_core/animation/cubic_interpolator.dart @@ -1,5 +1,7 @@ import 'dart:typed_data'; +import 'package:rive/src/core/core.dart'; import 'package:rive/src/rive_core/animation/interpolator.dart'; +import 'package:rive/src/rive_core/artboard.dart'; import 'package:rive/src/generated/animation/cubic_interpolator_base.dart'; const int newtonIterations = 4; @@ -41,8 +43,6 @@ class CubicInterpolator extends CubicInterpolatorBase implements Interpolator { @override void onAddedDirty() {} @override - void onRemoved() {} - @override double transform(double value) => _ease.transform(value); @override void x1Changed(double from, double to) => _updateStoredCubic(); @@ -55,6 +55,16 @@ class CubicInterpolator extends CubicInterpolatorBase implements Interpolator { void _updateStoredCubic() { _ease = _CubicEase.make(x1, y1, x2, y2); } + + @override + bool import(ImportStack stack) { + var artboardHelper = stack.latest<ArtboardImporter>(ArtboardBase.typeKey); + if (artboardHelper == null) { + return false; + } + artboardHelper.addComponent(this); + return super.import(stack); + } } class _Cubic extends _CubicEase { diff --git a/lib/src/rive_core/animation/keyed_object.dart b/lib/src/rive_core/animation/keyed_object.dart index 3f48f3a..b2d0dd4 100644 --- a/lib/src/rive_core/animation/keyed_object.dart +++ b/lib/src/rive_core/animation/keyed_object.dart @@ -2,6 +2,7 @@ import 'dart:collection'; import 'package:rive/src/core/core.dart'; import 'package:rive/src/rive_core/animation/keyed_property.dart'; import 'package:rive/src/generated/animation/keyed_object_base.dart'; +import 'linear_animation.dart'; export 'package:rive/src/generated/animation/keyed_object_base.dart'; class KeyedObject extends KeyedObjectBase<RuntimeArtboard> { @@ -46,4 +47,14 @@ class KeyedObject extends KeyedObjectBase<RuntimeArtboard> { @override void objectIdChanged(int from, int to) {} + @override + bool import(ImportStack stack) { + var animationHelper = + stack.latest<LinearAnimationImporter>(LinearAnimationBase.typeKey); + if (animationHelper == null) { + return false; + } + animationHelper.addKeyedObject(this); + return super.import(stack); + } } diff --git a/lib/src/rive_core/animation/keyed_property.dart b/lib/src/rive_core/animation/keyed_property.dart index 8b9e58b..a8a8060 100644 --- a/lib/src/rive_core/animation/keyed_property.dart +++ b/lib/src/rive_core/animation/keyed_property.dart @@ -1,4 +1,5 @@ import 'package:rive/src/core/core.dart'; +import 'package:rive/src/rive_core/animation/keyed_object.dart'; import 'package:rive/src/rive_core/animation/keyframe.dart'; import 'package:rive/src/generated/animation/keyed_property_base.dart'; export 'package:rive/src/generated/animation/keyed_property_base.dart'; @@ -149,4 +150,13 @@ class KeyedProperty extends KeyedPropertyBase<RuntimeArtboard> @override void propertyKeyChanged(int from, int to) {} + @override + bool import(ImportStack stack) { + var importer = stack.latest<KeyedObjectImporter>(KeyedObjectBase.typeKey); + if (importer == null) { + return false; + } + importer.addKeyedProperty(this); + return super.import(stack); + } } diff --git a/lib/src/rive_core/animation/keyframe.dart b/lib/src/rive_core/animation/keyframe.dart index 84aedd3..391bb9c 100644 --- a/lib/src/rive_core/animation/keyframe.dart +++ b/lib/src/rive_core/animation/keyframe.dart @@ -57,4 +57,15 @@ abstract class KeyFrame extends KeyFrameBase<RuntimeArtboard> _interpolator = value; interpolatorId = value?.id; } + + @override + bool import(ImportStack importStack) { + var keyedPropertyHelper = + importStack.latest<KeyedPropertyImporter>(KeyedPropertyBase.typeKey); + if (keyedPropertyHelper == null) { + return false; + } + keyedPropertyHelper.addKeyFrame(this); + return super.import(importStack); + } } diff --git a/lib/src/rive_core/animation/linear_animation.dart b/lib/src/rive_core/animation/linear_animation.dart index 92c1162..c79e12e 100644 --- a/lib/src/rive_core/animation/linear_animation.dart +++ b/lib/src/rive_core/animation/linear_animation.dart @@ -2,6 +2,7 @@ import 'dart:collection'; import 'package:rive/src/core/core.dart'; import 'package:rive/src/rive_core/animation/keyed_object.dart'; import 'package:rive/src/rive_core/animation/loop.dart'; +import 'package:rive/src/rive_core/artboard.dart'; import 'package:rive/src/generated/animation/linear_animation_base.dart'; export 'package:rive/src/generated/animation/linear_animation_base.dart'; @@ -44,4 +45,13 @@ class LinearAnimation extends LinearAnimationBase { void workEndChanged(int from, int to) {} @override void workStartChanged(int from, int to) {} + @override + bool import(ImportStack stack) { + var artboardImporter = stack.latest<ArtboardImporter>(ArtboardBase.typeKey); + if (artboardImporter == null) { + return false; + } + artboardImporter.addAnimation(this); + return super.import(stack); + } } diff --git a/lib/src/rive_core/component.dart b/lib/src/rive_core/component.dart index a9d4682..9858f94 100644 --- a/lib/src/rive_core/component.dart +++ b/lib/src/rive_core/component.dart @@ -171,4 +171,13 @@ abstract class Component extends ComponentBase<RuntimeArtboard> @override void nameChanged(String from, String to) {} + @override + bool import(ImportStack stack) { + var artboardImporter = stack.latest<ArtboardImporter>(ArtboardBase.typeKey); + if (artboardImporter == null) { + return false; + } + artboardImporter.addComponent(this); + return super.import(stack); + } } diff --git a/lib/src/rive_file.dart b/lib/src/rive_file.dart index cef8b95..3946e93 100644 --- a/lib/src/rive_file.dart +++ b/lib/src/rive_file.dart @@ -1,7 +1,11 @@ import 'dart:collection'; import 'dart:typed_data'; +import 'package:meta/meta.dart'; import 'package:rive/src/core/field_types/core_field_type.dart'; +import 'package:rive/src/generated/animation/keyed_property_base.dart'; +import 'package:rive/src/generated/animation/state_machine_base.dart'; +import 'package:rive/src/rive_core/animation/keyed_property.dart'; import 'package:rive/src/rive_core/component.dart'; import 'package:rive/src/rive_core/runtime/runtime_header.dart'; import 'package:rive/src/rive_core/backboard.dart'; @@ -10,8 +14,6 @@ import 'package:rive/src/utilities/binary_buffer/binary_reader.dart'; import 'package:rive/src/rive_core/runtime/exceptions/rive_format_error_exception.dart'; import 'package:rive/src/rive_core/animation/animation.dart'; import 'package:rive/src/rive_core/animation/keyed_object.dart'; -import 'package:rive/src/rive_core/animation/keyed_property.dart'; -import 'package:rive/src/rive_core/animation/keyframe.dart'; import 'package:rive/src/rive_core/animation/linear_animation.dart'; import 'package:rive/src/rive_core/artboard.dart'; @@ -59,108 +61,168 @@ class RiveFile { propertyToField[key] = indexToField[fieldIndex]; }); - - _backboard = _readRuntimeObject<Backboard>(reader, propertyToField); - if (_backboard == null) { - throw const RiveFormatErrorException( - 'expected first object to be a Backboard'); - } - - int numArtboards = reader.readVarUint(); - for (int i = 0; i < numArtboards; i++) { - var numObjects = reader.readVarUint(); - if (numObjects == 0) { - throw const RiveFormatErrorException( - 'artboards must contain at least one object (themselves)'); + var importStack = ImportStack(); + while (!reader.isEOF) { + var object = _readRuntimeObject(reader, propertyToField); + if (object == null) { + continue; } - var artboard = - _readRuntimeObject(reader, propertyToField, RuntimeArtboard()); - // Kind of weird, but the artboard is the core context at runtime, so we - // want other objects to be able to resolve it. It's always at the 0 - // index. - artboard?.addObject(artboard); - _artboards.add(artboard); - // var objects = List<Core<RiveCoreContext>>(numObjects); - for (int i = 1; i < numObjects; i++) { - Core<CoreContext> object = _readRuntimeObject(reader, propertyToField); - // N.B. we add objects that don't load (null) too as we need to look - // them up by index. - artboard.addObject(object); + var previousOfType = importStack.latest(object.coreType); + if (previousOfType != null) { + previousOfType.resolve(); } - // Animations also need to reference objects, so make sure they get read - // in before the hierarchy resolves (batch add completes). - var numAnimations = reader.readVarUint(); - for (int i = 0; i < numAnimations; i++) { - var animation = _readRuntimeObject<Animation>(reader, propertyToField); - if (animation == null) { - continue; - } - artboard.addObject(animation); - animation.artboard = artboard; - if (animation is LinearAnimation) { - var numKeyedObjects = reader.readVarUint(); - var keyedObjects = List<KeyedObject>.filled(numKeyedObjects, null); - for (int j = 0; j < numKeyedObjects; j++) { - var keyedObject = - _readRuntimeObject<KeyedObject>(reader, propertyToField); - if (keyedObject == null) { - continue; - } - keyedObjects[j] = keyedObject; - artboard.addObject(keyedObject); - - animation.internalAddKeyedObject(keyedObject); - - var numKeyedProperties = reader.readVarUint(); - for (int k = 0; k < numKeyedProperties; k++) { - var keyedProperty = - _readRuntimeObject<KeyedProperty>(reader, propertyToField); - if (keyedProperty == null) { - continue; - } - artboard.addObject(keyedProperty); - keyedObject.internalAddKeyedProperty(keyedProperty); - - var numKeyframes = reader.readVarUint(); - for (int l = 0; l < numKeyframes; l++) { - var keyframe = - _readRuntimeObject<KeyFrame>(reader, propertyToField); - if (keyframe == null) { - continue; - } - artboard.addObject(keyframe); - keyedProperty.internalAddKeyFrame(keyframe); - keyframe.computeSeconds(animation); - } + ImportStackObject stackObject; + switch (object.coreType) { + case ArtboardBase.typeKey: + stackObject = ArtboardImporter(object as RuntimeArtboard); + break; + case LinearAnimationBase.typeKey: + stackObject = LinearAnimationImporter(object as LinearAnimation); + // helper = _AnimationImportHelper(); + break; + case KeyedObjectBase.typeKey: + stackObject = KeyedObjectImporter(object as KeyedObject); + break; + case KeyedPropertyBase.typeKey: + { + // KeyedProperty importer requires a linear animation importer, so + // make sure there's one on the stack. + var linearAnimationImporter = importStack + .latest<LinearAnimationImporter>(LinearAnimationBase.typeKey); + if (linearAnimationImporter != null) { + stackObject = KeyedPropertyImporter(object as KeyedProperty, + linearAnimationImporter.linearAnimation); } + break; } - - for (final keyedObject in keyedObjects) { - keyedObject?.objectId ??= artboard.id; + case StateMachineBase.typeKey: + // stackObject = _LinearAnimationStackObject(object as LinearAnimation); + // helper = _AnimationImportHelper(); + break; + default: + if (object is Component) { + // helper = _ArtboardObjectImportHelper(); } - } + break; } - // Any component objects with no id map to the artboard. Skip first item - // as it's the artboard itself. - for (final object in artboard.objects.skip(1)) { - if (object is Component && object.parentId == null) { - object.parent = artboard; - } - object?.onAddedDirty(); - } + importStack.makeLatest(object.coreType, stackObject); - assert(!artboard.children.contains(artboard), - 'artboard should never contain itself as a child'); - for (final object in artboard.objects.toList(growable: false)) { - if (object == null) { - continue; + if (object?.import(importStack) ?? true) { + switch (object.coreType) { + case ArtboardBase.typeKey: + _artboards.add(object as Artboard); + break; + case BackboardBase.typeKey: + assert(_backboard == null, 'expect only one backboard in the file'); + _backboard = object as Backboard; + break; } - object.onAdded(); } - artboard.clean(); } + importStack.resolve(); + + // _backboard = _readRuntimeObject<Backboard>(reader, propertyToField); + // if (_backboard == null) { + // throw const RiveFormatErrorException( + // 'expected first object to be a Backboard'); + // } + + // int numArtboards = reader.readVarUint(); + // for (int i = 0; i < numArtboards; i++) { + // var numObjects = reader.readVarUint(); + // if (numObjects == 0) { + // throw const RiveFormatErrorException( + // 'artboards must contain at least one object (themselves)'); + // } + // var artboard = + // _readRuntimeObject(reader, propertyToField, RuntimeArtboard()); + // // Kind of weird, but the artboard is the core context at runtime, so we + // // want other objects to be able to resolve it. It's always at the 0 + // // index. + // artboard?.addObject(artboard); + // _artboards.add(artboard); + // // var objects = List<Core<RiveCoreContext>>(numObjects); + // for (int i = 1; i < numObjects; i++) { + // Core<CoreContext> object = _readRuntimeObject(reader, propertyToField); + // // N.B. we add objects that don't load (null) too as we need to look + // // them up by index. + // artboard.addObject(object); + // } + + // // Animations also need to reference objects, so make sure they get read + // // in before the hierarchy resolves (batch add completes). + // var numAnimations = reader.readVarUint(); + // for (int i = 0; i < numAnimations; i++) { + // var animation = _readRuntimeObject<Animation>(reader, propertyToField); + // if (animation == null) { + // continue; + // } + // artboard.addObject(animation); + // animation.artboard = artboard; + // if (animation is LinearAnimation) { + // var numKeyedObjects = reader.readVarUint(); + // var keyedObjects = List<KeyedObject>.filled(numKeyedObjects, null); + // for (int j = 0; j < numKeyedObjects; j++) { + // var keyedObject = + // _readRuntimeObject<KeyedObject>(reader, propertyToField); + // if (keyedObject == null) { + // continue; + // } + // keyedObjects[j] = keyedObject; + // artboard.addObject(keyedObject); + + // animation.internalAddKeyedObject(keyedObject); + + // var numKeyedProperties = reader.readVarUint(); + // for (int k = 0; k < numKeyedProperties; k++) { + // var keyedProperty = + // _readRuntimeObject<KeyedProperty>(reader, propertyToField); + // if (keyedProperty == null) { + // continue; + // } + // artboard.addObject(keyedProperty); + // keyedObject.internalAddKeyedProperty(keyedProperty); + + // var numKeyframes = reader.readVarUint(); + // for (int l = 0; l < numKeyframes; l++) { + // var keyframe = + // _readRuntimeObject<KeyFrame>(reader, propertyToField); + // if (keyframe == null) { + // continue; + // } + // artboard.addObject(keyframe); + // keyedProperty.internalAddKeyFrame(keyframe); + // keyframe.computeSeconds(animation); + // } + // } + // } + + // for (final keyedObject in keyedObjects) { + // keyedObject?.objectId ??= artboard.id; + // } + // } + // } + + // // Any component objects with no id map to the artboard. Skip first item + // // as it's the artboard itself. + // for (final object in artboard.objects.skip(1)) { + // if (object is Component && object.parentId == null) { + // object.parent = artboard; + // } + // object?.onAddedDirty(); + // } + + // assert(!artboard.children.contains(artboard), + // 'artboard should never contain itself as a child'); + // for (final object in artboard.objects.toList(growable: false)) { + // if (object == null) { + // continue; + // } + // object.onAdded(); + // } + // artboard.clean(); return true; } @@ -178,11 +240,16 @@ void _skipProperty(BinaryReader reader, int propertyKey, field.deserialize(reader); } -T _readRuntimeObject<T extends Core<CoreContext>>( - BinaryReader reader, HashMap<int, CoreFieldType> propertyToField, - [T instance]) { +Core<CoreContext> _readRuntimeObject( + BinaryReader reader, HashMap<int, CoreFieldType> propertyToField) { int coreObjectKey = reader.readVarUint(); + Core<CoreContext> instance; + switch (coreObjectKey) { + case ArtboardBase.typeKey: + instance = RuntimeArtboard(); + break; + } var object = instance ?? RiveCoreContext.makeCoreInstance(coreObjectKey); while (true) { @@ -200,5 +267,94 @@ T _readRuntimeObject<T extends Core<CoreContext>>( object, propertyKey, fieldType.deserialize(reader)); } } - return object as T; + return object; +} + +class _ArtboardImportStackObject extends ImportStackObject { + final RuntimeArtboard artboard; + _ArtboardImportStackObject(this.artboard); + + void addObject(Core<CoreContext> object) => artboard.addObject(object); + + @override + void resolve() { + for (final object in artboard.objects.skip(1)) { + if (object is Component && object.parentId == null) { + object.parent = artboard; + } + object?.onAddedDirty(); + } + assert(!artboard.children.contains(artboard), + 'artboard should never contain itself as a child'); + for (final object in artboard.objects.toList(growable: false)) { + if (object == null) { + continue; + } + object.onAdded(); + } + artboard.clean(); + } +} + +class _ArtboardObjectImportHelper<T extends Core<CoreContext>> + extends ImportHelper<T> { + @override + bool import(T object, ImportStack stack) { + _ArtboardImportStackObject artboardStackObject = + stack.latest(ArtboardBase.typeKey); + if (artboardStackObject == null) { + return false; + } + artboardStackObject.addObject(object); + withArtboard(object, artboardStackObject); + return true; + } + + @protected + void withArtboard(T object, _ArtboardImportStackObject artboardStackObject) {} +} + +class _AnimationImportHelper extends _ArtboardObjectImportHelper<Animation> { + @override + void withArtboard( + Animation object, _ArtboardImportStackObject artboardStackObject) { + object.artboard = artboardStackObject.artboard; + } +} + +class _LinearAnimationStackObject extends ImportStackObject { + final LinearAnimation linearAnimation; + final keyedObjects = <KeyedObject>[]; + + _LinearAnimationStackObject(this.linearAnimation); + + void addKeyedObject(KeyedObject object) { + keyedObjects.add(object); + linearAnimation.internalAddKeyedObject(object); + } + + @override + void resolve() { + for (final keyedObject in keyedObjects) { + keyedObject?.objectId ??= linearAnimation.artboard.id; + } + } +} + +class _KeyedObjectImportHelper + extends _ArtboardObjectImportHelper<KeyedObject> { + @override + bool import(KeyedObject object, ImportStack stack) { + if (!super.import(object, stack)) { + return false; + } + _LinearAnimationStackObject animationStackObject = + stack.latest(LinearAnimationBase.typeKey); + if (animationStackObject == null) { + return false; + } + animationStackObject.addKeyedObject(object); + + return true; + } }