More work for new format import.

This commit is contained in:
Luigi Rosso
2021-03-02 20:07:52 -08:00
parent 5db0e3a766
commit 35ea2cabef
12 changed files with 455 additions and 98 deletions

View File

@ -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();
}
}
}

View File

@ -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();
}
}

View File

@ -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() {}
}

View File

@ -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() {}
}

View File

@ -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;
}
}
}

View File

@ -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 {

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}