mirror of
https://github.com/rive-app/rive-flutter.git
synced 2025-07-01 12:22:49 +08:00
Blend states!
This commit is contained in:
BIN
example/assets/liquid_download.riv
Normal file
BIN
example/assets/liquid_download.riv
Normal file
Binary file not shown.
94
example/lib/liquid_download.dart
Normal file
94
example/lib/liquid_download.dart
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:rive/rive.dart';
|
||||||
|
|
||||||
|
/// An example showing how to drive a StateMachine via a trigger and number
|
||||||
|
/// input.
|
||||||
|
class LiquidDownload extends StatefulWidget {
|
||||||
|
const LiquidDownload({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_LiquidDownloadState createState() => _LiquidDownloadState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LiquidDownloadState extends State<LiquidDownload> {
|
||||||
|
/// Tracks if the animation is playing by whether controller is running.
|
||||||
|
bool get isPlaying => _controller?.isActive ?? false;
|
||||||
|
|
||||||
|
Artboard? _riveArtboard;
|
||||||
|
StateMachineController? _controller;
|
||||||
|
SMIInput<bool>? _start;
|
||||||
|
SMIInput<double>? _progress;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
// Load the animation file from the bundle, note that you could also
|
||||||
|
// download this. The RiveFile just expects a list of bytes.
|
||||||
|
rootBundle.load('assets/liquid_download.riv').then(
|
||||||
|
(data) async {
|
||||||
|
// Load the RiveFile from the binary data.
|
||||||
|
final file = RiveFile.import(data);
|
||||||
|
|
||||||
|
// The artboard is the root of the animation and gets drawn in the
|
||||||
|
// Rive widget.
|
||||||
|
final artboard = file.mainArtboard;
|
||||||
|
var controller =
|
||||||
|
StateMachineController.fromArtboard(artboard, 'Download');
|
||||||
|
if (controller != null) {
|
||||||
|
artboard.addController(controller);
|
||||||
|
_start = controller.findInput('Download');
|
||||||
|
_progress = controller.findInput('Progress');
|
||||||
|
}
|
||||||
|
setState(() => _riveArtboard = artboard);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: Colors.grey,
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Liquid Download'),
|
||||||
|
),
|
||||||
|
body: Center(
|
||||||
|
child: _riveArtboard == null
|
||||||
|
? const SizedBox()
|
||||||
|
: GestureDetector(
|
||||||
|
onTapDown: (_) => _start?.value = true,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
const Text(
|
||||||
|
'Press to activate, slide for progress...',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Slider(
|
||||||
|
value: _progress!.value,
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
label: _progress!.value.round().toString(),
|
||||||
|
onChanged: (double value) {
|
||||||
|
setState(() {
|
||||||
|
_progress!.value = value;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Expanded(
|
||||||
|
child: Rive(
|
||||||
|
artboard: _riveArtboard!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:rive_example/example_animation.dart';
|
import 'package:rive_example/example_animation.dart';
|
||||||
import 'package:rive_example/example_state_machine.dart';
|
import 'package:rive_example/example_state_machine.dart';
|
||||||
|
import 'package:rive_example/liquid_download.dart';
|
||||||
import 'package:rive_example/little_machine.dart';
|
import 'package:rive_example/little_machine.dart';
|
||||||
import 'package:rive_example/state_machine_skills.dart';
|
import 'package:rive_example/state_machine_skills.dart';
|
||||||
|
|
||||||
@ -73,6 +74,20 @@ class Home extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 10,
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
child: const Text('Liquid Download'),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute<void>(
|
||||||
|
builder: (context) => const LiquidDownload(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
20
lib/src/blend_animations.dart
Normal file
20
lib/src/blend_animations.dart
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import 'dart:collection';
|
||||||
|
|
||||||
|
import 'package:rive/src/rive_core/animation/blend_animation.dart';
|
||||||
|
|
||||||
|
class BlendAnimations<T extends BlendAnimation> extends ListBase<T> {
|
||||||
|
final List<T?> _values = [];
|
||||||
|
List<T> get values => _values.cast<T>();
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get length => _values.length;
|
||||||
|
|
||||||
|
@override
|
||||||
|
set length(int value) => _values.length = value;
|
||||||
|
|
||||||
|
@override
|
||||||
|
T operator [](int index) => _values[index]!;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void operator []=(int index, T value) => _values[index] = value;
|
||||||
|
}
|
@ -3,6 +3,7 @@ import 'dart:collection';
|
|||||||
import 'package:rive/src/rive_core/runtime/exceptions/rive_format_error_exception.dart';
|
import 'package:rive/src/rive_core/runtime/exceptions/rive_format_error_exception.dart';
|
||||||
export 'package:rive/src/animation_list.dart';
|
export 'package:rive/src/animation_list.dart';
|
||||||
export 'package:rive/src/state_machine_components.dart';
|
export 'package:rive/src/state_machine_components.dart';
|
||||||
|
export 'package:rive/src/blend_animations.dart';
|
||||||
export 'package:rive/src/state_transition_conditions.dart';
|
export 'package:rive/src/state_transition_conditions.dart';
|
||||||
export 'package:rive/src/state_transitions.dart';
|
export 'package:rive/src/state_transitions.dart';
|
||||||
export 'package:rive/src/container_children.dart';
|
export 'package:rive/src/container_children.dart';
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
import 'package:rive/src/core/importers/artboard_import_stack_object.dart';
|
import 'package:rive/src/core/importers/artboard_import_stack_object.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/blend_animation.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/blend_state.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/blend_state_direct.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/blend_state_transition.dart';
|
||||||
import 'package:rive/src/rive_core/animation/layer_state.dart';
|
import 'package:rive/src/rive_core/animation/layer_state.dart';
|
||||||
import 'package:rive/src/rive_core/animation/state_transition.dart';
|
import 'package:rive/src/rive_core/animation/state_transition.dart';
|
||||||
|
|
||||||
@ -10,4 +14,24 @@ class LayerStateImporter extends ArtboardImportStackObject {
|
|||||||
state.context.addObject(transition);
|
state.context.addObject(transition);
|
||||||
state.internalAddTransition(transition);
|
state.internalAddTransition(transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool addBlendAnimation(BlendAnimation blendAnimation) {
|
||||||
|
if (state is BlendStateDirect) {
|
||||||
|
var blendState = state as BlendStateDirect;
|
||||||
|
for (final transition
|
||||||
|
in state.transitions.whereType<BlendStateTransition>()) {
|
||||||
|
if (transition.exitBlendAnimationId >= 0 &&
|
||||||
|
transition.exitBlendAnimationId < blendState.animations.length) {
|
||||||
|
transition.exitBlendAnimation =
|
||||||
|
blendState.animations[transition.exitBlendAnimationId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (state is BlendState) {
|
||||||
|
(state as BlendState).internalAddAnimation(blendAnimation);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
import 'package:rive/rive.dart';
|
import 'package:rive/rive.dart';
|
||||||
import 'package:rive/src/core/core.dart';
|
import 'package:rive/src/core/core.dart';
|
||||||
import 'package:rive/src/rive_core/animation/animation_state.dart';
|
|
||||||
import 'package:rive/src/rive_core/animation/layer_state.dart';
|
import 'package:rive/src/rive_core/animation/layer_state.dart';
|
||||||
import 'package:rive/src/rive_core/animation/state_machine_layer.dart';
|
import 'package:rive/src/rive_core/animation/state_machine_layer.dart';
|
||||||
|
|
||||||
class StateMachineLayerImporter extends ImportStackObject {
|
class StateMachineLayerImporter extends ImportStackObject {
|
||||||
final StateMachineLayer layer;
|
final StateMachineLayer layer;
|
||||||
final ArtboardImporter artboardImporter;
|
StateMachineLayerImporter(this.layer);
|
||||||
StateMachineLayerImporter(this.layer, this.artboardImporter);
|
|
||||||
|
|
||||||
final List<LayerState> importedStates = [];
|
final List<LayerState> importedStates = [];
|
||||||
|
|
||||||
@ -27,12 +25,6 @@ class StateMachineLayerImporter extends ImportStackObject {
|
|||||||
assert(!_resolved);
|
assert(!_resolved);
|
||||||
_resolved = true;
|
_resolved = true;
|
||||||
for (final state in importedStates) {
|
for (final state in importedStates) {
|
||||||
if (state is AnimationState) {
|
|
||||||
int artboardAnimationIndex = state.animationId;
|
|
||||||
assert(artboardAnimationIndex >= 0 &&
|
|
||||||
artboardAnimationIndex < artboardImporter.animations.length);
|
|
||||||
state.animation = artboardImporter.animations[artboardAnimationIndex];
|
|
||||||
}
|
|
||||||
for (final transition in state.transitions) {
|
for (final transition in state.transitions) {
|
||||||
// At import time the stateToId is an index relative to the entire layer
|
// At import time the stateToId is an index relative to the entire layer
|
||||||
// (which state in this layer). We can use that to find the matching
|
// (which state in this layer). We can use that to find the matching
|
||||||
|
37
lib/src/generated/animation/blend_animation_1d_base.dart
Normal file
37
lib/src/generated/animation/blend_animation_1d_base.dart
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/// Core automatically generated
|
||||||
|
/// lib/src/generated/animation/blend_animation_1d_base.dart.
|
||||||
|
/// Do not modify manually.
|
||||||
|
|
||||||
|
import 'package:rive/src/generated/animation/blend_animation_base.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/blend_animation.dart';
|
||||||
|
|
||||||
|
abstract class BlendAnimation1DBase extends BlendAnimation {
|
||||||
|
static const int typeKey = 75;
|
||||||
|
@override
|
||||||
|
int get coreType => BlendAnimation1DBase.typeKey;
|
||||||
|
@override
|
||||||
|
Set<int> get coreTypes =>
|
||||||
|
{BlendAnimation1DBase.typeKey, BlendAnimationBase.typeKey};
|
||||||
|
|
||||||
|
/// --------------------------------------------------------------------------
|
||||||
|
/// Value field with key 166.
|
||||||
|
static const double valueInitialValue = 0;
|
||||||
|
double _value = valueInitialValue;
|
||||||
|
static const int valuePropertyKey = 166;
|
||||||
|
double get value => _value;
|
||||||
|
|
||||||
|
/// Change the [_value] field value.
|
||||||
|
/// [valueChanged] will be invoked only if the field's value has changed.
|
||||||
|
set value(double value) {
|
||||||
|
if (_value == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
double from = _value;
|
||||||
|
_value = value;
|
||||||
|
if (hasValidated) {
|
||||||
|
valueChanged(from, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void valueChanged(double from, double to);
|
||||||
|
}
|
38
lib/src/generated/animation/blend_animation_base.dart
Normal file
38
lib/src/generated/animation/blend_animation_base.dart
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/// Core automatically generated
|
||||||
|
/// lib/src/generated/animation/blend_animation_base.dart.
|
||||||
|
/// Do not modify manually.
|
||||||
|
|
||||||
|
import 'package:rive/src/core/core.dart';
|
||||||
|
|
||||||
|
abstract class BlendAnimationBase<T extends CoreContext> extends Core<T> {
|
||||||
|
static const int typeKey = 74;
|
||||||
|
@override
|
||||||
|
int get coreType => BlendAnimationBase.typeKey;
|
||||||
|
@override
|
||||||
|
Set<int> get coreTypes => {BlendAnimationBase.typeKey};
|
||||||
|
|
||||||
|
/// --------------------------------------------------------------------------
|
||||||
|
/// AnimationId field with key 165.
|
||||||
|
static const int animationIdInitialValue = -1;
|
||||||
|
int _animationId = animationIdInitialValue;
|
||||||
|
static const int animationIdPropertyKey = 165;
|
||||||
|
|
||||||
|
/// Id of the animation this BlendAnimation references.
|
||||||
|
int get animationId => _animationId;
|
||||||
|
|
||||||
|
/// Change the [_animationId] field value.
|
||||||
|
/// [animationIdChanged] will be invoked only if the field's value has
|
||||||
|
/// changed.
|
||||||
|
set animationId(int value) {
|
||||||
|
if (_animationId == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int from = _animationId;
|
||||||
|
_animationId = value;
|
||||||
|
if (hasValidated) {
|
||||||
|
animationIdChanged(from, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void animationIdChanged(int from, int to);
|
||||||
|
}
|
39
lib/src/generated/animation/blend_animation_direct_base.dart
Normal file
39
lib/src/generated/animation/blend_animation_direct_base.dart
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/// Core automatically generated
|
||||||
|
/// lib/src/generated/animation/blend_animation_direct_base.dart.
|
||||||
|
/// Do not modify manually.
|
||||||
|
|
||||||
|
import 'package:rive/src/generated/animation/blend_animation_base.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/blend_animation.dart';
|
||||||
|
|
||||||
|
abstract class BlendAnimationDirectBase extends BlendAnimation {
|
||||||
|
static const int typeKey = 77;
|
||||||
|
@override
|
||||||
|
int get coreType => BlendAnimationDirectBase.typeKey;
|
||||||
|
@override
|
||||||
|
Set<int> get coreTypes =>
|
||||||
|
{BlendAnimationDirectBase.typeKey, BlendAnimationBase.typeKey};
|
||||||
|
|
||||||
|
/// --------------------------------------------------------------------------
|
||||||
|
/// InputId field with key 168.
|
||||||
|
static const int inputIdInitialValue = -1;
|
||||||
|
int _inputId = inputIdInitialValue;
|
||||||
|
static const int inputIdPropertyKey = 168;
|
||||||
|
|
||||||
|
/// Id of the input that drives the direct mix value for this animation.
|
||||||
|
int get inputId => _inputId;
|
||||||
|
|
||||||
|
/// Change the [_inputId] field value.
|
||||||
|
/// [inputIdChanged] will be invoked only if the field's value has changed.
|
||||||
|
set inputId(int value) {
|
||||||
|
if (_inputId == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int from = _inputId;
|
||||||
|
_inputId = value;
|
||||||
|
if (hasValidated) {
|
||||||
|
inputIdChanged(from, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void inputIdChanged(int from, int to);
|
||||||
|
}
|
46
lib/src/generated/animation/blend_state_1d_base.dart
Normal file
46
lib/src/generated/animation/blend_state_1d_base.dart
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/// Core automatically generated
|
||||||
|
/// lib/src/generated/animation/blend_state_1d_base.dart.
|
||||||
|
/// Do not modify manually.
|
||||||
|
|
||||||
|
import 'package:rive/src/generated/animation/blend_state_base.dart';
|
||||||
|
import 'package:rive/src/generated/animation/layer_state_base.dart';
|
||||||
|
import 'package:rive/src/generated/animation/state_machine_layer_component_base.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/blend_animation_1d.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/blend_state.dart';
|
||||||
|
|
||||||
|
abstract class BlendState1DBase extends BlendState<BlendAnimation1D> {
|
||||||
|
static const int typeKey = 76;
|
||||||
|
@override
|
||||||
|
int get coreType => BlendState1DBase.typeKey;
|
||||||
|
@override
|
||||||
|
Set<int> get coreTypes => {
|
||||||
|
BlendState1DBase.typeKey,
|
||||||
|
BlendStateBase.typeKey,
|
||||||
|
LayerStateBase.typeKey,
|
||||||
|
StateMachineLayerComponentBase.typeKey
|
||||||
|
};
|
||||||
|
|
||||||
|
/// --------------------------------------------------------------------------
|
||||||
|
/// InputId field with key 167.
|
||||||
|
static const int inputIdInitialValue = -1;
|
||||||
|
int _inputId = inputIdInitialValue;
|
||||||
|
static const int inputIdPropertyKey = 167;
|
||||||
|
|
||||||
|
/// Id of the input that drives the mix value for this blend state.
|
||||||
|
int get inputId => _inputId;
|
||||||
|
|
||||||
|
/// Change the [_inputId] field value.
|
||||||
|
/// [inputIdChanged] will be invoked only if the field's value has changed.
|
||||||
|
set inputId(int value) {
|
||||||
|
if (_inputId == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int from = _inputId;
|
||||||
|
_inputId = value;
|
||||||
|
if (hasValidated) {
|
||||||
|
inputIdChanged(from, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void inputIdChanged(int from, int to);
|
||||||
|
}
|
19
lib/src/generated/animation/blend_state_base.dart
Normal file
19
lib/src/generated/animation/blend_state_base.dart
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/// Core automatically generated
|
||||||
|
/// lib/src/generated/animation/blend_state_base.dart.
|
||||||
|
/// Do not modify manually.
|
||||||
|
|
||||||
|
import 'package:rive/src/generated/animation/layer_state_base.dart';
|
||||||
|
import 'package:rive/src/generated/animation/state_machine_layer_component_base.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/layer_state.dart';
|
||||||
|
|
||||||
|
abstract class BlendStateBase extends LayerState {
|
||||||
|
static const int typeKey = 72;
|
||||||
|
@override
|
||||||
|
int get coreType => BlendStateBase.typeKey;
|
||||||
|
@override
|
||||||
|
Set<int> get coreTypes => {
|
||||||
|
BlendStateBase.typeKey,
|
||||||
|
LayerStateBase.typeKey,
|
||||||
|
StateMachineLayerComponentBase.typeKey
|
||||||
|
};
|
||||||
|
}
|
22
lib/src/generated/animation/blend_state_direct_base.dart
Normal file
22
lib/src/generated/animation/blend_state_direct_base.dart
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/// Core automatically generated
|
||||||
|
/// lib/src/generated/animation/blend_state_direct_base.dart.
|
||||||
|
/// Do not modify manually.
|
||||||
|
|
||||||
|
import 'package:rive/src/generated/animation/blend_state_base.dart';
|
||||||
|
import 'package:rive/src/generated/animation/layer_state_base.dart';
|
||||||
|
import 'package:rive/src/generated/animation/state_machine_layer_component_base.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/blend_animation_direct.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/blend_state.dart';
|
||||||
|
|
||||||
|
abstract class BlendStateDirectBase extends BlendState<BlendAnimationDirect> {
|
||||||
|
static const int typeKey = 73;
|
||||||
|
@override
|
||||||
|
int get coreType => BlendStateDirectBase.typeKey;
|
||||||
|
@override
|
||||||
|
Set<int> get coreTypes => {
|
||||||
|
BlendStateDirectBase.typeKey,
|
||||||
|
BlendStateBase.typeKey,
|
||||||
|
LayerStateBase.typeKey,
|
||||||
|
StateMachineLayerComponentBase.typeKey
|
||||||
|
};
|
||||||
|
}
|
44
lib/src/generated/animation/blend_state_transition_base.dart
Normal file
44
lib/src/generated/animation/blend_state_transition_base.dart
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/// Core automatically generated
|
||||||
|
/// lib/src/generated/animation/blend_state_transition_base.dart.
|
||||||
|
/// Do not modify manually.
|
||||||
|
|
||||||
|
import 'package:rive/src/generated/animation/state_machine_layer_component_base.dart';
|
||||||
|
import 'package:rive/src/generated/animation/state_transition_base.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/state_transition.dart';
|
||||||
|
|
||||||
|
abstract class BlendStateTransitionBase extends StateTransition {
|
||||||
|
static const int typeKey = 78;
|
||||||
|
@override
|
||||||
|
int get coreType => BlendStateTransitionBase.typeKey;
|
||||||
|
@override
|
||||||
|
Set<int> get coreTypes => {
|
||||||
|
BlendStateTransitionBase.typeKey,
|
||||||
|
StateTransitionBase.typeKey,
|
||||||
|
StateMachineLayerComponentBase.typeKey
|
||||||
|
};
|
||||||
|
|
||||||
|
/// --------------------------------------------------------------------------
|
||||||
|
/// ExitBlendAnimationId field with key 171.
|
||||||
|
static const int exitBlendAnimationIdInitialValue = -1;
|
||||||
|
int _exitBlendAnimationId = exitBlendAnimationIdInitialValue;
|
||||||
|
static const int exitBlendAnimationIdPropertyKey = 171;
|
||||||
|
|
||||||
|
/// Id of the state the blend state animation used for exit time calculation.
|
||||||
|
int get exitBlendAnimationId => _exitBlendAnimationId;
|
||||||
|
|
||||||
|
/// Change the [_exitBlendAnimationId] field value.
|
||||||
|
/// [exitBlendAnimationIdChanged] will be invoked only if the field's value
|
||||||
|
/// has changed.
|
||||||
|
set exitBlendAnimationId(int value) {
|
||||||
|
if (_exitBlendAnimationId == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int from = _exitBlendAnimationId;
|
||||||
|
_exitBlendAnimationId = value;
|
||||||
|
if (hasValidated) {
|
||||||
|
exitBlendAnimationIdChanged(from, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void exitBlendAnimationIdChanged(int from, int to);
|
||||||
|
}
|
@ -8,6 +8,12 @@ import 'package:rive/src/core/field_types/core_uint_type.dart';
|
|||||||
import 'package:rive/src/generated/animation/animation_base.dart';
|
import 'package:rive/src/generated/animation/animation_base.dart';
|
||||||
import 'package:rive/src/generated/animation/animation_state_base.dart';
|
import 'package:rive/src/generated/animation/animation_state_base.dart';
|
||||||
import 'package:rive/src/generated/animation/any_state_base.dart';
|
import 'package:rive/src/generated/animation/any_state_base.dart';
|
||||||
|
import 'package:rive/src/generated/animation/blend_animation_1d_base.dart';
|
||||||
|
import 'package:rive/src/generated/animation/blend_animation_base.dart';
|
||||||
|
import 'package:rive/src/generated/animation/blend_animation_direct_base.dart';
|
||||||
|
import 'package:rive/src/generated/animation/blend_state_1d_base.dart';
|
||||||
|
import 'package:rive/src/generated/animation/blend_state_direct_base.dart';
|
||||||
|
import 'package:rive/src/generated/animation/blend_state_transition_base.dart';
|
||||||
import 'package:rive/src/generated/animation/cubic_interpolator_base.dart';
|
import 'package:rive/src/generated/animation/cubic_interpolator_base.dart';
|
||||||
import 'package:rive/src/generated/animation/entry_state_base.dart';
|
import 'package:rive/src/generated/animation/entry_state_base.dart';
|
||||||
import 'package:rive/src/generated/animation/exit_state_base.dart';
|
import 'package:rive/src/generated/animation/exit_state_base.dart';
|
||||||
@ -70,6 +76,11 @@ import 'package:rive/src/generated/transform_component_base.dart';
|
|||||||
import 'package:rive/src/rive_core/animation/animation.dart';
|
import 'package:rive/src/rive_core/animation/animation.dart';
|
||||||
import 'package:rive/src/rive_core/animation/animation_state.dart';
|
import 'package:rive/src/rive_core/animation/animation_state.dart';
|
||||||
import 'package:rive/src/rive_core/animation/any_state.dart';
|
import 'package:rive/src/rive_core/animation/any_state.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/blend_animation_1d.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/blend_animation_direct.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/blend_state_1d.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/blend_state_direct.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/blend_state_transition.dart';
|
||||||
import 'package:rive/src/rive_core/animation/cubic_interpolator.dart';
|
import 'package:rive/src/rive_core/animation/cubic_interpolator.dart';
|
||||||
import 'package:rive/src/rive_core/animation/entry_state.dart';
|
import 'package:rive/src/rive_core/animation/entry_state.dart';
|
||||||
import 'package:rive/src/rive_core/animation/exit_state.dart';
|
import 'package:rive/src/rive_core/animation/exit_state.dart';
|
||||||
@ -129,6 +140,8 @@ class RiveCoreContext {
|
|||||||
return AnimationState();
|
return AnimationState();
|
||||||
case KeyedObjectBase.typeKey:
|
case KeyedObjectBase.typeKey:
|
||||||
return KeyedObject();
|
return KeyedObject();
|
||||||
|
case BlendAnimationDirectBase.typeKey:
|
||||||
|
return BlendAnimationDirect();
|
||||||
case StateMachineNumberBase.typeKey:
|
case StateMachineNumberBase.typeKey:
|
||||||
return StateMachineNumber();
|
return StateMachineNumber();
|
||||||
case TransitionTriggerConditionBase.typeKey:
|
case TransitionTriggerConditionBase.typeKey:
|
||||||
@ -161,10 +174,18 @@ class RiveCoreContext {
|
|||||||
return LinearAnimation();
|
return LinearAnimation();
|
||||||
case StateMachineTriggerBase.typeKey:
|
case StateMachineTriggerBase.typeKey:
|
||||||
return StateMachineTrigger();
|
return StateMachineTrigger();
|
||||||
|
case BlendStateDirectBase.typeKey:
|
||||||
|
return BlendStateDirect();
|
||||||
case ExitStateBase.typeKey:
|
case ExitStateBase.typeKey:
|
||||||
return ExitState();
|
return ExitState();
|
||||||
|
case BlendAnimation1DBase.typeKey:
|
||||||
|
return BlendAnimation1D();
|
||||||
|
case BlendState1DBase.typeKey:
|
||||||
|
return BlendState1D();
|
||||||
case TransitionBoolConditionBase.typeKey:
|
case TransitionBoolConditionBase.typeKey:
|
||||||
return TransitionBoolCondition();
|
return TransitionBoolCondition();
|
||||||
|
case BlendStateTransitionBase.typeKey:
|
||||||
|
return BlendStateTransition();
|
||||||
case StateMachineBoolBase.typeKey:
|
case StateMachineBoolBase.typeKey:
|
||||||
return StateMachineBool();
|
return StateMachineBool();
|
||||||
case LinearGradientBase.typeKey:
|
case LinearGradientBase.typeKey:
|
||||||
@ -262,6 +283,16 @@ class RiveCoreContext {
|
|||||||
object.objectId = value;
|
object.objectId = value;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case BlendAnimationBase.animationIdPropertyKey:
|
||||||
|
if (object is BlendAnimationBase && value is int) {
|
||||||
|
object.animationId = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case BlendAnimationDirectBase.inputIdPropertyKey:
|
||||||
|
if (object is BlendAnimationDirectBase && value is int) {
|
||||||
|
object.inputId = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case StateMachineComponentBase.namePropertyKey:
|
case StateMachineComponentBase.namePropertyKey:
|
||||||
if (object is StateMachineComponentBase && value is String) {
|
if (object is StateMachineComponentBase && value is String) {
|
||||||
object.name = value;
|
object.name = value;
|
||||||
@ -402,6 +433,21 @@ class RiveCoreContext {
|
|||||||
object.enableWorkArea = value;
|
object.enableWorkArea = value;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case BlendAnimation1DBase.valuePropertyKey:
|
||||||
|
if (object is BlendAnimation1DBase && value is double) {
|
||||||
|
object.value = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case BlendState1DBase.inputIdPropertyKey:
|
||||||
|
if (object is BlendState1DBase && value is int) {
|
||||||
|
object.inputId = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case BlendStateTransitionBase.exitBlendAnimationIdPropertyKey:
|
||||||
|
if (object is BlendStateTransitionBase && value is int) {
|
||||||
|
object.exitBlendAnimationId = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case StateMachineBoolBase.valuePropertyKey:
|
case StateMachineBoolBase.valuePropertyKey:
|
||||||
if (object is StateMachineBoolBase && value is bool) {
|
if (object is StateMachineBoolBase && value is bool) {
|
||||||
object.value = value;
|
object.value = value;
|
||||||
@ -846,6 +892,8 @@ class RiveCoreContext {
|
|||||||
case DrawTargetBase.placementValuePropertyKey:
|
case DrawTargetBase.placementValuePropertyKey:
|
||||||
case AnimationStateBase.animationIdPropertyKey:
|
case AnimationStateBase.animationIdPropertyKey:
|
||||||
case KeyedObjectBase.objectIdPropertyKey:
|
case KeyedObjectBase.objectIdPropertyKey:
|
||||||
|
case BlendAnimationBase.animationIdPropertyKey:
|
||||||
|
case BlendAnimationDirectBase.inputIdPropertyKey:
|
||||||
case TransitionConditionBase.inputIdPropertyKey:
|
case TransitionConditionBase.inputIdPropertyKey:
|
||||||
case KeyedPropertyBase.propertyKeyPropertyKey:
|
case KeyedPropertyBase.propertyKeyPropertyKey:
|
||||||
case KeyFrameBase.framePropertyKey:
|
case KeyFrameBase.framePropertyKey:
|
||||||
@ -862,6 +910,8 @@ class RiveCoreContext {
|
|||||||
case LinearAnimationBase.loopValuePropertyKey:
|
case LinearAnimationBase.loopValuePropertyKey:
|
||||||
case LinearAnimationBase.workStartPropertyKey:
|
case LinearAnimationBase.workStartPropertyKey:
|
||||||
case LinearAnimationBase.workEndPropertyKey:
|
case LinearAnimationBase.workEndPropertyKey:
|
||||||
|
case BlendState1DBase.inputIdPropertyKey:
|
||||||
|
case BlendStateTransitionBase.exitBlendAnimationIdPropertyKey:
|
||||||
case StrokeBase.capPropertyKey:
|
case StrokeBase.capPropertyKey:
|
||||||
case StrokeBase.joinPropertyKey:
|
case StrokeBase.joinPropertyKey:
|
||||||
case TrimPathBase.modeValuePropertyKey:
|
case TrimPathBase.modeValuePropertyKey:
|
||||||
@ -889,6 +939,7 @@ class RiveCoreContext {
|
|||||||
case CubicInterpolatorBase.y2PropertyKey:
|
case CubicInterpolatorBase.y2PropertyKey:
|
||||||
case KeyFrameDoubleBase.valuePropertyKey:
|
case KeyFrameDoubleBase.valuePropertyKey:
|
||||||
case LinearAnimationBase.speedPropertyKey:
|
case LinearAnimationBase.speedPropertyKey:
|
||||||
|
case BlendAnimation1DBase.valuePropertyKey:
|
||||||
case LinearGradientBase.startXPropertyKey:
|
case LinearGradientBase.startXPropertyKey:
|
||||||
case LinearGradientBase.startYPropertyKey:
|
case LinearGradientBase.startYPropertyKey:
|
||||||
case LinearGradientBase.endXPropertyKey:
|
case LinearGradientBase.endXPropertyKey:
|
||||||
@ -990,6 +1041,10 @@ class RiveCoreContext {
|
|||||||
return (object as AnimationStateBase).animationId;
|
return (object as AnimationStateBase).animationId;
|
||||||
case KeyedObjectBase.objectIdPropertyKey:
|
case KeyedObjectBase.objectIdPropertyKey:
|
||||||
return (object as KeyedObjectBase).objectId;
|
return (object as KeyedObjectBase).objectId;
|
||||||
|
case BlendAnimationBase.animationIdPropertyKey:
|
||||||
|
return (object as BlendAnimationBase).animationId;
|
||||||
|
case BlendAnimationDirectBase.inputIdPropertyKey:
|
||||||
|
return (object as BlendAnimationDirectBase).inputId;
|
||||||
case TransitionConditionBase.inputIdPropertyKey:
|
case TransitionConditionBase.inputIdPropertyKey:
|
||||||
return (object as TransitionConditionBase).inputId;
|
return (object as TransitionConditionBase).inputId;
|
||||||
case KeyedPropertyBase.propertyKeyPropertyKey:
|
case KeyedPropertyBase.propertyKeyPropertyKey:
|
||||||
@ -1022,6 +1077,10 @@ class RiveCoreContext {
|
|||||||
return (object as LinearAnimationBase).workStart;
|
return (object as LinearAnimationBase).workStart;
|
||||||
case LinearAnimationBase.workEndPropertyKey:
|
case LinearAnimationBase.workEndPropertyKey:
|
||||||
return (object as LinearAnimationBase).workEnd;
|
return (object as LinearAnimationBase).workEnd;
|
||||||
|
case BlendState1DBase.inputIdPropertyKey:
|
||||||
|
return (object as BlendState1DBase).inputId;
|
||||||
|
case BlendStateTransitionBase.exitBlendAnimationIdPropertyKey:
|
||||||
|
return (object as BlendStateTransitionBase).exitBlendAnimationId;
|
||||||
case StrokeBase.capPropertyKey:
|
case StrokeBase.capPropertyKey:
|
||||||
return (object as StrokeBase).cap;
|
return (object as StrokeBase).cap;
|
||||||
case StrokeBase.joinPropertyKey:
|
case StrokeBase.joinPropertyKey:
|
||||||
@ -1080,6 +1139,8 @@ class RiveCoreContext {
|
|||||||
return (object as KeyFrameDoubleBase).value;
|
return (object as KeyFrameDoubleBase).value;
|
||||||
case LinearAnimationBase.speedPropertyKey:
|
case LinearAnimationBase.speedPropertyKey:
|
||||||
return (object as LinearAnimationBase).speed;
|
return (object as LinearAnimationBase).speed;
|
||||||
|
case BlendAnimation1DBase.valuePropertyKey:
|
||||||
|
return (object as BlendAnimation1DBase).value;
|
||||||
case LinearGradientBase.startXPropertyKey:
|
case LinearGradientBase.startXPropertyKey:
|
||||||
return (object as LinearGradientBase).startX;
|
return (object as LinearGradientBase).startX;
|
||||||
case LinearGradientBase.startYPropertyKey:
|
case LinearGradientBase.startYPropertyKey:
|
||||||
@ -1237,13 +1298,19 @@ class RiveCoreContext {
|
|||||||
static void setString(Core object, int propertyKey, String value) {
|
static void setString(Core object, int propertyKey, String value) {
|
||||||
switch (propertyKey) {
|
switch (propertyKey) {
|
||||||
case ComponentBase.namePropertyKey:
|
case ComponentBase.namePropertyKey:
|
||||||
if (object is ComponentBase) object.name = value;
|
if (object is ComponentBase) {
|
||||||
|
object.name = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case StateMachineComponentBase.namePropertyKey:
|
case StateMachineComponentBase.namePropertyKey:
|
||||||
if (object is StateMachineComponentBase) object.name = value;
|
if (object is StateMachineComponentBase) {
|
||||||
|
object.name = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case AnimationBase.namePropertyKey:
|
case AnimationBase.namePropertyKey:
|
||||||
if (object is AnimationBase) object.name = value;
|
if (object is AnimationBase) {
|
||||||
|
object.name = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1251,121 +1318,219 @@ class RiveCoreContext {
|
|||||||
static void setUint(Core object, int propertyKey, int value) {
|
static void setUint(Core object, int propertyKey, int value) {
|
||||||
switch (propertyKey) {
|
switch (propertyKey) {
|
||||||
case ComponentBase.parentIdPropertyKey:
|
case ComponentBase.parentIdPropertyKey:
|
||||||
if (object is ComponentBase) object.parentId = value;
|
if (object is ComponentBase) {
|
||||||
|
object.parentId = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case DrawTargetBase.drawableIdPropertyKey:
|
case DrawTargetBase.drawableIdPropertyKey:
|
||||||
if (object is DrawTargetBase) object.drawableId = value;
|
if (object is DrawTargetBase) {
|
||||||
|
object.drawableId = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case DrawTargetBase.placementValuePropertyKey:
|
case DrawTargetBase.placementValuePropertyKey:
|
||||||
if (object is DrawTargetBase) object.placementValue = value;
|
if (object is DrawTargetBase) {
|
||||||
|
object.placementValue = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case AnimationStateBase.animationIdPropertyKey:
|
case AnimationStateBase.animationIdPropertyKey:
|
||||||
if (object is AnimationStateBase) object.animationId = value;
|
if (object is AnimationStateBase) {
|
||||||
|
object.animationId = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case KeyedObjectBase.objectIdPropertyKey:
|
case KeyedObjectBase.objectIdPropertyKey:
|
||||||
if (object is KeyedObjectBase) object.objectId = value;
|
if (object is KeyedObjectBase) {
|
||||||
|
object.objectId = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case BlendAnimationBase.animationIdPropertyKey:
|
||||||
|
if (object is BlendAnimationBase) {
|
||||||
|
object.animationId = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case BlendAnimationDirectBase.inputIdPropertyKey:
|
||||||
|
if (object is BlendAnimationDirectBase) {
|
||||||
|
object.inputId = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case TransitionConditionBase.inputIdPropertyKey:
|
case TransitionConditionBase.inputIdPropertyKey:
|
||||||
if (object is TransitionConditionBase) object.inputId = value;
|
if (object is TransitionConditionBase) {
|
||||||
|
object.inputId = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case KeyedPropertyBase.propertyKeyPropertyKey:
|
case KeyedPropertyBase.propertyKeyPropertyKey:
|
||||||
if (object is KeyedPropertyBase) object.propertyKey = value;
|
if (object is KeyedPropertyBase) {
|
||||||
|
object.propertyKey = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case KeyFrameBase.framePropertyKey:
|
case KeyFrameBase.framePropertyKey:
|
||||||
if (object is KeyFrameBase) object.frame = value;
|
if (object is KeyFrameBase) {
|
||||||
|
object.frame = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case KeyFrameBase.interpolationTypePropertyKey:
|
case KeyFrameBase.interpolationTypePropertyKey:
|
||||||
if (object is KeyFrameBase) object.interpolationType = value;
|
if (object is KeyFrameBase) {
|
||||||
|
object.interpolationType = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case KeyFrameBase.interpolatorIdPropertyKey:
|
case KeyFrameBase.interpolatorIdPropertyKey:
|
||||||
if (object is KeyFrameBase) object.interpolatorId = value;
|
if (object is KeyFrameBase) {
|
||||||
|
object.interpolatorId = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case KeyFrameIdBase.valuePropertyKey:
|
case KeyFrameIdBase.valuePropertyKey:
|
||||||
if (object is KeyFrameIdBase) object.value = value;
|
if (object is KeyFrameIdBase) {
|
||||||
|
object.value = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case TransitionValueConditionBase.opValuePropertyKey:
|
case TransitionValueConditionBase.opValuePropertyKey:
|
||||||
if (object is TransitionValueConditionBase) object.opValue = value;
|
if (object is TransitionValueConditionBase) {
|
||||||
|
object.opValue = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case StateTransitionBase.stateToIdPropertyKey:
|
case StateTransitionBase.stateToIdPropertyKey:
|
||||||
if (object is StateTransitionBase) object.stateToId = value;
|
if (object is StateTransitionBase) {
|
||||||
|
object.stateToId = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case StateTransitionBase.flagsPropertyKey:
|
case StateTransitionBase.flagsPropertyKey:
|
||||||
if (object is StateTransitionBase) object.flags = value;
|
if (object is StateTransitionBase) {
|
||||||
|
object.flags = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case StateTransitionBase.durationPropertyKey:
|
case StateTransitionBase.durationPropertyKey:
|
||||||
if (object is StateTransitionBase) object.duration = value;
|
if (object is StateTransitionBase) {
|
||||||
|
object.duration = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case StateTransitionBase.exitTimePropertyKey:
|
case StateTransitionBase.exitTimePropertyKey:
|
||||||
if (object is StateTransitionBase) object.exitTime = value;
|
if (object is StateTransitionBase) {
|
||||||
|
object.exitTime = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case LinearAnimationBase.fpsPropertyKey:
|
case LinearAnimationBase.fpsPropertyKey:
|
||||||
if (object is LinearAnimationBase) object.fps = value;
|
if (object is LinearAnimationBase) {
|
||||||
|
object.fps = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case LinearAnimationBase.durationPropertyKey:
|
case LinearAnimationBase.durationPropertyKey:
|
||||||
if (object is LinearAnimationBase) object.duration = value;
|
if (object is LinearAnimationBase) {
|
||||||
|
object.duration = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case LinearAnimationBase.loopValuePropertyKey:
|
case LinearAnimationBase.loopValuePropertyKey:
|
||||||
if (object is LinearAnimationBase) object.loopValue = value;
|
if (object is LinearAnimationBase) {
|
||||||
|
object.loopValue = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case LinearAnimationBase.workStartPropertyKey:
|
case LinearAnimationBase.workStartPropertyKey:
|
||||||
if (object is LinearAnimationBase) object.workStart = value;
|
if (object is LinearAnimationBase) {
|
||||||
|
object.workStart = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case LinearAnimationBase.workEndPropertyKey:
|
case LinearAnimationBase.workEndPropertyKey:
|
||||||
if (object is LinearAnimationBase) object.workEnd = value;
|
if (object is LinearAnimationBase) {
|
||||||
|
object.workEnd = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case BlendState1DBase.inputIdPropertyKey:
|
||||||
|
if (object is BlendState1DBase) {
|
||||||
|
object.inputId = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case BlendStateTransitionBase.exitBlendAnimationIdPropertyKey:
|
||||||
|
if (object is BlendStateTransitionBase) {
|
||||||
|
object.exitBlendAnimationId = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case StrokeBase.capPropertyKey:
|
case StrokeBase.capPropertyKey:
|
||||||
if (object is StrokeBase) object.cap = value;
|
if (object is StrokeBase) {
|
||||||
|
object.cap = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case StrokeBase.joinPropertyKey:
|
case StrokeBase.joinPropertyKey:
|
||||||
if (object is StrokeBase) object.join = value;
|
if (object is StrokeBase) {
|
||||||
|
object.join = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case TrimPathBase.modeValuePropertyKey:
|
case TrimPathBase.modeValuePropertyKey:
|
||||||
if (object is TrimPathBase) object.modeValue = value;
|
if (object is TrimPathBase) {
|
||||||
|
object.modeValue = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case FillBase.fillRulePropertyKey:
|
case FillBase.fillRulePropertyKey:
|
||||||
if (object is FillBase) object.fillRule = value;
|
if (object is FillBase) {
|
||||||
|
object.fillRule = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case PathBase.pathFlagsPropertyKey:
|
case PathBase.pathFlagsPropertyKey:
|
||||||
if (object is PathBase) object.pathFlags = value;
|
if (object is PathBase) {
|
||||||
|
object.pathFlags = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case DrawableBase.blendModeValuePropertyKey:
|
case DrawableBase.blendModeValuePropertyKey:
|
||||||
if (object is DrawableBase) object.blendModeValue = value;
|
if (object is DrawableBase) {
|
||||||
|
object.blendModeValue = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case DrawableBase.drawableFlagsPropertyKey:
|
case DrawableBase.drawableFlagsPropertyKey:
|
||||||
if (object is DrawableBase) object.drawableFlags = value;
|
if (object is DrawableBase) {
|
||||||
|
object.drawableFlags = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case WeightBase.valuesPropertyKey:
|
case WeightBase.valuesPropertyKey:
|
||||||
if (object is WeightBase) object.values = value;
|
if (object is WeightBase) {
|
||||||
|
object.values = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case WeightBase.indicesPropertyKey:
|
case WeightBase.indicesPropertyKey:
|
||||||
if (object is WeightBase) object.indices = value;
|
if (object is WeightBase) {
|
||||||
|
object.indices = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case CubicWeightBase.inValuesPropertyKey:
|
case CubicWeightBase.inValuesPropertyKey:
|
||||||
if (object is CubicWeightBase) object.inValues = value;
|
if (object is CubicWeightBase) {
|
||||||
|
object.inValues = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case CubicWeightBase.inIndicesPropertyKey:
|
case CubicWeightBase.inIndicesPropertyKey:
|
||||||
if (object is CubicWeightBase) object.inIndices = value;
|
if (object is CubicWeightBase) {
|
||||||
|
object.inIndices = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case CubicWeightBase.outValuesPropertyKey:
|
case CubicWeightBase.outValuesPropertyKey:
|
||||||
if (object is CubicWeightBase) object.outValues = value;
|
if (object is CubicWeightBase) {
|
||||||
|
object.outValues = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case CubicWeightBase.outIndicesPropertyKey:
|
case CubicWeightBase.outIndicesPropertyKey:
|
||||||
if (object is CubicWeightBase) object.outIndices = value;
|
if (object is CubicWeightBase) {
|
||||||
|
object.outIndices = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ClippingShapeBase.sourceIdPropertyKey:
|
case ClippingShapeBase.sourceIdPropertyKey:
|
||||||
if (object is ClippingShapeBase) object.sourceId = value;
|
if (object is ClippingShapeBase) {
|
||||||
|
object.sourceId = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ClippingShapeBase.fillRulePropertyKey:
|
case ClippingShapeBase.fillRulePropertyKey:
|
||||||
if (object is ClippingShapeBase) object.fillRule = value;
|
if (object is ClippingShapeBase) {
|
||||||
|
object.fillRule = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case PolygonBase.pointsPropertyKey:
|
case PolygonBase.pointsPropertyKey:
|
||||||
if (object is PolygonBase) object.points = value;
|
if (object is PolygonBase) {
|
||||||
|
object.points = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case DrawRulesBase.drawTargetIdPropertyKey:
|
case DrawRulesBase.drawTargetIdPropertyKey:
|
||||||
if (object is DrawRulesBase) object.drawTargetId = value;
|
if (object is DrawRulesBase) {
|
||||||
|
object.drawTargetId = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case TendonBase.boneIdPropertyKey:
|
case TendonBase.boneIdPropertyKey:
|
||||||
if (object is TendonBase) object.boneId = value;
|
if (object is TendonBase) {
|
||||||
|
object.boneId = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1373,205 +1538,344 @@ class RiveCoreContext {
|
|||||||
static void setDouble(Core object, int propertyKey, double value) {
|
static void setDouble(Core object, int propertyKey, double value) {
|
||||||
switch (propertyKey) {
|
switch (propertyKey) {
|
||||||
case StateMachineNumberBase.valuePropertyKey:
|
case StateMachineNumberBase.valuePropertyKey:
|
||||||
if (object is StateMachineNumberBase) object.value = value;
|
if (object is StateMachineNumberBase) {
|
||||||
|
object.value = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case TransitionNumberConditionBase.valuePropertyKey:
|
case TransitionNumberConditionBase.valuePropertyKey:
|
||||||
if (object is TransitionNumberConditionBase) object.value = value;
|
if (object is TransitionNumberConditionBase) {
|
||||||
|
object.value = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case CubicInterpolatorBase.x1PropertyKey:
|
case CubicInterpolatorBase.x1PropertyKey:
|
||||||
if (object is CubicInterpolatorBase) object.x1 = value;
|
if (object is CubicInterpolatorBase) {
|
||||||
|
object.x1 = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case CubicInterpolatorBase.y1PropertyKey:
|
case CubicInterpolatorBase.y1PropertyKey:
|
||||||
if (object is CubicInterpolatorBase) object.y1 = value;
|
if (object is CubicInterpolatorBase) {
|
||||||
|
object.y1 = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case CubicInterpolatorBase.x2PropertyKey:
|
case CubicInterpolatorBase.x2PropertyKey:
|
||||||
if (object is CubicInterpolatorBase) object.x2 = value;
|
if (object is CubicInterpolatorBase) {
|
||||||
|
object.x2 = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case CubicInterpolatorBase.y2PropertyKey:
|
case CubicInterpolatorBase.y2PropertyKey:
|
||||||
if (object is CubicInterpolatorBase) object.y2 = value;
|
if (object is CubicInterpolatorBase) {
|
||||||
|
object.y2 = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case KeyFrameDoubleBase.valuePropertyKey:
|
case KeyFrameDoubleBase.valuePropertyKey:
|
||||||
if (object is KeyFrameDoubleBase) object.value = value;
|
if (object is KeyFrameDoubleBase) {
|
||||||
|
object.value = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case LinearAnimationBase.speedPropertyKey:
|
case LinearAnimationBase.speedPropertyKey:
|
||||||
if (object is LinearAnimationBase) object.speed = value;
|
if (object is LinearAnimationBase) {
|
||||||
|
object.speed = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case BlendAnimation1DBase.valuePropertyKey:
|
||||||
|
if (object is BlendAnimation1DBase) {
|
||||||
|
object.value = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case LinearGradientBase.startXPropertyKey:
|
case LinearGradientBase.startXPropertyKey:
|
||||||
if (object is LinearGradientBase) object.startX = value;
|
if (object is LinearGradientBase) {
|
||||||
|
object.startX = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case LinearGradientBase.startYPropertyKey:
|
case LinearGradientBase.startYPropertyKey:
|
||||||
if (object is LinearGradientBase) object.startY = value;
|
if (object is LinearGradientBase) {
|
||||||
|
object.startY = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case LinearGradientBase.endXPropertyKey:
|
case LinearGradientBase.endXPropertyKey:
|
||||||
if (object is LinearGradientBase) object.endX = value;
|
if (object is LinearGradientBase) {
|
||||||
|
object.endX = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case LinearGradientBase.endYPropertyKey:
|
case LinearGradientBase.endYPropertyKey:
|
||||||
if (object is LinearGradientBase) object.endY = value;
|
if (object is LinearGradientBase) {
|
||||||
|
object.endY = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case LinearGradientBase.opacityPropertyKey:
|
case LinearGradientBase.opacityPropertyKey:
|
||||||
if (object is LinearGradientBase) object.opacity = value;
|
if (object is LinearGradientBase) {
|
||||||
|
object.opacity = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case StrokeBase.thicknessPropertyKey:
|
case StrokeBase.thicknessPropertyKey:
|
||||||
if (object is StrokeBase) object.thickness = value;
|
if (object is StrokeBase) {
|
||||||
|
object.thickness = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case GradientStopBase.positionPropertyKey:
|
case GradientStopBase.positionPropertyKey:
|
||||||
if (object is GradientStopBase) object.position = value;
|
if (object is GradientStopBase) {
|
||||||
|
object.position = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case TrimPathBase.startPropertyKey:
|
case TrimPathBase.startPropertyKey:
|
||||||
if (object is TrimPathBase) object.start = value;
|
if (object is TrimPathBase) {
|
||||||
|
object.start = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case TrimPathBase.endPropertyKey:
|
case TrimPathBase.endPropertyKey:
|
||||||
if (object is TrimPathBase) object.end = value;
|
if (object is TrimPathBase) {
|
||||||
|
object.end = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case TrimPathBase.offsetPropertyKey:
|
case TrimPathBase.offsetPropertyKey:
|
||||||
if (object is TrimPathBase) object.offset = value;
|
if (object is TrimPathBase) {
|
||||||
|
object.offset = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case TransformComponentBase.rotationPropertyKey:
|
case TransformComponentBase.rotationPropertyKey:
|
||||||
if (object is TransformComponentBase) object.rotation = value;
|
if (object is TransformComponentBase) {
|
||||||
|
object.rotation = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case TransformComponentBase.scaleXPropertyKey:
|
case TransformComponentBase.scaleXPropertyKey:
|
||||||
if (object is TransformComponentBase) object.scaleX = value;
|
if (object is TransformComponentBase) {
|
||||||
|
object.scaleX = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case TransformComponentBase.scaleYPropertyKey:
|
case TransformComponentBase.scaleYPropertyKey:
|
||||||
if (object is TransformComponentBase) object.scaleY = value;
|
if (object is TransformComponentBase) {
|
||||||
|
object.scaleY = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case TransformComponentBase.opacityPropertyKey:
|
case TransformComponentBase.opacityPropertyKey:
|
||||||
if (object is TransformComponentBase) object.opacity = value;
|
if (object is TransformComponentBase) {
|
||||||
|
object.opacity = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case NodeBase.xPropertyKey:
|
case NodeBase.xPropertyKey:
|
||||||
if (object is NodeBase) object.x = value;
|
if (object is NodeBase) {
|
||||||
|
object.x = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case NodeBase.yPropertyKey:
|
case NodeBase.yPropertyKey:
|
||||||
if (object is NodeBase) object.y = value;
|
if (object is NodeBase) {
|
||||||
|
object.y = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case PathVertexBase.xPropertyKey:
|
case PathVertexBase.xPropertyKey:
|
||||||
if (object is PathVertexBase) object.x = value;
|
if (object is PathVertexBase) {
|
||||||
|
object.x = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case PathVertexBase.yPropertyKey:
|
case PathVertexBase.yPropertyKey:
|
||||||
if (object is PathVertexBase) object.y = value;
|
if (object is PathVertexBase) {
|
||||||
|
object.y = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case StraightVertexBase.radiusPropertyKey:
|
case StraightVertexBase.radiusPropertyKey:
|
||||||
if (object is StraightVertexBase) object.radius = value;
|
if (object is StraightVertexBase) {
|
||||||
|
object.radius = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case CubicAsymmetricVertexBase.rotationPropertyKey:
|
case CubicAsymmetricVertexBase.rotationPropertyKey:
|
||||||
if (object is CubicAsymmetricVertexBase) object.rotation = value;
|
if (object is CubicAsymmetricVertexBase) {
|
||||||
|
object.rotation = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case CubicAsymmetricVertexBase.inDistancePropertyKey:
|
case CubicAsymmetricVertexBase.inDistancePropertyKey:
|
||||||
if (object is CubicAsymmetricVertexBase) object.inDistance = value;
|
if (object is CubicAsymmetricVertexBase) {
|
||||||
|
object.inDistance = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case CubicAsymmetricVertexBase.outDistancePropertyKey:
|
case CubicAsymmetricVertexBase.outDistancePropertyKey:
|
||||||
if (object is CubicAsymmetricVertexBase) object.outDistance = value;
|
if (object is CubicAsymmetricVertexBase) {
|
||||||
|
object.outDistance = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ParametricPathBase.widthPropertyKey:
|
case ParametricPathBase.widthPropertyKey:
|
||||||
if (object is ParametricPathBase) object.width = value;
|
if (object is ParametricPathBase) {
|
||||||
|
object.width = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ParametricPathBase.heightPropertyKey:
|
case ParametricPathBase.heightPropertyKey:
|
||||||
if (object is ParametricPathBase) object.height = value;
|
if (object is ParametricPathBase) {
|
||||||
|
object.height = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ParametricPathBase.originXPropertyKey:
|
case ParametricPathBase.originXPropertyKey:
|
||||||
if (object is ParametricPathBase) object.originX = value;
|
if (object is ParametricPathBase) {
|
||||||
|
object.originX = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ParametricPathBase.originYPropertyKey:
|
case ParametricPathBase.originYPropertyKey:
|
||||||
if (object is ParametricPathBase) object.originY = value;
|
if (object is ParametricPathBase) {
|
||||||
|
object.originY = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case RectangleBase.cornerRadiusTLPropertyKey:
|
case RectangleBase.cornerRadiusTLPropertyKey:
|
||||||
if (object is RectangleBase) object.cornerRadiusTL = value;
|
if (object is RectangleBase) {
|
||||||
|
object.cornerRadiusTL = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case RectangleBase.cornerRadiusTRPropertyKey:
|
case RectangleBase.cornerRadiusTRPropertyKey:
|
||||||
if (object is RectangleBase) object.cornerRadiusTR = value;
|
if (object is RectangleBase) {
|
||||||
|
object.cornerRadiusTR = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case RectangleBase.cornerRadiusBLPropertyKey:
|
case RectangleBase.cornerRadiusBLPropertyKey:
|
||||||
if (object is RectangleBase) object.cornerRadiusBL = value;
|
if (object is RectangleBase) {
|
||||||
|
object.cornerRadiusBL = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case RectangleBase.cornerRadiusBRPropertyKey:
|
case RectangleBase.cornerRadiusBRPropertyKey:
|
||||||
if (object is RectangleBase) object.cornerRadiusBR = value;
|
if (object is RectangleBase) {
|
||||||
|
object.cornerRadiusBR = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case CubicMirroredVertexBase.rotationPropertyKey:
|
case CubicMirroredVertexBase.rotationPropertyKey:
|
||||||
if (object is CubicMirroredVertexBase) object.rotation = value;
|
if (object is CubicMirroredVertexBase) {
|
||||||
|
object.rotation = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case CubicMirroredVertexBase.distancePropertyKey:
|
case CubicMirroredVertexBase.distancePropertyKey:
|
||||||
if (object is CubicMirroredVertexBase) object.distance = value;
|
if (object is CubicMirroredVertexBase) {
|
||||||
|
object.distance = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case PolygonBase.cornerRadiusPropertyKey:
|
case PolygonBase.cornerRadiusPropertyKey:
|
||||||
if (object is PolygonBase) object.cornerRadius = value;
|
if (object is PolygonBase) {
|
||||||
|
object.cornerRadius = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case StarBase.innerRadiusPropertyKey:
|
case StarBase.innerRadiusPropertyKey:
|
||||||
if (object is StarBase) object.innerRadius = value;
|
if (object is StarBase) {
|
||||||
|
object.innerRadius = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case CubicDetachedVertexBase.inRotationPropertyKey:
|
case CubicDetachedVertexBase.inRotationPropertyKey:
|
||||||
if (object is CubicDetachedVertexBase) object.inRotation = value;
|
if (object is CubicDetachedVertexBase) {
|
||||||
|
object.inRotation = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case CubicDetachedVertexBase.inDistancePropertyKey:
|
case CubicDetachedVertexBase.inDistancePropertyKey:
|
||||||
if (object is CubicDetachedVertexBase) object.inDistance = value;
|
if (object is CubicDetachedVertexBase) {
|
||||||
|
object.inDistance = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case CubicDetachedVertexBase.outRotationPropertyKey:
|
case CubicDetachedVertexBase.outRotationPropertyKey:
|
||||||
if (object is CubicDetachedVertexBase) object.outRotation = value;
|
if (object is CubicDetachedVertexBase) {
|
||||||
|
object.outRotation = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case CubicDetachedVertexBase.outDistancePropertyKey:
|
case CubicDetachedVertexBase.outDistancePropertyKey:
|
||||||
if (object is CubicDetachedVertexBase) object.outDistance = value;
|
if (object is CubicDetachedVertexBase) {
|
||||||
|
object.outDistance = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ArtboardBase.widthPropertyKey:
|
case ArtboardBase.widthPropertyKey:
|
||||||
if (object is ArtboardBase) object.width = value;
|
if (object is ArtboardBase) {
|
||||||
|
object.width = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ArtboardBase.heightPropertyKey:
|
case ArtboardBase.heightPropertyKey:
|
||||||
if (object is ArtboardBase) object.height = value;
|
if (object is ArtboardBase) {
|
||||||
|
object.height = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ArtboardBase.xPropertyKey:
|
case ArtboardBase.xPropertyKey:
|
||||||
if (object is ArtboardBase) object.x = value;
|
if (object is ArtboardBase) {
|
||||||
|
object.x = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ArtboardBase.yPropertyKey:
|
case ArtboardBase.yPropertyKey:
|
||||||
if (object is ArtboardBase) object.y = value;
|
if (object is ArtboardBase) {
|
||||||
|
object.y = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ArtboardBase.originXPropertyKey:
|
case ArtboardBase.originXPropertyKey:
|
||||||
if (object is ArtboardBase) object.originX = value;
|
if (object is ArtboardBase) {
|
||||||
|
object.originX = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ArtboardBase.originYPropertyKey:
|
case ArtboardBase.originYPropertyKey:
|
||||||
if (object is ArtboardBase) object.originY = value;
|
if (object is ArtboardBase) {
|
||||||
|
object.originY = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case BoneBase.lengthPropertyKey:
|
case BoneBase.lengthPropertyKey:
|
||||||
if (object is BoneBase) object.length = value;
|
if (object is BoneBase) {
|
||||||
|
object.length = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case RootBoneBase.xPropertyKey:
|
case RootBoneBase.xPropertyKey:
|
||||||
if (object is RootBoneBase) object.x = value;
|
if (object is RootBoneBase) {
|
||||||
|
object.x = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case RootBoneBase.yPropertyKey:
|
case RootBoneBase.yPropertyKey:
|
||||||
if (object is RootBoneBase) object.y = value;
|
if (object is RootBoneBase) {
|
||||||
|
object.y = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case SkinBase.xxPropertyKey:
|
case SkinBase.xxPropertyKey:
|
||||||
if (object is SkinBase) object.xx = value;
|
if (object is SkinBase) {
|
||||||
|
object.xx = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case SkinBase.yxPropertyKey:
|
case SkinBase.yxPropertyKey:
|
||||||
if (object is SkinBase) object.yx = value;
|
if (object is SkinBase) {
|
||||||
|
object.yx = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case SkinBase.xyPropertyKey:
|
case SkinBase.xyPropertyKey:
|
||||||
if (object is SkinBase) object.xy = value;
|
if (object is SkinBase) {
|
||||||
|
object.xy = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case SkinBase.yyPropertyKey:
|
case SkinBase.yyPropertyKey:
|
||||||
if (object is SkinBase) object.yy = value;
|
if (object is SkinBase) {
|
||||||
|
object.yy = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case SkinBase.txPropertyKey:
|
case SkinBase.txPropertyKey:
|
||||||
if (object is SkinBase) object.tx = value;
|
if (object is SkinBase) {
|
||||||
|
object.tx = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case SkinBase.tyPropertyKey:
|
case SkinBase.tyPropertyKey:
|
||||||
if (object is SkinBase) object.ty = value;
|
if (object is SkinBase) {
|
||||||
|
object.ty = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case TendonBase.xxPropertyKey:
|
case TendonBase.xxPropertyKey:
|
||||||
if (object is TendonBase) object.xx = value;
|
if (object is TendonBase) {
|
||||||
|
object.xx = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case TendonBase.yxPropertyKey:
|
case TendonBase.yxPropertyKey:
|
||||||
if (object is TendonBase) object.yx = value;
|
if (object is TendonBase) {
|
||||||
|
object.yx = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case TendonBase.xyPropertyKey:
|
case TendonBase.xyPropertyKey:
|
||||||
if (object is TendonBase) object.xy = value;
|
if (object is TendonBase) {
|
||||||
|
object.xy = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case TendonBase.yyPropertyKey:
|
case TendonBase.yyPropertyKey:
|
||||||
if (object is TendonBase) object.yy = value;
|
if (object is TendonBase) {
|
||||||
|
object.yy = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case TendonBase.txPropertyKey:
|
case TendonBase.txPropertyKey:
|
||||||
if (object is TendonBase) object.tx = value;
|
if (object is TendonBase) {
|
||||||
|
object.tx = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case TendonBase.tyPropertyKey:
|
case TendonBase.tyPropertyKey:
|
||||||
if (object is TendonBase) object.ty = value;
|
if (object is TendonBase) {
|
||||||
|
object.ty = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1579,13 +1883,19 @@ class RiveCoreContext {
|
|||||||
static void setColor(Core object, int propertyKey, int value) {
|
static void setColor(Core object, int propertyKey, int value) {
|
||||||
switch (propertyKey) {
|
switch (propertyKey) {
|
||||||
case KeyFrameColorBase.valuePropertyKey:
|
case KeyFrameColorBase.valuePropertyKey:
|
||||||
if (object is KeyFrameColorBase) object.value = value;
|
if (object is KeyFrameColorBase) {
|
||||||
|
object.value = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case SolidColorBase.colorValuePropertyKey:
|
case SolidColorBase.colorValuePropertyKey:
|
||||||
if (object is SolidColorBase) object.colorValue = value;
|
if (object is SolidColorBase) {
|
||||||
|
object.colorValue = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case GradientStopBase.colorValuePropertyKey:
|
case GradientStopBase.colorValuePropertyKey:
|
||||||
if (object is GradientStopBase) object.colorValue = value;
|
if (object is GradientStopBase) {
|
||||||
|
object.colorValue = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1593,25 +1903,39 @@ class RiveCoreContext {
|
|||||||
static void setBool(Core object, int propertyKey, bool value) {
|
static void setBool(Core object, int propertyKey, bool value) {
|
||||||
switch (propertyKey) {
|
switch (propertyKey) {
|
||||||
case LinearAnimationBase.enableWorkAreaPropertyKey:
|
case LinearAnimationBase.enableWorkAreaPropertyKey:
|
||||||
if (object is LinearAnimationBase) object.enableWorkArea = value;
|
if (object is LinearAnimationBase) {
|
||||||
|
object.enableWorkArea = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case StateMachineBoolBase.valuePropertyKey:
|
case StateMachineBoolBase.valuePropertyKey:
|
||||||
if (object is StateMachineBoolBase) object.value = value;
|
if (object is StateMachineBoolBase) {
|
||||||
|
object.value = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ShapePaintBase.isVisiblePropertyKey:
|
case ShapePaintBase.isVisiblePropertyKey:
|
||||||
if (object is ShapePaintBase) object.isVisible = value;
|
if (object is ShapePaintBase) {
|
||||||
|
object.isVisible = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case StrokeBase.transformAffectsStrokePropertyKey:
|
case StrokeBase.transformAffectsStrokePropertyKey:
|
||||||
if (object is StrokeBase) object.transformAffectsStroke = value;
|
if (object is StrokeBase) {
|
||||||
|
object.transformAffectsStroke = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case PointsPathBase.isClosedPropertyKey:
|
case PointsPathBase.isClosedPropertyKey:
|
||||||
if (object is PointsPathBase) object.isClosed = value;
|
if (object is PointsPathBase) {
|
||||||
|
object.isClosed = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case RectangleBase.linkCornerRadiusPropertyKey:
|
case RectangleBase.linkCornerRadiusPropertyKey:
|
||||||
if (object is RectangleBase) object.linkCornerRadius = value;
|
if (object is RectangleBase) {
|
||||||
|
object.linkCornerRadius = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ClippingShapeBase.isVisiblePropertyKey:
|
case ClippingShapeBase.isVisiblePropertyKey:
|
||||||
if (object is ClippingShapeBase) object.isVisible = value;
|
if (object is ClippingShapeBase) {
|
||||||
|
object.isVisible = value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:rive/src/core/core.dart';
|
import 'package:rive/src/core/core.dart';
|
||||||
import 'package:rive/src/rive_core/artboard.dart';
|
import 'package:rive/src/rive_core/artboard.dart';
|
||||||
|
|
||||||
import 'package:rive/src/generated/animation/animation_base.dart';
|
import 'package:rive/src/generated/animation/animation_base.dart';
|
||||||
export 'package:rive/src/generated/animation/animation_base.dart';
|
export 'package:rive/src/generated/animation/animation_base.dart';
|
||||||
|
|
||||||
@ -17,10 +18,13 @@ class Animation extends AnimationBase<RuntimeArtboard> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void onAddedDirty() {}
|
void onAddedDirty() {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onAdded() {}
|
void onAdded() {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool validate() => super.validate() && _artboard != null;
|
bool validate() => super.validate() && _artboard != null;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void nameChanged(String from, String to) {}
|
void nameChanged(String from, String to) {}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import 'package:rive/src/core/core.dart';
|
import 'package:rive/src/core/core.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/animation_state_instance.dart';
|
||||||
import 'package:rive/src/rive_core/animation/linear_animation.dart';
|
import 'package:rive/src/rive_core/animation/linear_animation.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/state_instance.dart';
|
||||||
|
import 'package:rive/src/rive_core/artboard.dart';
|
||||||
import 'package:rive/src/generated/animation/animation_state_base.dart';
|
import 'package:rive/src/generated/animation/animation_state_base.dart';
|
||||||
export 'package:rive/src/generated/animation/animation_state_base.dart';
|
export 'package:rive/src/generated/animation/animation_state_base.dart';
|
||||||
|
|
||||||
@ -15,6 +18,7 @@ class AnimationState extends AnimationStateBase {
|
|||||||
if (_animation == value) {
|
if (_animation == value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_animation = value;
|
_animation = value;
|
||||||
animationId = value?.id ?? Core.missingId;
|
animationId = value?.id ?? Core.missingId;
|
||||||
}
|
}
|
||||||
@ -23,4 +27,33 @@ class AnimationState extends AnimationStateBase {
|
|||||||
void animationIdChanged(int from, int to) {
|
void animationIdChanged(int from, int to) {
|
||||||
animation = id == Core.missingId ? null : context.resolve(to);
|
animation = id == Core.missingId ? null : context.resolve(to);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
StateInstance makeInstance() {
|
||||||
|
if (animation == null) {
|
||||||
|
// Failed to load at runtime/some new type we don't understand.
|
||||||
|
return SystemStateInstance(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return AnimationStateInstance(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We keep the importer code here so that we can inject this for runtime.
|
||||||
|
// #2690
|
||||||
|
@override
|
||||||
|
bool import(ImportStack stack) {
|
||||||
|
var importer = stack.latest<ArtboardImporter>(ArtboardBase.typeKey);
|
||||||
|
if (importer == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (animationId >= 0 && animationId < importer.artboard.animations.length) {
|
||||||
|
var found = importer.artboard.animations[animationId];
|
||||||
|
if (found is LinearAnimation) {
|
||||||
|
animation = found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.import(stack);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
25
lib/src/rive_core/animation/animation_state_instance.dart
Normal file
25
lib/src/rive_core/animation/animation_state_instance.dart
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import 'package:rive/src/core/core.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/animation_state.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/linear_animation_instance.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/state_instance.dart';
|
||||||
|
|
||||||
|
/// Simple wrapper around [LinearAnimationInstance] making it compatible with
|
||||||
|
/// the [StateMachine]'s [StateInstance] interface.
|
||||||
|
class AnimationStateInstance extends StateInstance {
|
||||||
|
final LinearAnimationInstance animationInstance;
|
||||||
|
|
||||||
|
AnimationStateInstance(AnimationState state)
|
||||||
|
: assert(state.animation != null),
|
||||||
|
animationInstance = LinearAnimationInstance(state.animation!),
|
||||||
|
super(state);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void advance(double seconds, _) => animationInstance.advance(seconds);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void apply(CoreContext core, double mix) => animationInstance.animation
|
||||||
|
.apply(animationInstance.time, coreContext: core, mix: mix);
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get keepGoing => animationInstance.keepGoing;
|
||||||
|
}
|
@ -1,4 +1,8 @@
|
|||||||
|
import 'package:rive/src/rive_core/animation/state_instance.dart';
|
||||||
import 'package:rive/src/generated/animation/any_state_base.dart';
|
import 'package:rive/src/generated/animation/any_state_base.dart';
|
||||||
export 'package:rive/src/generated/animation/any_state_base.dart';
|
export 'package:rive/src/generated/animation/any_state_base.dart';
|
||||||
|
|
||||||
class AnyState extends AnyStateBase {}
|
class AnyState extends AnyStateBase {
|
||||||
|
@override
|
||||||
|
StateInstance makeInstance() => SystemStateInstance(this);
|
||||||
|
}
|
||||||
|
52
lib/src/rive_core/animation/blend_animation.dart
Normal file
52
lib/src/rive_core/animation/blend_animation.dart
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import 'package:rive/src/core/core.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/layer_state.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/linear_animation.dart';
|
||||||
|
import 'package:rive/src/rive_core/artboard.dart';
|
||||||
|
import 'package:rive/src/generated/animation/blend_animation_base.dart';
|
||||||
|
import 'package:rive/src/generated/artboard_base.dart';
|
||||||
|
export 'package:rive/src/generated/animation/blend_animation_base.dart';
|
||||||
|
|
||||||
|
abstract class BlendAnimation extends BlendAnimationBase {
|
||||||
|
LinearAnimation? _animation;
|
||||||
|
LinearAnimation? get animation => _animation;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void animationIdChanged(int from, int to) {
|
||||||
|
_animation = context.resolve(to);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onAdded() {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onRemoved() {
|
||||||
|
super.onRemoved();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onAddedDirty() {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool import(ImportStack importStack) {
|
||||||
|
var importer =
|
||||||
|
importStack.latest<LayerStateImporter>(LayerStateBase.typeKey);
|
||||||
|
if (importer == null || !importer.addBlendAnimation(this)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var artboardImporter =
|
||||||
|
importStack.latest<ArtboardImporter>(ArtboardBase.typeKey);
|
||||||
|
if (artboardImporter == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (animationId >= 0 &&
|
||||||
|
animationId < artboardImporter.artboard.animations.length) {
|
||||||
|
var found = artboardImporter.artboard.animations[animationId];
|
||||||
|
if (found is LinearAnimation) {
|
||||||
|
_animation = found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.import(importStack);
|
||||||
|
}
|
||||||
|
}
|
9
lib/src/rive_core/animation/blend_animation_1d.dart
Normal file
9
lib/src/rive_core/animation/blend_animation_1d.dart
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import 'package:rive/src/generated/animation/blend_animation_1d_base.dart';
|
||||||
|
export 'package:rive/src/generated/animation/blend_animation_1d_base.dart';
|
||||||
|
|
||||||
|
class BlendAnimation1D extends BlendAnimation1DBase {
|
||||||
|
@override
|
||||||
|
void valueChanged(double from, double to) {
|
||||||
|
// TODO: implement valueChanged
|
||||||
|
}
|
||||||
|
}
|
30
lib/src/rive_core/animation/blend_animation_direct.dart
Normal file
30
lib/src/rive_core/animation/blend_animation_direct.dart
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import 'package:rive/src/core/core.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/state_machine.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/state_machine_number.dart';
|
||||||
|
import 'package:rive/src/generated/animation/blend_animation_direct_base.dart';
|
||||||
|
export 'package:rive/src/generated/animation/blend_animation_direct_base.dart';
|
||||||
|
|
||||||
|
class BlendAnimationDirect extends BlendAnimationDirectBase {
|
||||||
|
StateMachineNumber? _input;
|
||||||
|
StateMachineNumber? get input => _input;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void inputIdChanged(int from, int to) {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool import(ImportStack stack) {
|
||||||
|
var importer = stack.latest<StateMachineImporter>(StateMachineBase.typeKey);
|
||||||
|
if (importer == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (inputId >= 0 && inputId < importer.machine.inputs.length) {
|
||||||
|
var found = importer.machine.inputs[inputId];
|
||||||
|
if (found is StateMachineNumber) {
|
||||||
|
_input = found;
|
||||||
|
inputId = found.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.import(stack);
|
||||||
|
}
|
||||||
|
}
|
20
lib/src/rive_core/animation/blend_state.dart
Normal file
20
lib/src/rive_core/animation/blend_state.dart
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import 'package:rive/src/core/core.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/blend_animation.dart';
|
||||||
|
import 'package:rive/src/generated/animation/blend_state_base.dart';
|
||||||
|
export 'package:rive/src/generated/animation/blend_state_base.dart';
|
||||||
|
|
||||||
|
//
|
||||||
|
abstract class BlendState<T extends BlendAnimation> extends BlendStateBase {
|
||||||
|
final BlendAnimations<T> _animations = BlendAnimations<T>();
|
||||||
|
BlendAnimations<T> get animations => _animations;
|
||||||
|
|
||||||
|
void internalAddAnimation(T animation) {
|
||||||
|
assert(!_animations.contains(animation),
|
||||||
|
'shouln\'t already contain the animation');
|
||||||
|
_animations.add(animation);
|
||||||
|
}
|
||||||
|
|
||||||
|
void internalRemoveAnimation(T animation) {
|
||||||
|
_animations.remove(animation);
|
||||||
|
}
|
||||||
|
}
|
24
lib/src/rive_core/animation/blend_state_1d.dart
Normal file
24
lib/src/rive_core/animation/blend_state_1d.dart
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import 'package:rive/src/rive_core/animation/blend_state_1d_instance.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/state_instance.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/state_machine_number.dart';
|
||||||
|
import 'package:rive/src/generated/animation/blend_state_1d_base.dart';
|
||||||
|
export 'package:rive/src/generated/animation/blend_state_1d_base.dart';
|
||||||
|
|
||||||
|
class BlendState1D extends BlendState1DBase {
|
||||||
|
StateMachineNumber? _input;
|
||||||
|
StateMachineNumber? get input => _input;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void inputIdChanged(int from, int to) {
|
||||||
|
_input = context.resolve<StateMachineNumber>(to);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onAddedDirty() {
|
||||||
|
super.onAddedDirty();
|
||||||
|
_input = context.resolve<StateMachineNumber>(inputId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
StateInstance makeInstance() => BlendState1DInstance(this);
|
||||||
|
}
|
82
lib/src/rive_core/animation/blend_state_1d_instance.dart
Normal file
82
lib/src/rive_core/animation/blend_state_1d_instance.dart
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import 'dart:collection';
|
||||||
|
|
||||||
|
import 'package:rive/src/rive_core/animation/blend_animation_1d.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/blend_state_1d.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/blend_state_instance.dart';
|
||||||
|
|
||||||
|
/// [BlendState1D] mixing logic that runs inside the [StateMachine].
|
||||||
|
class BlendState1DInstance
|
||||||
|
extends BlendStateInstance<BlendState1D, BlendAnimation1D> {
|
||||||
|
BlendState1DInstance(BlendState1D state) : super(state) {
|
||||||
|
animationInstances.sort(
|
||||||
|
(a, b) => a.blendAnimation.value.compareTo(b.blendAnimation.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Binary find the closest animation index.
|
||||||
|
int animationIndex(double value) {
|
||||||
|
int idx = 0;
|
||||||
|
int mid = 0;
|
||||||
|
double closestValue = 0;
|
||||||
|
int start = 0;
|
||||||
|
int end = animationInstances.length - 1;
|
||||||
|
|
||||||
|
while (start <= end) {
|
||||||
|
mid = (start + end) >> 1;
|
||||||
|
closestValue = 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlendStateAnimationInstance<BlendAnimation1D>? _from;
|
||||||
|
BlendStateAnimationInstance<BlendAnimation1D>? _to;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void advance(double seconds, HashMap<int, dynamic> inputValues) {
|
||||||
|
super.advance(seconds, inputValues);
|
||||||
|
dynamic inputValue = inputValues[(state as BlendState1D).inputId];
|
||||||
|
var value = (inputValue is double
|
||||||
|
? inputValue
|
||||||
|
: (state as BlendState1D).input?.value) ??
|
||||||
|
0;
|
||||||
|
int index = animationIndex(value);
|
||||||
|
_to = index >= 0 && index < animationInstances.length
|
||||||
|
? animationInstances[index]
|
||||||
|
: null;
|
||||||
|
_from = index - 1 >= 0 && index - 1 < animationInstances.length
|
||||||
|
? animationInstances[index - 1]
|
||||||
|
: null;
|
||||||
|
|
||||||
|
double mix, mixFrom;
|
||||||
|
if (_to == null ||
|
||||||
|
_from == null ||
|
||||||
|
_to!.blendAnimation.value == _from!.blendAnimation.value) {
|
||||||
|
mix = mixFrom = 1;
|
||||||
|
} else {
|
||||||
|
mix = (value - _from!.blendAnimation.value) /
|
||||||
|
(_to!.blendAnimation.value - _from!.blendAnimation.value);
|
||||||
|
mixFrom = 1.0 - mix;
|
||||||
|
}
|
||||||
|
|
||||||
|
var toValue = _to?.blendAnimation.value;
|
||||||
|
var fromValue = _from?.blendAnimation.value;
|
||||||
|
for (final animation in animationInstances) {
|
||||||
|
if (animation.blendAnimation.value == toValue) {
|
||||||
|
animation.mix = mix;
|
||||||
|
} else if (animation.blendAnimation.value == fromValue) {
|
||||||
|
animation.mix = mixFrom;
|
||||||
|
} else {
|
||||||
|
animation.mix = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
lib/src/rive_core/animation/blend_state_direct.dart
Normal file
9
lib/src/rive_core/animation/blend_state_direct.dart
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import 'package:rive/src/rive_core/animation/blend_state_direct_instance.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/state_instance.dart';
|
||||||
|
import 'package:rive/src/generated/animation/blend_state_direct_base.dart';
|
||||||
|
export 'package:rive/src/generated/animation/blend_state_direct_base.dart';
|
||||||
|
|
||||||
|
class BlendStateDirect extends BlendStateDirectBase {
|
||||||
|
@override
|
||||||
|
StateInstance makeInstance() => BlendStateDirectInstance(this);
|
||||||
|
}
|
24
lib/src/rive_core/animation/blend_state_direct_instance.dart
Normal file
24
lib/src/rive_core/animation/blend_state_direct_instance.dart
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import 'dart:collection';
|
||||||
|
|
||||||
|
import 'package:rive/src/rive_core/animation/blend_animation_direct.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/blend_state_direct.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/blend_state_instance.dart';
|
||||||
|
|
||||||
|
/// [BlendStateDirect] mixing logic that runs inside the [StateMachine].
|
||||||
|
class BlendStateDirectInstance
|
||||||
|
extends BlendStateInstance<BlendStateDirect, BlendAnimationDirect> {
|
||||||
|
BlendStateDirectInstance(BlendStateDirect state) : super(state);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void advance(double seconds, HashMap<int, dynamic> inputValues) {
|
||||||
|
super.advance(seconds, inputValues);
|
||||||
|
for (final animation in animationInstances) {
|
||||||
|
dynamic inputValue = inputValues[animation.blendAnimation.inputId];
|
||||||
|
var value = (inputValue is double
|
||||||
|
? inputValue
|
||||||
|
: animation.blendAnimation.input?.value) ??
|
||||||
|
0;
|
||||||
|
animation.mix = value / 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
59
lib/src/rive_core/animation/blend_state_instance.dart
Normal file
59
lib/src/rive_core/animation/blend_state_instance.dart
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import 'dart:collection';
|
||||||
|
|
||||||
|
import 'package:rive/src/core/core.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/blend_animation.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/blend_state.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/linear_animation_instance.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/state_instance.dart';
|
||||||
|
|
||||||
|
/// Individual animation in a blend state instance.
|
||||||
|
class BlendStateAnimationInstance<T extends BlendAnimation> {
|
||||||
|
final T blendAnimation;
|
||||||
|
final LinearAnimationInstance animationInstance;
|
||||||
|
double mix = 0;
|
||||||
|
|
||||||
|
BlendStateAnimationInstance(this.blendAnimation)
|
||||||
|
: animationInstance = LinearAnimationInstance(blendAnimation.animation!);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generic blend state instance which works for [BlendState<BlendAnimation>]s
|
||||||
|
/// where T represents the BlendState and K the BlendAnimation.
|
||||||
|
abstract class BlendStateInstance<T extends BlendState<K>,
|
||||||
|
K extends BlendAnimation> extends StateInstance {
|
||||||
|
final List<BlendStateAnimationInstance<K>> animationInstances;
|
||||||
|
BlendStateInstance(T state)
|
||||||
|
: animationInstances = state.animations
|
||||||
|
.where((animation) => animation.animation != null)
|
||||||
|
.map((animation) => BlendStateAnimationInstance(animation))
|
||||||
|
.toList(growable: false),
|
||||||
|
super(state);
|
||||||
|
|
||||||
|
bool _keepGoing = true;
|
||||||
|
@override
|
||||||
|
bool get keepGoing => _keepGoing;
|
||||||
|
|
||||||
|
@mustCallSuper
|
||||||
|
@override
|
||||||
|
void advance(double seconds, HashMap<int, dynamic> inputValues) {
|
||||||
|
_keepGoing = false;
|
||||||
|
// Advance all the animations in the blend state
|
||||||
|
for (final animation in animationInstances) {
|
||||||
|
if (animation.animationInstance.advance(seconds) && !keepGoing) {
|
||||||
|
_keepGoing = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void apply(CoreContext core, double mix) {
|
||||||
|
for (final animation in animationInstances) {
|
||||||
|
double m = mix * animation.mix;
|
||||||
|
if (m == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
animation.animationInstance.animation
|
||||||
|
.apply(animation.animationInstance.time, coreContext: core, mix: m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
lib/src/rive_core/animation/blend_state_transition.dart
Normal file
31
lib/src/rive_core/animation/blend_state_transition.dart
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import 'package:rive/src/rive_core/animation/blend_animation.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/blend_state_instance.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/layer_state.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/linear_animation.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/linear_animation_instance.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/state_instance.dart';
|
||||||
|
import 'package:rive/src/generated/animation/blend_state_transition_base.dart';
|
||||||
|
export 'package:rive/src/generated/animation/blend_state_transition_base.dart';
|
||||||
|
|
||||||
|
class BlendStateTransition extends BlendStateTransitionBase {
|
||||||
|
BlendAnimation? exitBlendAnimation;
|
||||||
|
|
||||||
|
@override
|
||||||
|
LinearAnimationInstance? exitTimeAnimationInstance(StateInstance stateFrom) {
|
||||||
|
if (stateFrom is BlendStateInstance) {
|
||||||
|
for (final blendAnimation in stateFrom.animationInstances) {
|
||||||
|
if (blendAnimation.blendAnimation == exitBlendAnimation) {
|
||||||
|
return blendAnimation.animationInstance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
LinearAnimation? exitTimeAnimation(LayerState stateFrom) =>
|
||||||
|
exitBlendAnimation?.animation;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void exitBlendAnimationIdChanged(int from, int to) {}
|
||||||
|
}
|
@ -1,14 +1,18 @@
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:rive/src/core/core.dart';
|
import 'package:rive/src/core/core.dart';
|
||||||
import 'package:rive/src/rive_core/animation/interpolator.dart';
|
import 'package:rive/src/rive_core/animation/interpolator.dart';
|
||||||
import 'package:rive/src/rive_core/artboard.dart';
|
import 'package:rive/src/rive_core/artboard.dart';
|
||||||
import 'package:rive/src/generated/animation/cubic_interpolator_base.dart';
|
import 'package:rive/src/generated/animation/cubic_interpolator_base.dart';
|
||||||
|
|
||||||
const int newtonIterations = 4;
|
const int newtonIterations = 4;
|
||||||
|
|
||||||
|
// Implements https://github.com/gre/bezier-easing/blob/master/src/index.js
|
||||||
const double newtonMinSlope = 0.001;
|
const double newtonMinSlope = 0.001;
|
||||||
const double sampleStepSize = 1.0 / (splineTableSize - 1.0);
|
const double sampleStepSize = 1.0 / (splineTableSize - 1.0);
|
||||||
const int splineTableSize = 11;
|
const int splineTableSize = 11;
|
||||||
const int subdivisionMaxIterations = 10;
|
const int subdivisionMaxIterations = 10;
|
||||||
|
|
||||||
const double subdivisionPrecision = 0.0000001;
|
const double subdivisionPrecision = 0.0000001;
|
||||||
double _calcBezier(double aT, double aA1, double aA2) {
|
double _calcBezier(double aT, double aA1, double aA2) {
|
||||||
return (((1.0 - 3.0 * aA2 + 3.0 * aA1) * aT + (3.0 * aA2 - 6.0 * aA1)) * aT +
|
return (((1.0 - 3.0 * aA2 + 3.0 * aA1) * aT + (3.0 * aA2 - 6.0 * aA1)) * aT +
|
||||||
@ -16,14 +20,17 @@ double _calcBezier(double aT, double aA1, double aA2) {
|
|||||||
aT;
|
aT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
|
||||||
double _getSlope(double aT, double aA1, double aA2) {
|
double _getSlope(double aT, double aA1, double aA2) {
|
||||||
return 3.0 * (1.0 - 3.0 * aA2 + 3.0 * aA1) * aT * aT +
|
return 3.0 * (1.0 - 3.0 * aA2 + 3.0 * aA1) * aT * aT +
|
||||||
2.0 * (3.0 * aA2 - 6.0 * aA1) * aT +
|
2.0 * (3.0 * aA2 - 6.0 * aA1) * aT +
|
||||||
(3.0 * aA1);
|
(3.0 * aA1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2.
|
||||||
class CubicInterpolator extends CubicInterpolatorBase implements Interpolator {
|
class CubicInterpolator extends CubicInterpolatorBase implements Interpolator {
|
||||||
_CubicEase _ease = _CubicEase.make(0.42, 0, 0.58, 1);
|
_CubicEase _ease = _CubicEase.make(0.42, 0, 0.58, 1);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool equalParameters(Interpolator other) {
|
bool equalParameters(Interpolator other) {
|
||||||
if (other is CubicInterpolator) {
|
if (other is CubicInterpolator) {
|
||||||
@ -37,16 +44,22 @@ class CubicInterpolator extends CubicInterpolatorBase implements Interpolator {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void onAdded() => _updateStoredCubic();
|
void onAdded() => _updateStoredCubic();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onAddedDirty() {}
|
void onAddedDirty() {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
double transform(double value) => _ease.transform(value);
|
double transform(double value) => _ease.transform(value);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void x1Changed(double from, double to) => _updateStoredCubic();
|
void x1Changed(double from, double to) => _updateStoredCubic();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void x2Changed(double from, double to) => _updateStoredCubic();
|
void x2Changed(double from, double to) => _updateStoredCubic();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void y1Changed(double from, double to) => _updateStoredCubic();
|
void y1Changed(double from, double to) => _updateStoredCubic();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void y2Changed(double from, double to) => _updateStoredCubic();
|
void y2Changed(double from, double to) => _updateStoredCubic();
|
||||||
void _updateStoredCubic() {
|
void _updateStoredCubic() {
|
||||||
@ -60,6 +73,7 @@ class CubicInterpolator extends CubicInterpolatorBase implements Interpolator {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
artboardHelper.addComponent(this);
|
artboardHelper.addComponent(this);
|
||||||
|
|
||||||
return super.import(stack);
|
return super.import(stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -68,23 +82,29 @@ class _Cubic extends _CubicEase {
|
|||||||
final Float64List _values = Float64List(splineTableSize);
|
final Float64List _values = Float64List(splineTableSize);
|
||||||
final double x1, y1, x2, y2;
|
final double x1, y1, x2, y2;
|
||||||
_Cubic(this.x1, this.y1, this.x2, this.y2) {
|
_Cubic(this.x1, this.y1, this.x2, this.y2) {
|
||||||
|
// Precompute values table
|
||||||
for (int i = 0; i < splineTableSize; ++i) {
|
for (int i = 0; i < splineTableSize; ++i) {
|
||||||
_values[i] = _calcBezier(i * sampleStepSize, x1, x2);
|
_values[i] = _calcBezier(i * sampleStepSize, x1, x2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
double getT(double x) {
|
double getT(double x) {
|
||||||
double intervalStart = 0.0;
|
double intervalStart = 0.0;
|
||||||
int currentSample = 1;
|
int currentSample = 1;
|
||||||
int lastSample = splineTableSize - 1;
|
int lastSample = splineTableSize - 1;
|
||||||
|
|
||||||
for (;
|
for (;
|
||||||
currentSample != lastSample && _values[currentSample] <= x;
|
currentSample != lastSample && _values[currentSample] <= x;
|
||||||
++currentSample) {
|
++currentSample) {
|
||||||
intervalStart += sampleStepSize;
|
intervalStart += sampleStepSize;
|
||||||
}
|
}
|
||||||
--currentSample;
|
--currentSample;
|
||||||
|
|
||||||
|
// Interpolate to provide an initial guess for t
|
||||||
var dist = (x - _values[currentSample]) /
|
var dist = (x - _values[currentSample]) /
|
||||||
(_values[currentSample + 1] - _values[currentSample]);
|
(_values[currentSample + 1] - _values[currentSample]);
|
||||||
var guessForT = intervalStart + dist * sampleStepSize;
|
var guessForT = intervalStart + dist * sampleStepSize;
|
||||||
|
|
||||||
var initialSlope = _getSlope(guessForT, x1, x2);
|
var initialSlope = _getSlope(guessForT, x1, x2);
|
||||||
if (initialSlope >= newtonMinSlope) {
|
if (initialSlope >= newtonMinSlope) {
|
||||||
for (int i = 0; i < newtonIterations; ++i) {
|
for (int i = 0; i < newtonIterations; ++i) {
|
||||||
@ -124,6 +144,7 @@ class _Cubic extends _CubicEase {
|
|||||||
|
|
||||||
abstract class _CubicEase {
|
abstract class _CubicEase {
|
||||||
double transform(double t);
|
double transform(double t);
|
||||||
|
|
||||||
static _CubicEase make(double x1, double y1, double x2, double y2) {
|
static _CubicEase make(double x1, double y1, double x2, double y2) {
|
||||||
if (x1 == y1 && x2 == y2) {
|
if (x1 == y1 && x2 == y2) {
|
||||||
return _LinearCubicEase();
|
return _LinearCubicEase();
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
|
import 'package:rive/src/rive_core/animation/state_instance.dart';
|
||||||
import 'package:rive/src/generated/animation/entry_state_base.dart';
|
import 'package:rive/src/generated/animation/entry_state_base.dart';
|
||||||
export 'package:rive/src/generated/animation/entry_state_base.dart';
|
export 'package:rive/src/generated/animation/entry_state_base.dart';
|
||||||
|
|
||||||
class EntryState extends EntryStateBase {}
|
class EntryState extends EntryStateBase {
|
||||||
|
@override
|
||||||
|
StateInstance makeInstance() => SystemStateInstance(this);
|
||||||
|
}
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
|
import 'package:rive/src/rive_core/animation/state_instance.dart';
|
||||||
import 'package:rive/src/generated/animation/exit_state_base.dart';
|
import 'package:rive/src/generated/animation/exit_state_base.dart';
|
||||||
export 'package:rive/src/generated/animation/exit_state_base.dart';
|
export 'package:rive/src/generated/animation/exit_state_base.dart';
|
||||||
|
|
||||||
class ExitState extends ExitStateBase {}
|
class ExitState extends ExitStateBase {
|
||||||
|
@override
|
||||||
|
StateInstance makeInstance() => SystemStateInstance(this);
|
||||||
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
abstract class Interpolator {
|
abstract class Interpolator {
|
||||||
int get id;
|
int get id;
|
||||||
|
|
||||||
|
/// Convert a linear interpolation factor to an eased one.
|
||||||
double transform(double value);
|
double transform(double value);
|
||||||
|
|
||||||
bool equalParameters(Interpolator other);
|
bool equalParameters(Interpolator other);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
|
|
||||||
import 'package:rive/src/core/core.dart';
|
import 'package:rive/src/core/core.dart';
|
||||||
import 'package:rive/src/rive_core/animation/keyed_property.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/component.dart';
|
||||||
|
|
||||||
import 'package:rive/src/generated/animation/keyed_object_base.dart';
|
import 'package:rive/src/generated/animation/keyed_object_base.dart';
|
||||||
import 'linear_animation.dart';
|
import 'linear_animation.dart';
|
||||||
export 'package:rive/src/generated/animation/keyed_object_base.dart';
|
export 'package:rive/src/generated/animation/keyed_object_base.dart';
|
||||||
@ -9,20 +11,26 @@ export 'package:rive/src/generated/animation/keyed_object_base.dart';
|
|||||||
class KeyedObject extends KeyedObjectBase<RuntimeArtboard> {
|
class KeyedObject extends KeyedObjectBase<RuntimeArtboard> {
|
||||||
final HashMap<int, KeyedProperty> _keyedProperties =
|
final HashMap<int, KeyedProperty> _keyedProperties =
|
||||||
HashMap<int, KeyedProperty>();
|
HashMap<int, KeyedProperty>();
|
||||||
|
|
||||||
Iterable<KeyedProperty> get keyedProperties => _keyedProperties.values;
|
Iterable<KeyedProperty> get keyedProperties => _keyedProperties.values;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onAddedDirty() {}
|
void onAddedDirty() {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onAdded() {}
|
void onAdded() {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool validate() {
|
bool validate() {
|
||||||
if (!super.validate()) {
|
if (!super.validate()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var component = context.resolve<Component>(objectId);
|
var component = context.resolve<Component>(objectId);
|
||||||
if (component == null) {
|
if (component == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,14 +41,22 @@ class KeyedObject extends KeyedObjectBase<RuntimeArtboard> {
|
|||||||
|
|
||||||
bool isValidKeyedProperty(KeyedProperty property) {
|
bool isValidKeyedProperty(KeyedProperty property) {
|
||||||
var value = _keyedProperties[property.propertyKey];
|
var value = _keyedProperties[property.propertyKey];
|
||||||
|
|
||||||
|
// If the property is already keyed, that's ok just make sure the
|
||||||
|
// KeyedObject matches.
|
||||||
if (value != null && value != property) {
|
if (value != null && value != property) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Called by rive_core to add a KeyedProperty to the animation. This should
|
||||||
|
/// be @internal when it's supported.
|
||||||
bool internalAddKeyedProperty(KeyedProperty property) {
|
bool internalAddKeyedProperty(KeyedProperty property) {
|
||||||
var value = _keyedProperties[property.propertyKey];
|
var value = _keyedProperties[property.propertyKey];
|
||||||
|
|
||||||
|
// If the property is already keyed, that's ok just make sure the
|
||||||
|
// KeyedObject matches.
|
||||||
if (value != null && value != property) {
|
if (value != null && value != property) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -48,11 +64,17 @@ class KeyedObject extends KeyedObjectBase<RuntimeArtboard> {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Called by rive_core to remove a KeyedObject to the animation. This should
|
||||||
|
/// be @internal when it's supported.
|
||||||
bool internalRemoveKeyedProperty(KeyedProperty property) {
|
bool internalRemoveKeyedProperty(KeyedProperty property) {
|
||||||
var removed = _keyedProperties.remove(property.propertyKey);
|
var removed = _keyedProperties.remove(property.propertyKey);
|
||||||
|
|
||||||
if (_keyedProperties.isEmpty) {
|
if (_keyedProperties.isEmpty) {
|
||||||
|
// Remove this keyed property.
|
||||||
context.removeObject(this);
|
context.removeObject(this);
|
||||||
}
|
}
|
||||||
|
// assert(removed == null || removed == property,
|
||||||
|
// '$removed was not $property or null');
|
||||||
return removed != null;
|
return removed != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,6 +90,7 @@ class KeyedObject extends KeyedObjectBase<RuntimeArtboard> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void objectIdChanged(int from, int to) {}
|
void objectIdChanged(int from, int to) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool import(ImportStack stack) {
|
bool import(ImportStack stack) {
|
||||||
var animationHelper =
|
var animationHelper =
|
||||||
@ -76,6 +99,7 @@ class KeyedObject extends KeyedObjectBase<RuntimeArtboard> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
animationHelper.addKeyedObject(this);
|
animationHelper.addKeyedObject(this);
|
||||||
|
|
||||||
return super.import(stack);
|
return super.import(stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:rive/src/core/core.dart';
|
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_object.dart';
|
||||||
import 'package:rive/src/rive_core/animation/keyframe.dart';
|
import 'package:rive/src/rive_core/animation/keyframe.dart';
|
||||||
|
|
||||||
import 'package:rive/src/generated/animation/keyed_property_base.dart';
|
import 'package:rive/src/generated/animation/keyed_property_base.dart';
|
||||||
export 'package:rive/src/generated/animation/keyed_property_base.dart';
|
export 'package:rive/src/generated/animation/keyed_property_base.dart';
|
||||||
|
|
||||||
@ -12,6 +13,8 @@ class KeyFrameList<T extends KeyFrameInterface> {
|
|||||||
List<T> _keyframes = [];
|
List<T> _keyframes = [];
|
||||||
Iterable<T> get keyframes => _keyframes;
|
Iterable<T> get keyframes => _keyframes;
|
||||||
set keyframes(Iterable<T> frames) => _keyframes = frames.toList();
|
set keyframes(Iterable<T> frames) => _keyframes = frames.toList();
|
||||||
|
|
||||||
|
/// Get the keyframe immediately following the provided one.
|
||||||
T? after(T keyframe) {
|
T? after(T keyframe) {
|
||||||
var index = _keyframes.indexOf(keyframe);
|
var index = _keyframes.indexOf(keyframe);
|
||||||
if (index != -1 && index + 1 < _keyframes.length) {
|
if (index != -1 && index + 1 < _keyframes.length) {
|
||||||
@ -20,12 +23,15 @@ class KeyFrameList<T extends KeyFrameInterface> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Find the index in the keyframe list of a specific time frame.
|
||||||
int indexOfFrame(int frame) {
|
int indexOfFrame(int frame) {
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
|
// Binary find the keyframe index.
|
||||||
int mid = 0;
|
int mid = 0;
|
||||||
int closestFrame = 0;
|
int closestFrame = 0;
|
||||||
int start = 0;
|
int start = 0;
|
||||||
int end = _keyframes.length - 1;
|
int end = _keyframes.length - 1;
|
||||||
|
|
||||||
while (start <= end) {
|
while (start <= end) {
|
||||||
mid = (start + end) >> 1;
|
mid = (start + end) >> 1;
|
||||||
closestFrame = _keyframes[mid].frame;
|
closestFrame = _keyframes[mid].frame;
|
||||||
@ -37,6 +43,7 @@ class KeyFrameList<T extends KeyFrameInterface> {
|
|||||||
idx = start = mid;
|
idx = start = mid;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
idx = start;
|
idx = start;
|
||||||
}
|
}
|
||||||
return idx;
|
return idx;
|
||||||
@ -49,13 +56,17 @@ class KeyedProperty extends KeyedPropertyBase<RuntimeArtboard>
|
|||||||
with KeyFrameList<KeyFrame> {
|
with KeyFrameList<KeyFrame> {
|
||||||
@override
|
@override
|
||||||
void onAdded() {}
|
void onAdded() {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onAddedDirty() {}
|
void onAddedDirty() {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onRemoved() {
|
void onRemoved() {
|
||||||
super.onRemoved();
|
super.onRemoved();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Called by rive_core to add a KeyFrame to this KeyedProperty. This should
|
||||||
|
/// be @internal when it's supported.
|
||||||
bool internalAddKeyFrame(KeyFrame frame) {
|
bool internalAddKeyFrame(KeyFrame frame) {
|
||||||
if (_keyframes.contains(frame)) {
|
if (_keyframes.contains(frame)) {
|
||||||
return false;
|
return false;
|
||||||
@ -65,44 +76,64 @@ class KeyedProperty extends KeyedPropertyBase<RuntimeArtboard>
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Called by rive_core to remove a KeyFrame from this KeyedProperty. This
|
||||||
|
/// should be @internal when it's supported.
|
||||||
bool internalRemoveKeyFrame(KeyFrame frame) {
|
bool internalRemoveKeyFrame(KeyFrame frame) {
|
||||||
var removed = _keyframes.remove(frame);
|
var removed = _keyframes.remove(frame);
|
||||||
if (_keyframes.isEmpty) {
|
if (_keyframes.isEmpty) {
|
||||||
|
// If they keyframes are now empty, we might want to remove this keyed
|
||||||
|
// property. Wait for any other pending changes to complete before
|
||||||
|
// checking.
|
||||||
context.dirty(_checkShouldRemove);
|
context.dirty(_checkShouldRemove);
|
||||||
}
|
}
|
||||||
|
|
||||||
return removed;
|
return removed;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _checkShouldRemove() {
|
void _checkShouldRemove() {
|
||||||
if (_keyframes.isEmpty) {
|
if (_keyframes.isEmpty) {
|
||||||
|
// Remove this keyed property.
|
||||||
context.removeObject(this);
|
context.removeObject(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Called by keyframes when their time value changes. This is a pretty rare
|
||||||
|
/// operation, usually occurs when a user moves a keyframe. Meaning: this
|
||||||
|
/// shouldn't make it into the runtimes unless we want to allow users moving
|
||||||
|
/// keyframes around at runtime via code for some reason.
|
||||||
void markKeyFrameOrderDirty() {
|
void markKeyFrameOrderDirty() {
|
||||||
context.dirty(_sortAndValidateKeyFrames);
|
context.dirty(_sortAndValidateKeyFrames);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _sortAndValidateKeyFrames() {
|
void _sortAndValidateKeyFrames() {
|
||||||
sort();
|
sort();
|
||||||
|
|
||||||
for (int i = 0; i < _keyframes.length - 1; i++) {
|
for (int i = 0; i < _keyframes.length - 1; i++) {
|
||||||
var a = _keyframes[i];
|
var a = _keyframes[i];
|
||||||
var b = _keyframes[i + 1];
|
var b = _keyframes[i + 1];
|
||||||
if (a.frame == b.frame) {
|
if (a.frame == b.frame) {
|
||||||
|
// N.B. this removes it from the list too.
|
||||||
context.removeObject(a);
|
context.removeObject(a);
|
||||||
|
// Repeat current.
|
||||||
i--;
|
i--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Number of keyframes for this keyed property.
|
||||||
int get numFrames => _keyframes.length;
|
int get numFrames => _keyframes.length;
|
||||||
|
|
||||||
KeyFrame getFrameAt(int index) => _keyframes[index];
|
KeyFrame getFrameAt(int index) => _keyframes[index];
|
||||||
|
|
||||||
int closestFrameIndex(double seconds) {
|
int closestFrameIndex(double seconds) {
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
|
// Binary find the keyframe index (use timeInSeconds here as opposed to the
|
||||||
|
// finder above which operates in frames).
|
||||||
int mid = 0;
|
int mid = 0;
|
||||||
double closestSeconds = 0;
|
double closestSeconds = 0;
|
||||||
int start = 0;
|
int start = 0;
|
||||||
int end = _keyframes.length - 1;
|
int end = _keyframes.length - 1;
|
||||||
|
|
||||||
while (start <= end) {
|
while (start <= end) {
|
||||||
mid = (start + end) >> 1;
|
mid = (start + end) >> 1;
|
||||||
closestSeconds = _keyframes[mid].seconds;
|
closestSeconds = _keyframes[mid].seconds;
|
||||||
@ -123,10 +154,12 @@ class KeyedProperty extends KeyedPropertyBase<RuntimeArtboard>
|
|||||||
if (_keyframes.isEmpty) {
|
if (_keyframes.isEmpty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int idx = closestFrameIndex(seconds);
|
int idx = closestFrameIndex(seconds);
|
||||||
int pk = propertyKey;
|
int pk = propertyKey;
|
||||||
if (idx == 0) {
|
if (idx == 0) {
|
||||||
var first = _keyframes[0];
|
var first = _keyframes[0];
|
||||||
|
|
||||||
first.apply(object, pk, mix);
|
first.apply(object, pk, mix);
|
||||||
} else {
|
} else {
|
||||||
if (idx < _keyframes.length) {
|
if (idx < _keyframes.length) {
|
||||||
@ -135,6 +168,8 @@ class KeyedProperty extends KeyedPropertyBase<RuntimeArtboard>
|
|||||||
if (seconds == toFrame.seconds) {
|
if (seconds == toFrame.seconds) {
|
||||||
toFrame.apply(object, pk, mix);
|
toFrame.apply(object, pk, mix);
|
||||||
} else {
|
} else {
|
||||||
|
/// Equivalent to fromFrame.interpolation ==
|
||||||
|
/// KeyFrameInterpolation.hold.
|
||||||
if (fromFrame.interpolationType == 0) {
|
if (fromFrame.interpolationType == 0) {
|
||||||
fromFrame.apply(object, pk, mix);
|
fromFrame.apply(object, pk, mix);
|
||||||
} else {
|
} else {
|
||||||
@ -143,6 +178,7 @@ class KeyedProperty extends KeyedPropertyBase<RuntimeArtboard>
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var last = _keyframes[idx - 1];
|
var last = _keyframes[idx - 1];
|
||||||
|
|
||||||
last.apply(object, pk, mix);
|
last.apply(object, pk, mix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -150,6 +186,7 @@ class KeyedProperty extends KeyedPropertyBase<RuntimeArtboard>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void propertyKeyChanged(int from, int to) {}
|
void propertyKeyChanged(int from, int to) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool import(ImportStack stack) {
|
bool import(ImportStack stack) {
|
||||||
var importer = stack.latest<KeyedObjectImporter>(KeyedObjectBase.typeKey);
|
var importer = stack.latest<KeyedObjectImporter>(KeyedObjectBase.typeKey);
|
||||||
@ -157,6 +194,7 @@ class KeyedProperty extends KeyedPropertyBase<RuntimeArtboard>
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
importer.addKeyedProperty(this);
|
importer.addKeyedProperty(this);
|
||||||
|
|
||||||
return super.import(stack);
|
return super.import(stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,14 +3,18 @@ import 'package:rive/src/rive_core/animation/interpolator.dart';
|
|||||||
import 'package:rive/src/rive_core/animation/keyed_property.dart';
|
import 'package:rive/src/rive_core/animation/keyed_property.dart';
|
||||||
import 'package:rive/src/rive_core/animation/keyframe_interpolation.dart';
|
import 'package:rive/src/rive_core/animation/keyframe_interpolation.dart';
|
||||||
import 'package:rive/src/rive_core/animation/linear_animation.dart';
|
import 'package:rive/src/rive_core/animation/linear_animation.dart';
|
||||||
|
|
||||||
import 'package:rive/src/generated/animation/keyframe_base.dart';
|
import 'package:rive/src/generated/animation/keyframe_base.dart';
|
||||||
|
|
||||||
export 'package:rive/src/generated/animation/keyframe_base.dart';
|
export 'package:rive/src/generated/animation/keyframe_base.dart';
|
||||||
|
|
||||||
abstract class KeyFrame extends KeyFrameBase<RuntimeArtboard>
|
abstract class KeyFrame extends KeyFrameBase<RuntimeArtboard>
|
||||||
implements KeyFrameInterface {
|
implements KeyFrameInterface {
|
||||||
double _timeInSeconds = 0;
|
double _timeInSeconds = 0;
|
||||||
double get seconds => _timeInSeconds;
|
double get seconds => _timeInSeconds;
|
||||||
|
|
||||||
bool get canInterpolate => true;
|
bool get canInterpolate => true;
|
||||||
|
|
||||||
KeyFrameInterpolation get interpolation =>
|
KeyFrameInterpolation get interpolation =>
|
||||||
KeyFrameInterpolation.values[interpolationType];
|
KeyFrameInterpolation.values[interpolationType];
|
||||||
set interpolation(KeyFrameInterpolation value) {
|
set interpolation(KeyFrameInterpolation value) {
|
||||||
@ -19,13 +23,17 @@ abstract class KeyFrame extends KeyFrameBase<RuntimeArtboard>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void interpolationTypeChanged(int from, int to) {}
|
void interpolationTypeChanged(int from, int to) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void interpolatorIdChanged(int from, int to) {
|
void interpolatorIdChanged(int from, int to) {
|
||||||
|
// This might resolve to null during a load or if context isn't available
|
||||||
|
// yet so we also do this in onAddedDirty.
|
||||||
interpolator = context.resolve(to);
|
interpolator = context.resolve(to);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onAdded() {}
|
void onAdded() {}
|
||||||
|
|
||||||
void computeSeconds(LinearAnimation animation) {
|
void computeSeconds(LinearAnimation animation) {
|
||||||
_timeInSeconds = frame / animation.fps;
|
_timeInSeconds = frame / animation.fps;
|
||||||
}
|
}
|
||||||
@ -44,15 +52,22 @@ abstract class KeyFrame extends KeyFrameBase<RuntimeArtboard>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void frameChanged(int from, int to) {}
|
void frameChanged(int from, int to) {}
|
||||||
|
|
||||||
|
/// Apply the value of this keyframe to the object's property.
|
||||||
void apply(Core object, int propertyKey, double mix);
|
void apply(Core object, int propertyKey, double mix);
|
||||||
|
|
||||||
|
/// Interpolate the value between this keyframe and the next and apply it to
|
||||||
|
/// the object's property.
|
||||||
void applyInterpolation(Core object, int propertyKey, double seconds,
|
void applyInterpolation(Core object, int propertyKey, double seconds,
|
||||||
covariant KeyFrame nextFrame, double mix);
|
covariant KeyFrame nextFrame, double mix);
|
||||||
|
|
||||||
Interpolator? _interpolator;
|
Interpolator? _interpolator;
|
||||||
Interpolator? get interpolator => _interpolator;
|
Interpolator? get interpolator => _interpolator;
|
||||||
set interpolator(Interpolator? value) {
|
set interpolator(Interpolator? value) {
|
||||||
if (_interpolator == value) {
|
if (_interpolator == value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_interpolator = value;
|
_interpolator = value;
|
||||||
interpolatorId = value?.id ?? Core.missingId;
|
interpolatorId = value?.id ?? Core.missingId;
|
||||||
}
|
}
|
||||||
@ -65,6 +80,7 @@ abstract class KeyFrame extends KeyFrameBase<RuntimeArtboard>
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
keyedPropertyHelper.addKeyFrame(this);
|
keyedPropertyHelper.addKeyFrame(this);
|
||||||
|
|
||||||
return super.import(importStack);
|
return super.import(importStack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:rive/src/core/core.dart';
|
import 'package:rive/src/core/core.dart';
|
||||||
import 'package:rive/src/generated/animation/keyframe_color_base.dart';
|
import 'package:rive/src/generated/animation/keyframe_color_base.dart';
|
||||||
import 'package:rive/src/generated/rive_core_context.dart';
|
import 'package:rive/src/generated/rive_core_context.dart';
|
||||||
@ -22,13 +23,16 @@ class KeyFrameColor extends KeyFrameColorBase {
|
|||||||
@override
|
@override
|
||||||
void apply(Core<CoreContext> object, int propertyKey, double mix) =>
|
void apply(Core<CoreContext> object, int propertyKey, double mix) =>
|
||||||
_apply(object, propertyKey, mix, value);
|
_apply(object, propertyKey, mix, value);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void applyInterpolation(Core<CoreContext> object, int propertyKey,
|
void applyInterpolation(Core<CoreContext> object, int propertyKey,
|
||||||
double currentTime, KeyFrameColor nextFrame, double mix) {
|
double currentTime, KeyFrameColor nextFrame, double mix) {
|
||||||
var f = (currentTime - seconds) / (nextFrame.seconds - seconds);
|
var f = (currentTime - seconds) / (nextFrame.seconds - seconds);
|
||||||
|
|
||||||
if (interpolator != null) {
|
if (interpolator != null) {
|
||||||
f = interpolator!.transform(f);
|
f = interpolator!.transform(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
var color = Color.lerp(Color(value), Color(nextFrame.value), f);
|
var color = Color.lerp(Color(value), Color(nextFrame.value), f);
|
||||||
if (color != null) {
|
if (color != null) {
|
||||||
_apply(object, propertyKey, mix, color.value);
|
_apply(object, propertyKey, mix, color.value);
|
||||||
|
@ -22,13 +22,16 @@ class KeyFrameDouble extends KeyFrameDoubleBase {
|
|||||||
@override
|
@override
|
||||||
void apply(Core<CoreContext> object, int propertyKey, double mix) =>
|
void apply(Core<CoreContext> object, int propertyKey, double mix) =>
|
||||||
_apply(object, propertyKey, mix, value);
|
_apply(object, propertyKey, mix, value);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void applyInterpolation(Core<CoreContext> object, int propertyKey,
|
void applyInterpolation(Core<CoreContext> object, int propertyKey,
|
||||||
double currentTime, KeyFrameDouble nextFrame, double mix) {
|
double currentTime, KeyFrameDouble nextFrame, double mix) {
|
||||||
var f = (currentTime - seconds) / (nextFrame.seconds - seconds);
|
var f = (currentTime - seconds) / (nextFrame.seconds - seconds);
|
||||||
|
|
||||||
if (interpolator != null) {
|
if (interpolator != null) {
|
||||||
f = interpolator!.transform(f);
|
f = interpolator!.transform(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
_apply(object, propertyKey, mix, value + (nextFrame.value - value) * f);
|
_apply(object, propertyKey, mix, value + (nextFrame.value - value) * f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ export 'package:rive/src/generated/animation/keyframe_id_base.dart';
|
|||||||
class KeyFrameId extends KeyFrameIdBase {
|
class KeyFrameId extends KeyFrameIdBase {
|
||||||
@override
|
@override
|
||||||
bool get canInterpolate => false;
|
bool get canInterpolate => false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void apply(Core<CoreContext> object, int propertyKey, double mix) {
|
void apply(Core<CoreContext> object, int propertyKey, double mix) {
|
||||||
RiveCoreContext.setUint(object, propertyKey, value);
|
RiveCoreContext.setUint(object, propertyKey, value);
|
||||||
|
@ -1 +1,12 @@
|
|||||||
enum KeyFrameInterpolation { hold, linear, cubic }
|
/// The type of interpolation used for a keyframe.
|
||||||
|
enum KeyFrameInterpolation {
|
||||||
|
/// Hold the incoming value until the next keyframe is reached.
|
||||||
|
hold,
|
||||||
|
|
||||||
|
/// Linearly interpolate from the incoming to the outgoing value.
|
||||||
|
linear,
|
||||||
|
|
||||||
|
/// Cubicly interpolate from incoming to outgoing value based on the
|
||||||
|
/// [CubicInterpolator]'s parameters.
|
||||||
|
cubic,
|
||||||
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:rive/src/core/core.dart';
|
import 'package:rive/src/core/core.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/state_instance.dart';
|
||||||
import 'package:rive/src/rive_core/animation/state_machine_layer.dart';
|
import 'package:rive/src/rive_core/animation/state_machine_layer.dart';
|
||||||
import 'package:rive/src/rive_core/animation/state_transition.dart';
|
import 'package:rive/src/rive_core/animation/state_transition.dart';
|
||||||
import 'package:rive/src/generated/animation/layer_state_base.dart';
|
import 'package:rive/src/generated/animation/layer_state_base.dart';
|
||||||
@ -7,11 +8,16 @@ export 'package:rive/src/generated/animation/layer_state_base.dart';
|
|||||||
abstract class LayerState extends LayerStateBase {
|
abstract class LayerState extends LayerStateBase {
|
||||||
final StateTransitions _transitions = StateTransitions();
|
final StateTransitions _transitions = StateTransitions();
|
||||||
StateTransitions get transitions => _transitions;
|
StateTransitions get transitions => _transitions;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onAdded() {}
|
void onAdded() {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onAddedDirty() {}
|
void onAddedDirty() {}
|
||||||
|
|
||||||
void internalAddTransition(StateTransition transition) {
|
void internalAddTransition(StateTransition transition) {
|
||||||
|
assert(!_transitions.contains(transition),
|
||||||
|
'shouldn\'t already contain the transition');
|
||||||
_transitions.add(transition);
|
_transitions.add(transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,6 +30,8 @@ abstract class LayerState extends LayerStateBase {
|
|||||||
super.onRemoved();
|
super.onRemoved();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StateInstance makeInstance();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool import(ImportStack stack) {
|
bool import(ImportStack stack) {
|
||||||
var importer =
|
var importer =
|
||||||
@ -32,6 +40,7 @@ abstract class LayerState extends LayerStateBase {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
importer.addState(this);
|
importer.addState(this);
|
||||||
|
|
||||||
return super.import(stack);
|
return super.import(stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
|
|
||||||
import 'package:rive/src/core/core.dart';
|
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_object.dart';
|
||||||
import 'package:rive/src/rive_core/animation/loop.dart';
|
import 'package:rive/src/rive_core/animation/loop.dart';
|
||||||
@ -7,8 +8,16 @@ import 'package:rive/src/generated/animation/linear_animation_base.dart';
|
|||||||
export 'package:rive/src/generated/animation/linear_animation_base.dart';
|
export 'package:rive/src/generated/animation/linear_animation_base.dart';
|
||||||
|
|
||||||
class LinearAnimation extends LinearAnimationBase {
|
class LinearAnimation extends LinearAnimationBase {
|
||||||
|
/// Map objectId to KeyedObject. N.B. this is the id of the object that we
|
||||||
|
/// want to key in core, not of the KeyedObject. It's a clear way to see if an
|
||||||
|
/// object is keyed in this animation.
|
||||||
final _keyedObjects = HashMap<int, KeyedObject>();
|
final _keyedObjects = HashMap<int, KeyedObject>();
|
||||||
|
|
||||||
|
/// The metadata for the objects that are keyed in this animation.
|
||||||
Iterable<KeyedObject> get keyedObjects => _keyedObjects.values;
|
Iterable<KeyedObject> get keyedObjects => _keyedObjects.values;
|
||||||
|
|
||||||
|
/// Called by rive_core to add a KeyedObject to the animation. This should be
|
||||||
|
/// @internal when it's supported.
|
||||||
bool internalAddKeyedObject(KeyedObject object) {
|
bool internalAddKeyedObject(KeyedObject object) {
|
||||||
if (internalCheckAddKeyedObject(object)) {
|
if (internalCheckAddKeyedObject(object)) {
|
||||||
_keyedObjects[object.objectId] = object;
|
_keyedObjects[object.objectId] = object;
|
||||||
@ -19,6 +28,9 @@ class LinearAnimation extends LinearAnimationBase {
|
|||||||
|
|
||||||
bool internalCheckAddKeyedObject(KeyedObject object) {
|
bool internalCheckAddKeyedObject(KeyedObject object) {
|
||||||
var value = _keyedObjects[object.objectId];
|
var value = _keyedObjects[object.objectId];
|
||||||
|
|
||||||
|
// If the object is already keyed, that's ok just make sure the KeyedObject
|
||||||
|
// matches.
|
||||||
if (value != null && value != object) {
|
if (value != null && value != object) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -29,6 +41,13 @@ class LinearAnimation extends LinearAnimationBase {
|
|||||||
double get endSeconds =>
|
double get endSeconds =>
|
||||||
(enableWorkArea ? workEnd : duration).toDouble() / fps;
|
(enableWorkArea ? workEnd : duration).toDouble() / fps;
|
||||||
double get durationSeconds => endSeconds - startSeconds;
|
double get durationSeconds => endSeconds - startSeconds;
|
||||||
|
|
||||||
|
/// Pass in a different [core] context if you want to apply the animation to a
|
||||||
|
/// different instance. This isn't meant to be used yet but left as mostly a
|
||||||
|
/// note to remember that at runtime we have to support applying animations to
|
||||||
|
/// instances. We do a nice job of not duping all that data at runtime (so
|
||||||
|
/// animations exist once but entire Rive file can be instanced multiple times
|
||||||
|
/// playing different positions).
|
||||||
void apply(double time, {required CoreContext coreContext, double mix = 1}) {
|
void apply(double time, {required CoreContext coreContext, double mix = 1}) {
|
||||||
for (final keyedObject in _keyedObjects.values) {
|
for (final keyedObject in _keyedObjects.values) {
|
||||||
keyedObject.apply(time, mix, coreContext);
|
keyedObject.apply(time, mix, coreContext);
|
||||||
@ -37,20 +56,28 @@ class LinearAnimation extends LinearAnimationBase {
|
|||||||
|
|
||||||
Loop get loop => Loop.values[loopValue];
|
Loop get loop => Loop.values[loopValue];
|
||||||
set loop(Loop value) => loopValue = value.index;
|
set loop(Loop value) => loopValue = value.index;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void durationChanged(int from, int to) {}
|
void durationChanged(int from, int to) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void enableWorkAreaChanged(bool from, bool to) {}
|
void enableWorkAreaChanged(bool from, bool to) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void fpsChanged(int from, int to) {}
|
void fpsChanged(int from, int to) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void loopValueChanged(int from, int to) {}
|
void loopValueChanged(int from, int to) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void speedChanged(double from, double to) {}
|
void speedChanged(double from, double to) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void workEndChanged(int from, int to) {}
|
void workEndChanged(int from, int to) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void workStartChanged(int from, int to) {}
|
void workStartChanged(int from, int to) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool import(ImportStack stack) {
|
bool import(ImportStack stack) {
|
||||||
var artboardImporter = stack.latest<ArtboardImporter>(ArtboardBase.typeKey);
|
var artboardImporter = stack.latest<ArtboardImporter>(ArtboardBase.typeKey);
|
||||||
@ -58,6 +85,7 @@ class LinearAnimation extends LinearAnimationBase {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
artboardImporter.addAnimation(this);
|
artboardImporter.addAnimation(this);
|
||||||
|
|
||||||
return super.import(stack);
|
return super.import(stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,47 +11,73 @@ class LinearAnimationInstance {
|
|||||||
bool get didLoop => _didLoop;
|
bool get didLoop => _didLoop;
|
||||||
double _spilledTime = 0;
|
double _spilledTime = 0;
|
||||||
double get spilledTime => _spilledTime;
|
double get spilledTime => _spilledTime;
|
||||||
|
|
||||||
double get totalTime => _totalTime;
|
double get totalTime => _totalTime;
|
||||||
double get lastTotalTime => _lastTotalTime;
|
double get lastTotalTime => _lastTotalTime;
|
||||||
|
|
||||||
LinearAnimationInstance(this.animation)
|
LinearAnimationInstance(this.animation)
|
||||||
: _time =
|
: _time =
|
||||||
(animation.enableWorkArea ? animation.workStart : 0).toDouble() /
|
(animation.enableWorkArea ? animation.workStart : 0).toDouble() /
|
||||||
animation.fps;
|
animation.fps;
|
||||||
|
|
||||||
|
/// Note that when time is set, the direction will be changed to 1
|
||||||
set time(double value) {
|
set time(double value) {
|
||||||
if (_time == value) {
|
if (_time == value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Make sure to keep last and total in relative lockstep so state machines
|
||||||
|
// can track change even when setting time.
|
||||||
var diff = _totalTime - _lastTotalTime;
|
var diff = _totalTime - _lastTotalTime;
|
||||||
_time = _totalTime = value;
|
_time = _totalTime = value;
|
||||||
_lastTotalTime = _totalTime - diff;
|
_lastTotalTime = _totalTime - diff;
|
||||||
_direction = 1;
|
_direction = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the current time position of the animation in seconds
|
||||||
double get time => _time;
|
double get time => _time;
|
||||||
|
|
||||||
|
/// Direction should only be +1 or -1
|
||||||
set direction(int value) => _direction = value == -1 ? -1 : 1;
|
set direction(int value) => _direction = value == -1 ? -1 : 1;
|
||||||
|
|
||||||
|
/// Returns the animation's play direction: 1 for forwards, -1 for backwards
|
||||||
int get direction => _direction;
|
int get direction => _direction;
|
||||||
|
|
||||||
|
/// Returns the end time of the animation in seconds
|
||||||
double get endTime =>
|
double get endTime =>
|
||||||
(animation.enableWorkArea ? animation.workEnd : animation.duration)
|
(animation.enableWorkArea ? animation.workEnd : animation.duration)
|
||||||
.toDouble() /
|
.toDouble() /
|
||||||
animation.fps;
|
animation.fps;
|
||||||
|
|
||||||
|
/// Returns the start time of the animation in seconds
|
||||||
double get startTime =>
|
double get startTime =>
|
||||||
(animation.enableWorkArea ? animation.workStart : 0).toDouble() /
|
(animation.enableWorkArea ? animation.workStart : 0).toDouble() /
|
||||||
animation.fps;
|
animation.fps;
|
||||||
|
|
||||||
double get progress => (_time - startTime) / (endTime - startTime);
|
double get progress => (_time - startTime) / (endTime - startTime);
|
||||||
|
|
||||||
|
/// Resets the animation to the starting frame
|
||||||
void reset() => _time = startTime;
|
void reset() => _time = startTime;
|
||||||
|
|
||||||
|
/// Whether the controller driving this animation should keep requesting
|
||||||
|
/// frames be drawn.
|
||||||
bool get keepGoing => animation.loop != Loop.oneShot || !_didLoop;
|
bool get keepGoing => animation.loop != Loop.oneShot || !_didLoop;
|
||||||
|
|
||||||
bool advance(double elapsedSeconds) {
|
bool advance(double elapsedSeconds) {
|
||||||
var deltaSeconds = elapsedSeconds * animation.speed * _direction;
|
var deltaSeconds = elapsedSeconds * animation.speed * _direction;
|
||||||
_lastTotalTime = _totalTime;
|
_lastTotalTime = _totalTime;
|
||||||
_totalTime += deltaSeconds;
|
_totalTime += deltaSeconds;
|
||||||
_time += deltaSeconds;
|
_time += deltaSeconds;
|
||||||
|
|
||||||
double frames = _time * animation.fps;
|
double frames = _time * animation.fps;
|
||||||
|
|
||||||
var start = animation.enableWorkArea ? animation.workStart : 0;
|
var start = animation.enableWorkArea ? animation.workStart : 0;
|
||||||
var end = animation.enableWorkArea ? animation.workEnd : animation.duration;
|
var end = animation.enableWorkArea ? animation.workEnd : animation.duration;
|
||||||
var range = end - start;
|
var range = end - start;
|
||||||
|
|
||||||
bool keepGoing = true;
|
bool keepGoing = true;
|
||||||
_didLoop = false;
|
_didLoop = false;
|
||||||
_spilledTime = 0;
|
_spilledTime = 0;
|
||||||
|
|
||||||
switch (animation.loop) {
|
switch (animation.loop) {
|
||||||
case Loop.oneShot:
|
case Loop.oneShot:
|
||||||
if (frames > end) {
|
if (frames > end) {
|
||||||
@ -72,6 +98,7 @@ class LinearAnimationInstance {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Loop.pingPong:
|
case Loop.pingPong:
|
||||||
|
// ignore: literal_only_boolean_expressions
|
||||||
while (true) {
|
while (true) {
|
||||||
if (_direction == 1 && frames >= end) {
|
if (_direction == 1 && frames >= end) {
|
||||||
_spilledTime = (frames - end) / animation.fps;
|
_spilledTime = (frames - end) / animation.fps;
|
||||||
@ -86,6 +113,11 @@ class LinearAnimationInstance {
|
|||||||
_time = frames / animation.fps;
|
_time = frames / animation.fps;
|
||||||
_didLoop = true;
|
_didLoop = true;
|
||||||
} else {
|
} else {
|
||||||
|
// we're within the range, we can stop fixing. We do this in a
|
||||||
|
// loop to fix conditions when time has advanced so far that we've
|
||||||
|
// ping-ponged back and forth a few times in a single frame. We
|
||||||
|
// want to accomodate for this in cases where animations are not
|
||||||
|
// advanced on regular intervals.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1 +1,12 @@
|
|||||||
enum Loop { oneShot, loop, pingPong }
|
/// Loop options for linear animations.
|
||||||
|
enum Loop {
|
||||||
|
/// Play until the duration or end of work area of the animation.
|
||||||
|
oneShot,
|
||||||
|
|
||||||
|
/// Play until the duration or end of work area of the animation and then go
|
||||||
|
/// back to the start (0 seconds).
|
||||||
|
loop,
|
||||||
|
|
||||||
|
/// Play to the end of the duration/work area and then play back.
|
||||||
|
pingPong,
|
||||||
|
}
|
||||||
|
33
lib/src/rive_core/animation/state_instance.dart
Normal file
33
lib/src/rive_core/animation/state_instance.dart
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import 'dart:collection';
|
||||||
|
|
||||||
|
import 'package:rive/src/core/core.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/layer_state.dart';
|
||||||
|
|
||||||
|
/// Represents the instance of a [LayerState] which is being used in a
|
||||||
|
/// [LayerController] of a [StateMachineController]. Abstract representation of
|
||||||
|
/// an Animation (for [AnimationState]) or set of Animations in the case of a
|
||||||
|
/// [BlendState].
|
||||||
|
abstract class StateInstance {
|
||||||
|
final LayerState state;
|
||||||
|
|
||||||
|
StateInstance(this.state);
|
||||||
|
|
||||||
|
void advance(double seconds, HashMap<int, dynamic> inputValues);
|
||||||
|
void apply(CoreContext core, double mix);
|
||||||
|
|
||||||
|
bool get keepGoing;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A single one of these is created per Layer which just represents/wraps the
|
||||||
|
/// AnyState but conforms to the instance interface.
|
||||||
|
class SystemStateInstance extends StateInstance {
|
||||||
|
SystemStateInstance(LayerState state) : super(state);
|
||||||
|
@override
|
||||||
|
void advance(double seconds, HashMap<int, dynamic> inputValues) {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void apply(CoreContext core, double mix) {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get keepGoing => false;
|
||||||
|
}
|
@ -10,6 +10,7 @@ class StateMachine extends StateMachineBase {
|
|||||||
StateMachineComponents<StateMachineInput>();
|
StateMachineComponents<StateMachineInput>();
|
||||||
final StateMachineComponents<StateMachineLayer> layers =
|
final StateMachineComponents<StateMachineLayer> layers =
|
||||||
StateMachineComponents<StateMachineLayer>();
|
StateMachineComponents<StateMachineLayer>();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool import(ImportStack stack) {
|
bool import(ImportStack stack) {
|
||||||
var artboardImporter = stack.latest<ArtboardImporter>(ArtboardBase.typeKey);
|
var artboardImporter = stack.latest<ArtboardImporter>(ArtboardBase.typeKey);
|
||||||
@ -17,6 +18,7 @@ class StateMachine extends StateMachineBase {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
artboardImporter.addStateMachine(this);
|
artboardImporter.addStateMachine(this);
|
||||||
|
|
||||||
return super.import(stack);
|
return super.import(stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,10 @@ export 'package:rive/src/generated/animation/state_machine_bool_base.dart';
|
|||||||
class StateMachineBool extends StateMachineBoolBase {
|
class StateMachineBool extends StateMachineBoolBase {
|
||||||
@override
|
@override
|
||||||
void valueChanged(bool from, bool to) {}
|
void valueChanged(bool from, bool to) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool isValidType<T>() => T == bool;
|
bool isValidType<T>() => T == bool;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
dynamic get controllerValue => value;
|
dynamic get controllerValue => value;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
|
|
||||||
import 'package:rive/src/core/core.dart';
|
import 'package:rive/src/core/core.dart';
|
||||||
import 'package:rive/src/rive_core/animation/state_machine.dart';
|
import 'package:rive/src/rive_core/animation/state_machine.dart';
|
||||||
import 'package:rive/src/generated/animation/state_machine_component_base.dart';
|
import 'package:rive/src/generated/animation/state_machine_component_base.dart';
|
||||||
export 'package:rive/src/generated/animation/state_machine_component_base.dart';
|
export 'package:rive/src/generated/animation/state_machine_component_base.dart';
|
||||||
|
|
||||||
|
/// Implemented by state machine inputs and layers.
|
||||||
abstract class StateMachineComponent extends StateMachineComponentBase {
|
abstract class StateMachineComponent extends StateMachineComponentBase {
|
||||||
StateMachine? _stateMachine;
|
StateMachine? _stateMachine;
|
||||||
StateMachine? get stateMachine => _stateMachine;
|
StateMachine? get stateMachine => _stateMachine;
|
||||||
@ -15,16 +17,22 @@ abstract class StateMachineComponent extends StateMachineComponentBase {
|
|||||||
machineComponentList(_stateMachine!).remove(this);
|
machineComponentList(_stateMachine!).remove(this);
|
||||||
}
|
}
|
||||||
_stateMachine = machine;
|
_stateMachine = machine;
|
||||||
|
|
||||||
if (_stateMachine != null) {
|
if (_stateMachine != null) {
|
||||||
machineComponentList(_stateMachine!).add(this);
|
machineComponentList(_stateMachine!).add(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Intentionally using ListBase instead of FractionallyIndexedList here as
|
||||||
|
// it's more compatible with runtime.
|
||||||
ListBase<StateMachineComponent> machineComponentList(StateMachine machine);
|
ListBase<StateMachineComponent> machineComponentList(StateMachine machine);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void nameChanged(String from, String to) {}
|
void nameChanged(String from, String to) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onAddedDirty() {}
|
void onAddedDirty() {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onRemoved() {
|
void onRemoved() {
|
||||||
super.onRemoved();
|
super.onRemoved();
|
||||||
@ -39,6 +47,7 @@ abstract class StateMachineComponent extends StateMachineComponentBase {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
importer.addMachineComponent(this);
|
importer.addMachineComponent(this);
|
||||||
|
|
||||||
return super.import(importStack);
|
return super.import(importStack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
|
|
||||||
import 'package:rive/src/rive_core/animation/state_machine.dart';
|
import 'package:rive/src/rive_core/animation/state_machine.dart';
|
||||||
import 'package:rive/src/rive_core/animation/state_machine_component.dart';
|
import 'package:rive/src/rive_core/animation/state_machine_component.dart';
|
||||||
import 'package:rive/src/generated/animation/state_machine_input_base.dart';
|
import 'package:rive/src/generated/animation/state_machine_input_base.dart';
|
||||||
@ -6,9 +7,11 @@ export 'package:rive/src/generated/animation/state_machine_input_base.dart';
|
|||||||
|
|
||||||
abstract class StateMachineInput extends StateMachineInputBase {
|
abstract class StateMachineInput extends StateMachineInputBase {
|
||||||
static final StateMachineInput unknown = _StateMachineUnknownInput();
|
static final StateMachineInput unknown = _StateMachineUnknownInput();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ListBase<StateMachineComponent> machineComponentList(StateMachine machine) =>
|
ListBase<StateMachineComponent> machineComponentList(StateMachine machine) =>
|
||||||
machine.inputs;
|
machine.inputs;
|
||||||
|
|
||||||
bool isValidType<T>() => false;
|
bool isValidType<T>() => false;
|
||||||
dynamic get controllerValue => null;
|
dynamic get controllerValue => null;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
|
|
||||||
import 'package:rive/src/rive_core/animation/any_state.dart';
|
import 'package:rive/src/rive_core/animation/any_state.dart';
|
||||||
import 'package:rive/src/rive_core/animation/entry_state.dart';
|
import 'package:rive/src/rive_core/animation/entry_state.dart';
|
||||||
import 'package:rive/src/rive_core/animation/exit_state.dart';
|
import 'package:rive/src/rive_core/animation/exit_state.dart';
|
||||||
@ -13,12 +14,17 @@ class StateMachineLayer extends StateMachineLayerBase {
|
|||||||
LayerState? _entryState;
|
LayerState? _entryState;
|
||||||
LayerState? _anyState;
|
LayerState? _anyState;
|
||||||
LayerState? _exitState;
|
LayerState? _exitState;
|
||||||
|
|
||||||
LayerState? get entryState => _entryState;
|
LayerState? get entryState => _entryState;
|
||||||
LayerState? get anyState => _anyState;
|
LayerState? get anyState => _anyState;
|
||||||
LayerState? get exitState => _exitState;
|
LayerState? get exitState => _exitState;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ListBase<StateMachineComponent> machineComponentList(StateMachine machine) =>
|
ListBase<StateMachineComponent> machineComponentList(StateMachine machine) =>
|
||||||
machine.layers;
|
machine.layers;
|
||||||
|
|
||||||
|
/// Called by rive_core to add a LayerState to the StateMachineLayer. This
|
||||||
|
/// should be @internal when it's supported.
|
||||||
bool internalAddState(LayerState state) {
|
bool internalAddState(LayerState state) {
|
||||||
switch (state.coreType) {
|
switch (state.coreType) {
|
||||||
case AnyStateBase.typeKey:
|
case AnyStateBase.typeKey:
|
||||||
@ -31,6 +37,7 @@ class StateMachineLayer extends StateMachineLayerBase {
|
|||||||
_entryState = state;
|
_entryState = state;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
|
// We really want this file to import core for the flutter runtime, so make the
|
||||||
|
// linter happy...
|
||||||
|
|
||||||
|
// ignore: unused_import
|
||||||
import 'package:rive/src/core/core.dart';
|
import 'package:rive/src/core/core.dart';
|
||||||
|
|
||||||
import 'package:rive/src/generated/animation/state_machine_layer_component_base.dart';
|
import 'package:rive/src/generated/animation/state_machine_layer_component_base.dart';
|
||||||
export 'package:rive/src/generated/animation/state_machine_layer_component_base.dart';
|
export 'package:rive/src/generated/animation/state_machine_layer_component_base.dart';
|
||||||
|
|
||||||
|
@ -4,8 +4,10 @@ export 'package:rive/src/generated/animation/state_machine_number_base.dart';
|
|||||||
class StateMachineNumber extends StateMachineNumberBase {
|
class StateMachineNumber extends StateMachineNumberBase {
|
||||||
@override
|
@override
|
||||||
void valueChanged(double from, double to) {}
|
void valueChanged(double from, double to) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool isValidType<T>() => T == double;
|
bool isValidType<T>() => T == double;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
dynamic get controllerValue => value;
|
dynamic get controllerValue => value;
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ export 'package:rive/src/generated/animation/state_machine_trigger_base.dart';
|
|||||||
class StateMachineTrigger extends StateMachineTriggerBase {
|
class StateMachineTrigger extends StateMachineTriggerBase {
|
||||||
bool _triggered = false;
|
bool _triggered = false;
|
||||||
bool get triggered => _triggered;
|
bool get triggered => _triggered;
|
||||||
|
|
||||||
void fire() {
|
void fire() {
|
||||||
_triggered = true;
|
_triggered = true;
|
||||||
}
|
}
|
||||||
@ -14,6 +15,7 @@ class StateMachineTrigger extends StateMachineTriggerBase {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool isValidType<T>() => T == bool;
|
bool isValidType<T>() => T == bool;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
dynamic get controllerValue => _triggered;
|
dynamic get controllerValue => _triggered;
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,40 @@
|
|||||||
|
import 'dart:collection';
|
||||||
|
|
||||||
import 'package:rive/src/core/core.dart';
|
import 'package:rive/src/core/core.dart';
|
||||||
import 'package:rive/src/rive_core/animation/animation_state.dart';
|
import 'package:rive/src/rive_core/animation/animation_state.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/animation_state_instance.dart';
|
||||||
import 'package:rive/src/rive_core/animation/layer_state.dart';
|
import 'package:rive/src/rive_core/animation/layer_state.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/linear_animation.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/linear_animation_instance.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/state_instance.dart';
|
||||||
import 'package:rive/src/rive_core/animation/transition_condition.dart';
|
import 'package:rive/src/rive_core/animation/transition_condition.dart';
|
||||||
|
import 'package:rive/src/rive_core/animation/transition_trigger_condition.dart';
|
||||||
import 'package:rive/src/generated/animation/state_transition_base.dart';
|
import 'package:rive/src/generated/animation/state_transition_base.dart';
|
||||||
import 'package:rive/src/rive_core/state_transition_flags.dart';
|
import 'package:rive/src/rive_core/state_transition_flags.dart';
|
||||||
export 'package:rive/src/generated/animation/state_transition_base.dart';
|
export 'package:rive/src/generated/animation/state_transition_base.dart';
|
||||||
|
|
||||||
|
enum AllowTransition { no, waitingForExit, yes }
|
||||||
|
|
||||||
class StateTransition extends StateTransitionBase {
|
class StateTransition extends StateTransitionBase {
|
||||||
final StateTransitionConditions conditions = StateTransitionConditions();
|
final StateTransitionConditions conditions = StateTransitionConditions();
|
||||||
LayerState? stateTo;
|
LayerState? stateTo;
|
||||||
static final StateTransition unknown = StateTransition();
|
static final StateTransition unknown = StateTransition();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool validate() {
|
bool validate() {
|
||||||
return super.validate() && stateTo != null;
|
return super.validate() &&
|
||||||
|
|
||||||
|
// need this last so runtimes get it which makes the whole
|
||||||
|
// allowTransitionFrom thing above a little weird.
|
||||||
|
stateTo != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onAdded() {}
|
void onAdded() {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onAddedDirty() {}
|
void onAddedDirty() {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onRemoved() {
|
void onRemoved() {
|
||||||
super.onRemoved();
|
super.onRemoved();
|
||||||
@ -27,6 +43,11 @@ class StateTransition extends StateTransitionBase {
|
|||||||
bool get isDisabled => (flags & StateTransitionFlags.disabled) != 0;
|
bool get isDisabled => (flags & StateTransitionFlags.disabled) != 0;
|
||||||
bool get pauseOnExit => (flags & StateTransitionFlags.pauseOnExit) != 0;
|
bool get pauseOnExit => (flags & StateTransitionFlags.pauseOnExit) != 0;
|
||||||
bool get enableExitTime => (flags & StateTransitionFlags.enableExitTime) != 0;
|
bool get enableExitTime => (flags & StateTransitionFlags.enableExitTime) != 0;
|
||||||
|
|
||||||
|
/// The amount of time to mix the outgoing animation onto the incoming one
|
||||||
|
/// when changing state. Only applies when going out from an AnimationState.
|
||||||
|
/// [stateFrom] must be provided as at runtime we don't store the reference to
|
||||||
|
/// the state this transition comes from.
|
||||||
double mixTime(LayerState stateFrom) {
|
double mixTime(LayerState stateFrom) {
|
||||||
if (duration == 0) {
|
if (duration == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -42,14 +63,30 @@ class StateTransition extends StateTransitionBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Provide the animation instance to use for computing percentage durations
|
||||||
|
/// for exit time.
|
||||||
|
LinearAnimationInstance? exitTimeAnimationInstance(StateInstance stateFrom) =>
|
||||||
|
stateFrom is AnimationStateInstance ? stateFrom.animationInstance : null;
|
||||||
|
|
||||||
|
/// Provide the animation to use for computing percentage durations for exit
|
||||||
|
/// time.
|
||||||
|
LinearAnimation? exitTimeAnimation(LayerState stateFrom) =>
|
||||||
|
stateFrom is AnimationState ? stateFrom.animation : null;
|
||||||
|
|
||||||
|
/// 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.
|
||||||
double exitTimeSeconds(LayerState stateFrom, {bool absolute = false}) {
|
double exitTimeSeconds(LayerState stateFrom, {bool absolute = false}) {
|
||||||
if ((flags & StateTransitionFlags.exitTimeIsPercentage) != 0) {
|
if ((flags & StateTransitionFlags.exitTimeIsPercentage) != 0) {
|
||||||
var animationDuration = 0.0;
|
var animationDuration = 0.0;
|
||||||
var start = 0.0;
|
var start = 0.0;
|
||||||
if (stateFrom is AnimationState) {
|
|
||||||
start = absolute ? stateFrom.animation?.startSeconds ?? 0 : 0;
|
var exitAnimation = exitTimeAnimation(stateFrom);
|
||||||
animationDuration = stateFrom.animation?.durationSeconds ?? 0;
|
if (exitAnimation != null) {
|
||||||
|
start = absolute ? exitAnimation.startSeconds : 0;
|
||||||
|
animationDuration = exitAnimation.durationSeconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
return start + exitTime / 100 * animationDuration;
|
return start + exitTime / 100 * animationDuration;
|
||||||
} else {
|
} else {
|
||||||
return exitTime / 1000;
|
return exitTime / 1000;
|
||||||
@ -64,28 +101,89 @@ class StateTransition extends StateTransitionBase {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
importer.addTransition(this);
|
importer.addTransition(this);
|
||||||
|
|
||||||
return super.import(importStack);
|
return super.import(importStack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Called by rive_core to add a [TransitionCondition] to this
|
||||||
|
/// [StateTransition]. This should be @internal when it's supported.
|
||||||
bool internalAddCondition(TransitionCondition condition) {
|
bool internalAddCondition(TransitionCondition condition) {
|
||||||
if (conditions.contains(condition)) {
|
if (conditions.contains(condition)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
conditions.add(condition);
|
conditions.add(condition);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Called by rive_core to remove a [TransitionCondition] from this
|
||||||
|
/// [StateTransition]. This should be @internal when it's supported.
|
||||||
bool internalRemoveCondition(TransitionCondition condition) {
|
bool internalRemoveCondition(TransitionCondition condition) {
|
||||||
var removed = conditions.remove(condition);
|
var removed = conditions.remove(condition);
|
||||||
|
|
||||||
return removed;
|
return removed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void flagsChanged(int from, int to) {}
|
void flagsChanged(int from, int to) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void durationChanged(int from, int to) {}
|
void durationChanged(int from, int to) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void exitTimeChanged(int from, int to) {}
|
void exitTimeChanged(int from, int to) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void stateToIdChanged(int from, int to) {}
|
void stateToIdChanged(int from, int to) {}
|
||||||
|
|
||||||
|
/// Returns true when this transition can be taken from [stateFrom] with the
|
||||||
|
/// given [inputValues].
|
||||||
|
AllowTransition allowed(StateInstance stateFrom,
|
||||||
|
HashMap<int, dynamic> inputValues, bool ignoreTriggers) {
|
||||||
|
if (isDisabled) {
|
||||||
|
return AllowTransition.no;
|
||||||
|
}
|
||||||
|
for (final condition in conditions) {
|
||||||
|
if ((ignoreTriggers && condition is TransitionTriggerCondition) ||
|
||||||
|
!condition.evaluate(inputValues)) {
|
||||||
|
return AllowTransition.no;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// For now we only enable exit time from AnimationStates, do we want to
|
||||||
|
// enable this for BlendStates? How would that work?
|
||||||
|
if (enableExitTime) {
|
||||||
|
var exitAnimation = exitTimeAnimationInstance(stateFrom);
|
||||||
|
if (exitAnimation != null) {
|
||||||
|
// 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.
|
||||||
|
var lastTime = exitAnimation.lastTotalTime;
|
||||||
|
var time = exitAnimation.totalTime;
|
||||||
|
var exitTime = exitTimeSeconds(stateFrom.state);
|
||||||
|
var animationFrom = exitAnimation.animation;
|
||||||
|
if (exitTime < animationFrom.durationSeconds) {
|
||||||
|
// Get exit time relative to the loop lastTime was in.
|
||||||
|
exitTime += (lastTime / animationFrom.durationSeconds).floor() *
|
||||||
|
animationFrom.durationSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (time < exitTime) {
|
||||||
|
return AllowTransition.waitingForExit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return AllowTransition.yes;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool applyExitCondition(StateInstance stateFrom) {
|
||||||
|
// Hold exit time when the user has set to pauseOnExit on this condition
|
||||||
|
// (only valid when exiting from an Animation).
|
||||||
|
bool useExitTime = enableExitTime && stateFrom is AnimationStateInstance;
|
||||||
|
if (pauseOnExit && useExitTime) {
|
||||||
|
stateFrom.animationInstance.time =
|
||||||
|
exitTimeSeconds(stateFrom.state, absolute: true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return useExitTime;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ export 'package:rive/src/generated/animation/transition_bool_condition_base.dart
|
|||||||
class TransitionBoolCondition extends TransitionBoolConditionBase {
|
class TransitionBoolCondition extends TransitionBoolConditionBase {
|
||||||
@override
|
@override
|
||||||
bool validate() => super.validate() && (input is StateMachineBool);
|
bool validate() => super.validate() && (input is StateMachineBool);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool evaluate(HashMap<int, dynamic> values) {
|
bool evaluate(HashMap<int, dynamic> values) {
|
||||||
if (input is! StateMachineBool) {
|
if (input is! StateMachineBool) {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
|
|
||||||
import 'package:rive/src/core/core.dart';
|
import 'package:rive/src/core/core.dart';
|
||||||
import 'package:rive/src/rive_core/animation/state_machine_input.dart';
|
import 'package:rive/src/rive_core/animation/state_machine_input.dart';
|
||||||
import 'package:rive/src/rive_core/animation/state_transition.dart';
|
import 'package:rive/src/rive_core/animation/state_transition.dart';
|
||||||
@ -11,7 +12,7 @@ enum TransitionConditionOp {
|
|||||||
lessThanOrEqual,
|
lessThanOrEqual,
|
||||||
greaterThanOrEqual,
|
greaterThanOrEqual,
|
||||||
lessThan,
|
lessThan,
|
||||||
greaterThan
|
greaterThan,
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class TransitionCondition extends TransitionConditionBase {
|
abstract class TransitionCondition extends TransitionConditionBase {
|
||||||
@ -21,7 +22,9 @@ abstract class TransitionCondition extends TransitionConditionBase {
|
|||||||
if (_input == value) {
|
if (_input == value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_input = value;
|
_input = value;
|
||||||
|
|
||||||
inputId = _input.id;
|
inputId = _input.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,12 +35,14 @@ abstract class TransitionCondition extends TransitionConditionBase {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void onAdded() {}
|
void onAdded() {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onAddedDirty() {
|
void onAddedDirty() {
|
||||||
input = context.resolveWithDefault(inputId, StateMachineInput.unknown);
|
input = context.resolveWithDefault(inputId, StateMachineInput.unknown);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool evaluate(HashMap<int, dynamic> values);
|
bool evaluate(HashMap<int, dynamic> values);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool import(ImportStack importStack) {
|
bool import(ImportStack importStack) {
|
||||||
var importer = importStack
|
var importer = importStack
|
||||||
@ -46,6 +51,7 @@ abstract class TransitionCondition extends TransitionConditionBase {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
importer.addCondition(this);
|
importer.addCondition(this);
|
||||||
|
|
||||||
return super.import(importStack);
|
return super.import(importStack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
|
|
||||||
import 'package:rive/src/rive_core/animation/state_machine_number.dart';
|
import 'package:rive/src/rive_core/animation/state_machine_number.dart';
|
||||||
import 'package:rive/src/rive_core/animation/transition_condition.dart';
|
import 'package:rive/src/rive_core/animation/transition_condition.dart';
|
||||||
import 'package:rive/src/generated/animation/transition_number_condition_base.dart';
|
import 'package:rive/src/generated/animation/transition_number_condition_base.dart';
|
||||||
@ -7,8 +8,10 @@ export 'package:rive/src/generated/animation/transition_number_condition_base.da
|
|||||||
class TransitionNumberCondition extends TransitionNumberConditionBase {
|
class TransitionNumberCondition extends TransitionNumberConditionBase {
|
||||||
@override
|
@override
|
||||||
void valueChanged(double from, double to) {}
|
void valueChanged(double from, double to) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool validate() => super.validate() && (input is StateMachineNumber);
|
bool validate() => super.validate() && (input is StateMachineNumber);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool evaluate(HashMap<int, dynamic> values) {
|
bool evaluate(HashMap<int, dynamic> values) {
|
||||||
if (input is! StateMachineNumber) {
|
if (input is! StateMachineNumber) {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
|
|
||||||
import 'package:rive/src/rive_core/animation/state_machine_trigger.dart';
|
import 'package:rive/src/rive_core/animation/state_machine_trigger.dart';
|
||||||
import 'package:rive/src/generated/animation/transition_trigger_condition_base.dart';
|
import 'package:rive/src/generated/animation/transition_trigger_condition_base.dart';
|
||||||
export 'package:rive/src/generated/animation/transition_trigger_condition_base.dart';
|
export 'package:rive/src/generated/animation/transition_trigger_condition_base.dart';
|
||||||
@ -6,6 +7,7 @@ export 'package:rive/src/generated/animation/transition_trigger_condition_base.d
|
|||||||
class TransitionTriggerCondition extends TransitionTriggerConditionBase {
|
class TransitionTriggerCondition extends TransitionTriggerConditionBase {
|
||||||
@override
|
@override
|
||||||
bool validate() => super.validate() && (input is StateMachineTrigger);
|
bool validate() => super.validate() && (input is StateMachineTrigger);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool evaluate(HashMap<int, dynamic> values) {
|
bool evaluate(HashMap<int, dynamic> values) {
|
||||||
if (input is! StateMachineTrigger) {
|
if (input is! StateMachineTrigger) {
|
||||||
@ -16,6 +18,7 @@ class TransitionTriggerCondition extends TransitionTriggerConditionBase {
|
|||||||
values[input.id] = false;
|
values[input.id] = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var triggerInput = input as StateMachineTrigger;
|
var triggerInput = input as StateMachineTrigger;
|
||||||
if (triggerInput.triggered) {
|
if (triggerInput.triggered) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -4,6 +4,9 @@ export 'package:rive/src/generated/animation/transition_value_condition_base.dar
|
|||||||
|
|
||||||
abstract class TransitionValueCondition extends TransitionValueConditionBase {
|
abstract class TransitionValueCondition extends TransitionValueConditionBase {
|
||||||
TransitionConditionOp get op => TransitionConditionOp.values[opValue];
|
TransitionConditionOp get op => TransitionConditionOp.values[opValue];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void opValueChanged(int from, int to) {}
|
void opValueChanged(int from, int to) {
|
||||||
|
// TODO: implement opValueChanged
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:rive/src/core/core.dart';
|
import 'package:rive/src/core/core.dart';
|
||||||
import 'package:rive/src/rive_core/animation/animation.dart';
|
import 'package:rive/src/rive_core/animation/animation.dart';
|
||||||
import 'package:rive/src/rive_core/component.dart';
|
import 'package:rive/src/rive_core/component.dart';
|
||||||
@ -12,34 +13,52 @@ import 'package:rive/src/rive_core/rive_animation_controller.dart';
|
|||||||
import 'package:rive/src/rive_core/shapes/paint/shape_paint_mutator.dart';
|
import 'package:rive/src/rive_core/shapes/paint/shape_paint_mutator.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/shape_paint_container.dart';
|
import 'package:rive/src/rive_core/shapes/shape_paint_container.dart';
|
||||||
import 'package:rive/src/utilities/dependency_sorter.dart';
|
import 'package:rive/src/utilities/dependency_sorter.dart';
|
||||||
|
|
||||||
import 'package:rive/src/generated/artboard_base.dart';
|
import 'package:rive/src/generated/artboard_base.dart';
|
||||||
|
|
||||||
export 'package:rive/src/generated/artboard_base.dart';
|
export 'package:rive/src/generated/artboard_base.dart';
|
||||||
|
|
||||||
class Artboard extends ArtboardBase with ShapePaintContainer {
|
class Artboard extends ArtboardBase with ShapePaintContainer {
|
||||||
|
/// Artboard are one of the few (only?) components that can be orphaned.
|
||||||
@override
|
@override
|
||||||
bool get canBeOrphaned => true;
|
bool get canBeOrphaned => true;
|
||||||
|
|
||||||
final Path path = Path();
|
final Path path = Path();
|
||||||
List<Component> _dependencyOrder = [];
|
List<Component> _dependencyOrder = [];
|
||||||
final List<Drawable> _drawables = [];
|
final List<Drawable> _drawables = [];
|
||||||
final List<DrawRules> _rules = [];
|
final List<DrawRules> _rules = [];
|
||||||
List<DrawTarget> _sortedDrawRules = [];
|
List<DrawTarget> _sortedDrawRules = [];
|
||||||
|
|
||||||
final Set<Component> _components = {};
|
final Set<Component> _components = {};
|
||||||
|
|
||||||
List<Drawable> get drawables => _drawables;
|
List<Drawable> get drawables => _drawables;
|
||||||
|
|
||||||
final AnimationList _animations = AnimationList();
|
final AnimationList _animations = AnimationList();
|
||||||
|
|
||||||
|
/// List of animations in this artboard.
|
||||||
AnimationList get animations => _animations;
|
AnimationList get animations => _animations;
|
||||||
|
|
||||||
|
/// Does this artboard have animations?
|
||||||
bool get hasAnimations => _animations.isNotEmpty;
|
bool get hasAnimations => _animations.isNotEmpty;
|
||||||
|
|
||||||
int _dirtDepth = 0;
|
int _dirtDepth = 0;
|
||||||
int _dirt = 255;
|
int _dirt = 255;
|
||||||
|
|
||||||
void forEachComponent(void Function(Component) callback) =>
|
void forEachComponent(void Function(Component) callback) =>
|
||||||
_components.forEach(callback);
|
_components.forEach(callback);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Artboard get artboard => this;
|
Artboard get artboard => this;
|
||||||
|
|
||||||
Vec2D get originWorld {
|
Vec2D get originWorld {
|
||||||
return Vec2D.fromValues(x + width * originX, y + height * originY);
|
return Vec2D.fromValues(x + width * originX, y + height * originY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Walk the dependency tree and update components in order. Returns true if
|
||||||
|
/// any component updated.
|
||||||
bool updateComponents() {
|
bool updateComponents() {
|
||||||
bool didUpdate = false;
|
bool didUpdate = false;
|
||||||
|
|
||||||
if ((_dirt & ComponentDirt.drawOrder) != 0) {
|
if ((_dirt & ComponentDirt.drawOrder) != 0) {
|
||||||
sortDrawOrder();
|
sortDrawOrder();
|
||||||
_dirt &= ~ComponentDirt.drawOrder;
|
_dirt &= ~ComponentDirt.drawOrder;
|
||||||
@ -51,6 +70,8 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
|
|||||||
int count = _dependencyOrder.length;
|
int count = _dependencyOrder.length;
|
||||||
while ((_dirt & ComponentDirt.components) != 0 && step < maxSteps) {
|
while ((_dirt & ComponentDirt.components) != 0 && step < maxSteps) {
|
||||||
_dirt &= ~ComponentDirt.components;
|
_dirt &= ~ComponentDirt.components;
|
||||||
|
// Track dirt depth here so that if something else marks
|
||||||
|
// dirty, we restart.
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
Component component = _dependencyOrder[i];
|
Component component = _dependencyOrder[i];
|
||||||
_dirtDepth = i;
|
_dirtDepth = i;
|
||||||
@ -71,6 +92,7 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
|
|||||||
return didUpdate;
|
return didUpdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update any dirty components in this artboard.
|
||||||
bool advance(double elapsedSeconds) {
|
bool advance(double elapsedSeconds) {
|
||||||
bool didUpdate = false;
|
bool didUpdate = false;
|
||||||
for (final controller in _animationControllers) {
|
for (final controller in _animationControllers) {
|
||||||
@ -93,6 +115,10 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
|
|||||||
context.markNeedsAdvance();
|
context.markNeedsAdvance();
|
||||||
_dirt |= ComponentDirt.components;
|
_dirt |= ComponentDirt.components;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If the order of the component is less than the current dirt depth,
|
||||||
|
/// update the dirt depth so that the update loop can break out early and
|
||||||
|
/// re-run (something up the tree is dirty).
|
||||||
if (component.graphOrder < _dirtDepth) {
|
if (component.graphOrder < _dirtDepth) {
|
||||||
_dirtDepth = component.graphOrder;
|
_dirtDepth = component.graphOrder;
|
||||||
}
|
}
|
||||||
@ -100,17 +126,24 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool resolveArtboard() => true;
|
bool resolveArtboard() => true;
|
||||||
|
|
||||||
|
/// Sort the DAG for resolution in order of dependencies such that dependent
|
||||||
|
/// compnents process after their dependencies.
|
||||||
void sortDependencies() {
|
void sortDependencies() {
|
||||||
var optimistic = DependencySorter<Component>();
|
var optimistic = DependencySorter<Component>();
|
||||||
var order = optimistic.sort(this);
|
var order = optimistic.sort(this);
|
||||||
if (order.isEmpty) {
|
if (order.isEmpty) {
|
||||||
|
// cycle detected, use a more robust solver
|
||||||
var robust = TarjansDependencySorter<Component>();
|
var robust = TarjansDependencySorter<Component>();
|
||||||
order = robust.sort(this);
|
order = robust.sort(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
_dependencyOrder = order;
|
_dependencyOrder = order;
|
||||||
for (final component in _dependencyOrder) {
|
for (final component in _dependencyOrder) {
|
||||||
component.graphOrder = graphOrder++;
|
component.graphOrder = graphOrder++;
|
||||||
|
// component.dirt = 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
_dirt |= ComponentDirt.components;
|
_dirt |= ComponentDirt.components;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,16 +178,23 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
|
|||||||
return Vec2D.add(Vec2D(), worldTranslation, wt);
|
return Vec2D.add(Vec2D(), worldTranslation, wt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a component to the artboard. Good place for the artboard to check for
|
||||||
|
/// components it'll later need to do stuff with (like draw them or sort them
|
||||||
|
/// when the draw order changes).
|
||||||
void addComponent(Component component) {
|
void addComponent(Component component) {
|
||||||
if (!_components.add(component)) {
|
if (!_components.add(component)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Remove a component from the artboard and its various tracked lists of
|
||||||
|
/// components.
|
||||||
void removeComponent(Component component) {
|
void removeComponent(Component component) {
|
||||||
_components.remove(component);
|
_components.remove(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Let the artboard know that the drawables need to be resorted before
|
||||||
|
/// drawing next.
|
||||||
void markDrawOrderDirty() {
|
void markDrawOrderDirty() {
|
||||||
if ((dirt & ComponentDirt.drawOrder) == 0) {
|
if ((dirt & ComponentDirt.drawOrder) == 0) {
|
||||||
context.markNeedsAdvance();
|
context.markNeedsAdvance();
|
||||||
@ -162,13 +202,25 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Draw the drawable components in this artboard.
|
||||||
void draw(Canvas canvas) {
|
void draw(Canvas canvas) {
|
||||||
canvas.save();
|
canvas.save();
|
||||||
canvas.clipRect(Rect.fromLTWH(0, 0, width, height));
|
canvas.clipRect(Rect.fromLTWH(0, 0, width, height));
|
||||||
|
// Get into artboard's world space. This is because the artboard draws
|
||||||
|
// components in the artboard's space (in component lingo we call this world
|
||||||
|
// space). The artboards themselves are drawn in the editor's world space,
|
||||||
|
// which is the world space that is used by stageItems. This is a little
|
||||||
|
// confusing and perhaps we should find a better wording for the transform
|
||||||
|
// spaces. We used "world space" in components as that's the game engine
|
||||||
|
// ratified way of naming the top-most transformation. Perhaps we should
|
||||||
|
// rename those to artboardTransform and worldTransform is only reserved for
|
||||||
|
// stageItems? The other option is to stick with 'worldTransform' in
|
||||||
|
// components and use 'editor or stageTransform' for stageItems.
|
||||||
canvas.translate(width * originX, height * originY);
|
canvas.translate(width * originX, height * originY);
|
||||||
for (final fill in fills) {
|
for (final fill in fills) {
|
||||||
fill.draw(canvas, path);
|
fill.draw(canvas, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var drawable = _firstDrawable;
|
for (var drawable = _firstDrawable;
|
||||||
drawable != null;
|
drawable != null;
|
||||||
drawable = drawable.prev) {
|
drawable = drawable.prev) {
|
||||||
@ -180,8 +232,10 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
|
|||||||
canvas.restore();
|
canvas.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Our world transform is always the identity. Artboard defines world space.
|
||||||
@override
|
@override
|
||||||
Mat2D get worldTransform => Mat2D();
|
Mat2D get worldTransform => Mat2D();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void originXChanged(double from, double to) {
|
void originXChanged(double from, double to) {
|
||||||
addDirt(ComponentDirt.worldTransform);
|
addDirt(ComponentDirt.worldTransform);
|
||||||
@ -192,20 +246,31 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
|
|||||||
addDirt(ComponentDirt.worldTransform);
|
addDirt(ComponentDirt.worldTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Called by rive_core to add an Animation to an Artboard. This should be
|
||||||
|
/// @internal when it's supported.
|
||||||
bool internalAddAnimation(Animation animation) {
|
bool internalAddAnimation(Animation animation) {
|
||||||
if (_animations.contains(animation)) {
|
if (_animations.contains(animation)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
_animations.add(animation);
|
_animations.add(animation);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Called by rive_core to remove an Animation from an Artboard. This should
|
||||||
|
/// be @internal when it's supported.
|
||||||
bool internalRemoveAnimation(Animation animation) {
|
bool internalRemoveAnimation(Animation animation) {
|
||||||
bool removed = _animations.remove(animation);
|
bool removed = _animations.remove(animation);
|
||||||
|
|
||||||
return removed;
|
return removed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The animation controllers that are called back whenever the artboard
|
||||||
|
/// advances.
|
||||||
final Set<RiveAnimationController> _animationControllers = {};
|
final Set<RiveAnimationController> _animationControllers = {};
|
||||||
|
|
||||||
|
/// Add an animation controller to this artboard. Playing will be scheduled if
|
||||||
|
/// it's already playing.
|
||||||
bool addController(RiveAnimationController controller) {
|
bool addController(RiveAnimationController controller) {
|
||||||
if (_animationControllers.contains(controller) ||
|
if (_animationControllers.contains(controller) ||
|
||||||
!controller.init(context)) {
|
!controller.init(context)) {
|
||||||
@ -219,6 +284,7 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Remove an animation controller form this artboard.
|
||||||
bool removeController(RiveAnimationController controller) {
|
bool removeController(RiveAnimationController controller) {
|
||||||
if (_animationControllers.remove(controller)) {
|
if (_animationControllers.remove(controller)) {
|
||||||
controller.isActiveChanged.removeListener(_onControllerPlayingChanged);
|
controller.isActiveChanged.removeListener(_onControllerPlayingChanged);
|
||||||
@ -229,25 +295,37 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _onControllerPlayingChanged() => context.markNeedsAdvance();
|
void _onControllerPlayingChanged() => context.markNeedsAdvance();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onFillsChanged() {}
|
void onFillsChanged() {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onPaintMutatorChanged(ShapePaintMutator mutator) {}
|
void onPaintMutatorChanged(ShapePaintMutator mutator) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onStrokesChanged() {}
|
void onStrokesChanged() {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Vec2D get worldTranslation => Vec2D();
|
Vec2D get worldTranslation => Vec2D();
|
||||||
|
|
||||||
Drawable? _firstDrawable;
|
Drawable? _firstDrawable;
|
||||||
|
|
||||||
void computeDrawOrder() {
|
void computeDrawOrder() {
|
||||||
_drawables.clear();
|
_drawables.clear();
|
||||||
_rules.clear();
|
_rules.clear();
|
||||||
buildDrawOrder(_drawables, null, _rules);
|
buildDrawOrder(_drawables, null, _rules);
|
||||||
|
|
||||||
|
// Build rule dependencies. In practice this'll need to happen anytime a
|
||||||
|
// target drawable is changed or rule is added/removed.
|
||||||
var root = DrawTarget();
|
var root = DrawTarget();
|
||||||
|
// Make sure all dependents are empty.
|
||||||
for (final nodeRules in _rules) {
|
for (final nodeRules in _rules) {
|
||||||
for (final target in nodeRules.targets) {
|
for (final target in nodeRules.targets) {
|
||||||
target.dependents.clear();
|
target.dependents.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now build up the dependencies.
|
||||||
for (final nodeRules in _rules) {
|
for (final nodeRules in _rules) {
|
||||||
for (final target in nodeRules.targets) {
|
for (final target in nodeRules.targets) {
|
||||||
root.dependents.add(target);
|
root.dependents.add(target);
|
||||||
@ -259,19 +337,25 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var sorter = DependencySorter<Component>();
|
var sorter = DependencySorter<Component>();
|
||||||
|
|
||||||
_sortedDrawRules = sorter.sort(root).cast<DrawTarget>().skip(1).toList();
|
_sortedDrawRules = sorter.sort(root).cast<DrawTarget>().skip(1).toList();
|
||||||
|
|
||||||
sortDrawOrder();
|
sortDrawOrder();
|
||||||
}
|
}
|
||||||
|
|
||||||
void sortDrawOrder() {
|
void sortDrawOrder() {
|
||||||
|
// Clear out rule first/last items.
|
||||||
for (final rule in _sortedDrawRules) {
|
for (final rule in _sortedDrawRules) {
|
||||||
rule.first = rule.last = null;
|
rule.first = rule.last = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_firstDrawable = null;
|
_firstDrawable = null;
|
||||||
Drawable? lastDrawable;
|
Drawable? lastDrawable;
|
||||||
for (final drawable in _drawables) {
|
for (final drawable in _drawables) {
|
||||||
var rules = drawable.flattenedDrawRules;
|
var rules = drawable.flattenedDrawRules;
|
||||||
|
|
||||||
var target = rules?.activeTarget;
|
var target = rules?.activeTarget;
|
||||||
if (target != null) {
|
if (target != null) {
|
||||||
if (target.first == null) {
|
if (target.first == null) {
|
||||||
@ -294,6 +378,7 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final rule in _sortedDrawRules) {
|
for (final rule in _sortedDrawRules) {
|
||||||
if (rule.first == null) {
|
if (rule.first == null) {
|
||||||
continue;
|
continue;
|
||||||
@ -323,6 +408,7 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_firstDrawable = lastDrawable;
|
_firstDrawable = lastDrawable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,10 @@ export 'package:rive/src/generated/backboard_base.dart';
|
|||||||
|
|
||||||
class Backboard extends BackboardBase {
|
class Backboard extends BackboardBase {
|
||||||
static final Backboard unknown = Backboard();
|
static final Backboard unknown = Backboard();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onAdded() {}
|
void onAdded() {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onAddedDirty() {}
|
void onAddedDirty() {}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,9 @@ class Bone extends BoneBase {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Iterate through the child bones. [BoneCallback] returns false if iteration
|
||||||
|
/// can stop. Returns false if iteration stopped, true if it made it through
|
||||||
|
/// the whole list.
|
||||||
bool forEachBone(BoneCallback callback) {
|
bool forEachBone(BoneCallback callback) {
|
||||||
for (final child in children) {
|
for (final child in children) {
|
||||||
if (child.coreType == BoneBase.typeKey) {
|
if (child.coreType == BoneBase.typeKey) {
|
||||||
@ -35,6 +38,7 @@ class Bone extends BoneBase {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
double get x => (parent as Bone).length;
|
double get x => (parent as Bone).length;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
set x(double value) {
|
set x(double value) {
|
||||||
throw UnsupportedError('not expected to set x on a bone.');
|
throw UnsupportedError('not expected to set x on a bone.');
|
||||||
@ -42,6 +46,7 @@ class Bone extends BoneBase {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
double get y => 0;
|
double get y => 0;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
set y(double value) {
|
set y(double value) {
|
||||||
throw UnsupportedError('not expected to set y on a bone.');
|
throw UnsupportedError('not expected to set y on a bone.');
|
||||||
@ -49,6 +54,9 @@ class Bone extends BoneBase {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool validate() {
|
bool validate() {
|
||||||
|
// Bones are only valid if they're parented to other bones. RootBones are a
|
||||||
|
// special case, but they inherit from bone so we check the concrete type
|
||||||
|
// here to make sure we evalute this check only for non-root bones.
|
||||||
return super.validate() && (coreType != BoneBase.typeKey || parent is Bone);
|
return super.validate() && (coreType != BoneBase.typeKey || parent is Bone);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,12 +5,16 @@ export 'package:rive/src/generated/bones/cubic_weight_base.dart';
|
|||||||
class CubicWeight extends CubicWeightBase {
|
class CubicWeight extends CubicWeightBase {
|
||||||
final Vec2D inTranslation = Vec2D();
|
final Vec2D inTranslation = Vec2D();
|
||||||
final Vec2D outTranslation = Vec2D();
|
final Vec2D outTranslation = Vec2D();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void inIndicesChanged(int from, int to) {}
|
void inIndicesChanged(int from, int to) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void inValuesChanged(int from, int to) {}
|
void inValuesChanged(int from, int to) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void outIndicesChanged(int from, int to) {}
|
void outIndicesChanged(int from, int to) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void outValuesChanged(int from, int to) {}
|
void outValuesChanged(int from, int to) {}
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,37 @@
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:rive/src/rive_core/bones/skinnable.dart';
|
import 'package:rive/src/rive_core/bones/skinnable.dart';
|
||||||
import 'package:rive/src/rive_core/bones/tendon.dart';
|
import 'package:rive/src/rive_core/bones/tendon.dart';
|
||||||
import 'package:rive/src/rive_core/component.dart';
|
import 'package:rive/src/rive_core/component.dart';
|
||||||
import 'package:rive/src/rive_core/math/mat2d.dart';
|
import 'package:rive/src/rive_core/math/mat2d.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/path_vertex.dart';
|
import 'package:rive/src/rive_core/shapes/path_vertex.dart';
|
||||||
|
|
||||||
import 'package:rive/src/generated/bones/skin_base.dart';
|
import 'package:rive/src/generated/bones/skin_base.dart';
|
||||||
export 'package:rive/src/generated/bones/skin_base.dart';
|
export 'package:rive/src/generated/bones/skin_base.dart';
|
||||||
|
|
||||||
|
/// Represents a skin deformation of either a Path or an Image Mesh connected to
|
||||||
|
/// a set of bones.
|
||||||
class Skin extends SkinBase {
|
class Skin extends SkinBase {
|
||||||
final List<Tendon> _tendons = [];
|
final List<Tendon> _tendons = [];
|
||||||
List<Tendon> get tendons => _tendons;
|
List<Tendon> get tendons => _tendons;
|
||||||
Float32List _boneTransforms = Float32List(0);
|
Float32List _boneTransforms = Float32List(0);
|
||||||
final Mat2D _worldTransform = Mat2D();
|
final Mat2D _worldTransform = Mat2D();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onDirty(int mask) {
|
void onDirty(int mask) {
|
||||||
|
// When the skin is dirty the deformed skinnable will need to regenerate its
|
||||||
|
// drawing commands.
|
||||||
|
|
||||||
|
// TODO: rename path to topology/surface something common between path &
|
||||||
|
// mesh.
|
||||||
(parent as Skinnable).markSkinDirty();
|
(parent as Skinnable).markSkinDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void update(int dirt) {
|
void update(int dirt) {
|
||||||
|
// Any dirt here indicates that the transforms needs to be rebuilt. This
|
||||||
|
// should only be worldTransform from the bones (recursively passed down) or
|
||||||
|
// ComponentDirt.path from the PointsPath (set explicitly).
|
||||||
var size = (_tendons.length + 1) * 6;
|
var size = (_tendons.length + 1) * 6;
|
||||||
if (_boneTransforms.length != size) {
|
if (_boneTransforms.length != size) {
|
||||||
_boneTransforms = Float32List(size);
|
_boneTransforms = Float32List(size);
|
||||||
@ -29,6 +42,7 @@ class Skin extends SkinBase {
|
|||||||
_boneTransforms[4] = 0;
|
_boneTransforms[4] = 0;
|
||||||
_boneTransforms[5] = 0;
|
_boneTransforms[5] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
var temp = Mat2D();
|
var temp = Mat2D();
|
||||||
var bidx = 6;
|
var bidx = 6;
|
||||||
for (final tendon in _tendons) {
|
for (final tendon in _tendons) {
|
||||||
@ -79,6 +93,8 @@ class Skin extends SkinBase {
|
|||||||
@override
|
@override
|
||||||
void buildDependencies() {
|
void buildDependencies() {
|
||||||
super.buildDependencies();
|
super.buildDependencies();
|
||||||
|
// A skin depends on all its bones. N.B. that we don't depend on the parent
|
||||||
|
// skinnable. The skinnable depends on us.
|
||||||
for (final tendon in _tendons) {
|
for (final tendon in _tendons) {
|
||||||
tendon.bone?.addDependent(this);
|
tendon.bone?.addDependent(this);
|
||||||
}
|
}
|
||||||
@ -110,6 +126,7 @@ class Skin extends SkinBase {
|
|||||||
markRebuildDependencies();
|
markRebuildDependencies();
|
||||||
}
|
}
|
||||||
parent?.markRebuildDependencies();
|
parent?.markRebuildDependencies();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,27 @@
|
|||||||
import 'package:rive/src/rive_core/bones/skin.dart';
|
import 'package:rive/src/rive_core/bones/skin.dart';
|
||||||
import 'package:rive/src/rive_core/component.dart';
|
import 'package:rive/src/rive_core/component.dart';
|
||||||
|
|
||||||
|
/// An abstraction to give a common interface to any container component that
|
||||||
|
/// can contain a skin to bind bones to.
|
||||||
abstract class Skinnable {
|
abstract class Skinnable {
|
||||||
|
// _skin is null when this object isn't connected to bones.
|
||||||
Skin? _skin;
|
Skin? _skin;
|
||||||
Skin? get skin => _skin;
|
Skin? get skin => _skin;
|
||||||
|
|
||||||
void appendChild(Component child);
|
void appendChild(Component child);
|
||||||
|
|
||||||
|
// ignore: use_setters_to_change_properties
|
||||||
void addSkin(Skin skin) {
|
void addSkin(Skin skin) {
|
||||||
|
// Notify old skin/maybe support multiple skins in the future?
|
||||||
_skin = skin;
|
_skin = skin;
|
||||||
|
|
||||||
markSkinDirty();
|
markSkinDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeSkin(Skin skin) {
|
void removeSkin(Skin skin) {
|
||||||
if (_skin == skin) {
|
if (_skin == skin) {
|
||||||
_skin = null;
|
_skin = null;
|
||||||
|
|
||||||
markSkinDirty();
|
markSkinDirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ class Tendon extends TendonBase {
|
|||||||
Mat2D? _inverseBind;
|
Mat2D? _inverseBind;
|
||||||
SkeletalComponent? _bone;
|
SkeletalComponent? _bone;
|
||||||
SkeletalComponent? get bone => _bone;
|
SkeletalComponent? get bone => _bone;
|
||||||
|
|
||||||
Mat2D get inverseBind {
|
Mat2D get inverseBind {
|
||||||
if (_inverseBind == null) {
|
if (_inverseBind == null) {
|
||||||
_inverseBind = Mat2D();
|
_inverseBind = Mat2D();
|
||||||
@ -17,11 +18,16 @@ class Tendon extends TendonBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void boneIdChanged(int from, int to) {}
|
void boneIdChanged(int from, int to) {
|
||||||
|
// This never happens, or at least it should only happen prior to an
|
||||||
|
// onAddedDirty call.
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onAddedDirty() {
|
void onAddedDirty() {
|
||||||
super.onAddedDirty();
|
super.onAddedDirty();
|
||||||
_bone = context.resolve(boneId);
|
_bone = context.resolve(boneId);
|
||||||
|
|
||||||
_bind[0] = xx;
|
_bind[0] = xx;
|
||||||
_bind[1] = xy;
|
_bind[1] = xy;
|
||||||
_bind[2] = yx;
|
_bind[2] = yx;
|
||||||
@ -32,6 +38,7 @@ class Tendon extends TendonBase {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void update(int dirt) {}
|
void update(int dirt) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void txChanged(double from, double to) {
|
void txChanged(double from, double to) {
|
||||||
_bind[4] = to;
|
_bind[4] = to;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:rive/src/rive_core/math/mat2d.dart';
|
import 'package:rive/src/rive_core/math/mat2d.dart';
|
||||||
import 'package:rive/src/rive_core/math/vec2d.dart';
|
import 'package:rive/src/rive_core/math/vec2d.dart';
|
||||||
import 'package:rive/src/generated/bones/weight_base.dart';
|
import 'package:rive/src/generated/bones/weight_base.dart';
|
||||||
@ -6,12 +7,18 @@ export 'package:rive/src/generated/bones/weight_base.dart';
|
|||||||
|
|
||||||
class Weight extends WeightBase {
|
class Weight extends WeightBase {
|
||||||
final Vec2D translation = Vec2D();
|
final Vec2D translation = Vec2D();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void indicesChanged(int from, int to) {}
|
void indicesChanged(int from, int to) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void update(int dirt) {}
|
void update(int dirt) {
|
||||||
|
// Intentionally empty. Weights don't update.
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void valuesChanged(int from, int to) {}
|
void valuesChanged(int from, int to) {}
|
||||||
|
|
||||||
static void deform(double x, double y, int indices, int weights, Mat2D world,
|
static void deform(double x, double y, int indices, int weights, Mat2D world,
|
||||||
Float32List boneTransforms, Vec2D result) {
|
Float32List boneTransforms, Vec2D result) {
|
||||||
double xx = 0, xy = 0, yx = 0, yy = 0, tx = 0, ty = 0;
|
double xx = 0, xy = 0, yx = 0, yy = 0, tx = 0, ty = 0;
|
||||||
@ -22,6 +29,7 @@ class Weight extends WeightBase {
|
|||||||
if (weight == 0) {
|
if (weight == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
double normalizedWeight = weight / 255;
|
double normalizedWeight = weight / 255;
|
||||||
var index = encodedWeightValue(i, indices);
|
var index = encodedWeightValue(i, indices);
|
||||||
var startBoneTransformIndex = index * 6;
|
var startBoneTransformIndex = index * 6;
|
||||||
|
@ -1,19 +1,31 @@
|
|||||||
|
/// Helper to abstract changing weighted values on a vertex.
|
||||||
abstract class WeightedVertex {
|
abstract class WeightedVertex {
|
||||||
int get weights;
|
int get weights;
|
||||||
int get weightIndices;
|
int get weightIndices;
|
||||||
set weights(int value);
|
set weights(int value);
|
||||||
set weightIndices(int value);
|
set weightIndices(int value);
|
||||||
|
|
||||||
|
/// Set the weight of this vertex for a specific tendon.
|
||||||
void setWeight(int tendonIndex, int tendonCount, double weight) {
|
void setWeight(int tendonIndex, int tendonCount, double weight) {
|
||||||
int tendonWeightIndex =
|
int tendonWeightIndex =
|
||||||
_setTendonWeight(tendonIndex, (weight.clamp(0, 1) * 255).round());
|
_setTendonWeight(tendonIndex, (weight.clamp(0, 1) * 255).round());
|
||||||
|
|
||||||
|
// re-normalize the list such that only bones with value are at the
|
||||||
|
// start and they sum to 100%, if any need to change make sure to give
|
||||||
|
// priority (not change) tendonIndex which we just tried to set.
|
||||||
|
|
||||||
var tendonWeights = _tendonWeights;
|
var tendonWeights = _tendonWeights;
|
||||||
int totalWeight = tendonWeights.fold(
|
int totalWeight = tendonWeights.fold(
|
||||||
0, (value, tendonWeight) => value + tendonWeight.weight);
|
0, (value, tendonWeight) => value + tendonWeight.weight);
|
||||||
var vertexTendons =
|
var vertexTendons =
|
||||||
tendonWeights.where((tendonWeight) => tendonWeight.tendon != 0);
|
tendonWeights.where((tendonWeight) => tendonWeight.tendon != 0);
|
||||||
|
|
||||||
const maxWeight = 255;
|
const maxWeight = 255;
|
||||||
|
|
||||||
var remainder = maxWeight - totalWeight;
|
var remainder = maxWeight - totalWeight;
|
||||||
if (vertexTendons.length == 1) {
|
if (vertexTendons.length == 1) {
|
||||||
|
// User is specifically setting a single tendon to a value, just pick
|
||||||
|
// the next one up (modulate by the total number of tendons).
|
||||||
var patchTendonIndex = (tendonIndex + 1) % tendonCount;
|
var patchTendonIndex = (tendonIndex + 1) % tendonCount;
|
||||||
_setTendonWeight(
|
_setTendonWeight(
|
||||||
patchTendonIndex, tendonCount == 1 ? maxWeight : remainder);
|
patchTendonIndex, tendonCount == 1 ? maxWeight : remainder);
|
||||||
@ -40,6 +52,8 @@ abstract class WeightedVertex {
|
|||||||
|
|
||||||
void _sortWeights() {
|
void _sortWeights() {
|
||||||
var tendonWeights = _tendonWeights;
|
var tendonWeights = _tendonWeights;
|
||||||
|
// Sort weights such that tendons with value show up first and any with no
|
||||||
|
// value (0 weight) are cleared to the 0 (no) tendon.
|
||||||
tendonWeights.sort((a, b) => b.weight.compareTo(a.weight));
|
tendonWeights.sort((a, b) => b.weight.compareTo(a.weight));
|
||||||
for (int i = 0; i < tendonWeights.length; i++) {
|
for (int i = 0; i < tendonWeights.length; i++) {
|
||||||
final tw = tendonWeights[i];
|
final tw = tendonWeights[i];
|
||||||
@ -54,14 +68,16 @@ abstract class WeightedVertex {
|
|||||||
_WeightHelper(2, (weightIndices >> 16) & 0xFF, _getRawWeight(2)),
|
_WeightHelper(2, (weightIndices >> 16) & 0xFF, _getRawWeight(2)),
|
||||||
_WeightHelper(3, (weightIndices >> 24) & 0xFF, _getRawWeight(3))
|
_WeightHelper(3, (weightIndices >> 24) & 0xFF, _getRawWeight(3))
|
||||||
];
|
];
|
||||||
|
|
||||||
int _setTendonWeight(int tendonIndex, int weight) {
|
int _setTendonWeight(int tendonIndex, int weight) {
|
||||||
var indices = weightIndices;
|
var indices = weightIndices;
|
||||||
var bonesIndices = [
|
var bonesIndices = [
|
||||||
indices & 0xFF,
|
indices & 0xFF,
|
||||||
(indices >> 8) & 0xFF,
|
(indices >> 8) & 0xFF,
|
||||||
(indices >> 16) & 0xFF,
|
(indices >> 16) & 0xFF,
|
||||||
(indices >> 24) & 0xFF
|
(indices >> 24) & 0xFF,
|
||||||
];
|
];
|
||||||
|
|
||||||
int setWeightIndex = -1;
|
int setWeightIndex = -1;
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
if (bonesIndices[i] == tendonIndex + 1) {
|
if (bonesIndices[i] == tendonIndex + 1) {
|
||||||
@ -70,10 +86,14 @@ abstract class WeightedVertex {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This bone wasn't weighted for this vertex, go find the bone with the
|
||||||
|
// least weight (or a 0 bone) and use it.
|
||||||
if (setWeightIndex == -1) {
|
if (setWeightIndex == -1) {
|
||||||
int lowestWeight = double.maxFinite.toInt();
|
int lowestWeight = double.maxFinite.toInt();
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
if (bonesIndices[i] == 0) {
|
if (bonesIndices[i] == 0) {
|
||||||
|
// this isn't set to a bone yet, use it!
|
||||||
setWeightIndex = i;
|
setWeightIndex = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -83,16 +103,21 @@ abstract class WeightedVertex {
|
|||||||
lowestWeight = weight;
|
lowestWeight = weight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_setTendonIndex(setWeightIndex, tendonIndex + 1);
|
_setTendonIndex(setWeightIndex, tendonIndex + 1);
|
||||||
_rawSetWeight(setWeightIndex, weight);
|
_rawSetWeight(setWeightIndex, weight);
|
||||||
}
|
}
|
||||||
return setWeightIndex;
|
return setWeightIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// [tendonIndex] of 0 means no bound tendon, when bound to an actual tendon,
|
||||||
|
/// it should be set to the skin's tendon's index + 1.
|
||||||
void _setTendonIndex(int weightIndex, int tendonIndex) {
|
void _setTendonIndex(int weightIndex, int tendonIndex) {
|
||||||
assert(weightIndex < 4 && weightIndex >= 0);
|
assert(weightIndex < 4 && weightIndex >= 0);
|
||||||
var indexValues = weightIndices;
|
var indexValues = weightIndices;
|
||||||
|
// Clear the bits for this weight value.
|
||||||
indexValues &= ~(0xFF << (weightIndex * 8));
|
indexValues &= ~(0xFF << (weightIndex * 8));
|
||||||
|
// Set the bits for this weight value.
|
||||||
weightIndices = indexValues | (tendonIndex << (weightIndex * 8));
|
weightIndices = indexValues | (tendonIndex << (weightIndex * 8));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,11 +129,14 @@ abstract class WeightedVertex {
|
|||||||
void _rawSetWeight(int weightIndex, int weightValue) {
|
void _rawSetWeight(int weightIndex, int weightValue) {
|
||||||
assert(weightIndex < 4 && weightIndex >= 0);
|
assert(weightIndex < 4 && weightIndex >= 0);
|
||||||
var weightValues = weights;
|
var weightValues = weights;
|
||||||
|
// Clear the bits for this weight value.
|
||||||
weightValues &= ~(0xFF << (weightIndex * 8));
|
weightValues &= ~(0xFF << (weightIndex * 8));
|
||||||
|
// Set the bits for this weight value.
|
||||||
weights = weightValues | (weightValue << (weightIndex * 8));
|
weights = weightValues | (weightValue << (weightIndex * 8));
|
||||||
}
|
}
|
||||||
|
|
||||||
int _getRawWeight(int weightIndex) => (weights >> (weightIndex * 8)) & 0xFF;
|
int _getRawWeight(int weightIndex) => (weights >> (weightIndex * 8)) & 0xFF;
|
||||||
|
|
||||||
double getWeight(int tendonIndex) {
|
double getWeight(int tendonIndex) {
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
if (getTendon(i) == tendonIndex + 1) {
|
if (getTendon(i) == tendonIndex + 1) {
|
||||||
@ -123,5 +151,6 @@ class _WeightHelper {
|
|||||||
final int index;
|
final int index;
|
||||||
final int tendon;
|
final int tendon;
|
||||||
int weight;
|
int weight;
|
||||||
|
|
||||||
_WeightHelper(this.index, this.tendon, this.weight);
|
_WeightHelper(this.index, this.tendon, this.weight);
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import 'package:rive/src/core/core.dart';
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:rive/src/rive_core/artboard.dart';
|
import 'package:rive/src/rive_core/artboard.dart';
|
||||||
import 'package:rive/src/rive_core/container_component.dart';
|
import 'package:rive/src/rive_core/container_component.dart';
|
||||||
|
|
||||||
import 'package:rive/src/generated/component_base.dart';
|
import 'package:rive/src/generated/component_base.dart';
|
||||||
import 'package:rive/src/utilities/dependency_sorter.dart';
|
import 'package:rive/src/utilities/dependency_sorter.dart';
|
||||||
import 'package:rive/src/utilities/tops.dart';
|
import 'package:rive/src/utilities/tops.dart';
|
||||||
@ -11,20 +12,36 @@ abstract class Component extends ComponentBase<RuntimeArtboard>
|
|||||||
implements DependencyGraphNode<Component>, Parentable<Component> {
|
implements DependencyGraphNode<Component>, Parentable<Component> {
|
||||||
Artboard? _artboard;
|
Artboard? _artboard;
|
||||||
dynamic _userData;
|
dynamic _userData;
|
||||||
|
|
||||||
|
/// Override to true if you want some object inheriting from Component to not
|
||||||
|
/// have a parent. Most objects will validate that they have a parent during
|
||||||
|
/// the onAdded callback otherwise they are considered invalid and are culled
|
||||||
|
/// from core.
|
||||||
bool get canBeOrphaned => false;
|
bool get canBeOrphaned => false;
|
||||||
|
|
||||||
|
// Used during update process.
|
||||||
int graphOrder = 0;
|
int graphOrder = 0;
|
||||||
int dirt = 0xFFFF;
|
int dirt = 0xFFFF;
|
||||||
|
|
||||||
|
// This is really only for sanity and earlying out of recursive loops.
|
||||||
static const int maxTreeDepth = 5000;
|
static const int maxTreeDepth = 5000;
|
||||||
|
|
||||||
bool addDirt(int value, {bool recurse = false}) {
|
bool addDirt(int value, {bool recurse = false}) {
|
||||||
if ((dirt & value) == value) {
|
if ((dirt & value) == value) {
|
||||||
|
// Already marked.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure dirt is set before calling anything that can set more dirt.
|
||||||
dirt |= value;
|
dirt |= value;
|
||||||
|
|
||||||
onDirty(dirt);
|
onDirty(dirt);
|
||||||
artboard?.onComponentDirty(this);
|
artboard?.onComponentDirty(this);
|
||||||
|
|
||||||
if (!recurse) {
|
if (!recurse) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final d in dependents) {
|
for (final d in dependents) {
|
||||||
d.addDirt(value, recurse: recurse);
|
d.addDirt(value, recurse: recurse);
|
||||||
}
|
}
|
||||||
@ -33,7 +50,12 @@ abstract class Component extends ComponentBase<RuntimeArtboard>
|
|||||||
|
|
||||||
void onDirty(int mask) {}
|
void onDirty(int mask) {}
|
||||||
void update(int dirt);
|
void update(int dirt);
|
||||||
|
|
||||||
|
/// The artboard this component belongs to.
|
||||||
Artboard? get artboard => _artboard;
|
Artboard? get artboard => _artboard;
|
||||||
|
|
||||||
|
// Note that this isn't a setter as we don't want anything externally changing
|
||||||
|
// the artboard.
|
||||||
void _changeArtboard(Artboard? value) {
|
void _changeArtboard(Artboard? value) {
|
||||||
if (_artboard == value) {
|
if (_artboard == value) {
|
||||||
return;
|
return;
|
||||||
@ -43,8 +65,14 @@ abstract class Component extends ComponentBase<RuntimeArtboard>
|
|||||||
_artboard?.addComponent(this);
|
_artboard?.addComponent(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Called whenever we're resolving the artboard, we piggy back on that
|
||||||
|
/// process to visit ancestors in the tree. This is a good opportunity to
|
||||||
|
/// check if we have an ancestor of a specific type. For example, a Path needs
|
||||||
|
/// to know which Shape it's within.
|
||||||
@mustCallSuper
|
@mustCallSuper
|
||||||
void visitAncestor(Component ancestor) {}
|
void visitAncestor(Component ancestor) {}
|
||||||
|
|
||||||
|
/// Find the artboard in the hierarchy.
|
||||||
bool resolveArtboard() {
|
bool resolveArtboard() {
|
||||||
int sanity = maxTreeDepth;
|
int sanity = maxTreeDepth;
|
||||||
for (Component? curr = this;
|
for (Component? curr = this;
|
||||||
@ -71,6 +99,7 @@ abstract class Component extends ComponentBase<RuntimeArtboard>
|
|||||||
}
|
}
|
||||||
|
|
||||||
void userDataChanged(dynamic from, dynamic to) {}
|
void userDataChanged(dynamic from, dynamic to) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void parentIdChanged(int from, int to) {
|
void parentIdChanged(int from, int to) {
|
||||||
parent = context.resolve(to);
|
parent = context.resolve(to);
|
||||||
@ -79,6 +108,7 @@ abstract class Component extends ComponentBase<RuntimeArtboard>
|
|||||||
ContainerComponent? _parent;
|
ContainerComponent? _parent;
|
||||||
@override
|
@override
|
||||||
ContainerComponent? get parent => _parent;
|
ContainerComponent? get parent => _parent;
|
||||||
|
|
||||||
set parent(ContainerComponent? value) {
|
set parent(ContainerComponent? value) {
|
||||||
if (_parent == value) {
|
if (_parent == value) {
|
||||||
return;
|
return;
|
||||||
@ -93,28 +123,40 @@ abstract class Component extends ComponentBase<RuntimeArtboard>
|
|||||||
void parentChanged(ContainerComponent? from, ContainerComponent? to) {
|
void parentChanged(ContainerComponent? from, ContainerComponent? to) {
|
||||||
from?.children.remove(this);
|
from?.children.remove(this);
|
||||||
from?.childRemoved(this);
|
from?.childRemoved(this);
|
||||||
|
|
||||||
to?.children.add(this);
|
to?.children.add(this);
|
||||||
to?.childAdded(this);
|
to?.childAdded(this);
|
||||||
|
|
||||||
|
// We need to resolve our artboard.
|
||||||
markRebuildDependencies();
|
markRebuildDependencies();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Components that depend on this component.
|
||||||
final Set<Component> _dependents = {};
|
final Set<Component> _dependents = {};
|
||||||
|
|
||||||
|
/// Components that this component depends on.
|
||||||
final Set<Component> _dependsOn = {};
|
final Set<Component> _dependsOn = {};
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Set<Component> get dependents => _dependents;
|
Set<Component> get dependents => _dependents;
|
||||||
|
|
||||||
bool addDependent(Component dependent) {
|
bool addDependent(Component dependent) {
|
||||||
assert(artboard == dependent.artboard,
|
assert(artboard == dependent.artboard,
|
||||||
'Components must be in the same artboard.');
|
'Components must be in the same artboard.');
|
||||||
|
|
||||||
if (!_dependents.add(dependent)) {
|
if (!_dependents.add(dependent)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
dependent._dependsOn.add(this);
|
dependent._dependsOn.add(this);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isValidParent(Component parent) => parent is ContainerComponent;
|
bool isValidParent(Component parent) => parent is ContainerComponent;
|
||||||
|
|
||||||
void markRebuildDependencies() {
|
void markRebuildDependencies() {
|
||||||
if (!context.markDependenciesDirty(this)) {
|
if (!context.markDependenciesDirty(this)) {
|
||||||
|
// no context, or already dirty.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (final dependent in _dependents) {
|
for (final dependent in _dependents) {
|
||||||
@ -128,11 +170,18 @@ abstract class Component extends ComponentBase<RuntimeArtboard>
|
|||||||
parentDep._dependents.remove(this);
|
parentDep._dependents.remove(this);
|
||||||
}
|
}
|
||||||
_dependsOn.clear();
|
_dependsOn.clear();
|
||||||
|
// by default a component depends on nothing (likely it will depend on the
|
||||||
|
// parent but we leave that for specific implementations to supply).
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Something we depend on has been removed. It's important to clear out any
|
||||||
|
/// stored references to that dependency so it can be garbage collected (if
|
||||||
|
/// necessary).
|
||||||
void onDependencyRemoved(Component dependent) {}
|
void onDependencyRemoved(Component dependent) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onAdded() {}
|
void onAdded() {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onAddedDirty() {
|
void onAddedDirty() {
|
||||||
if (parentId != Core.missingId) {
|
if (parentId != Core.missingId) {
|
||||||
@ -140,6 +189,11 @@ abstract class Component extends ComponentBase<RuntimeArtboard>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// When a component has been removed from the Core Context, we clean up any
|
||||||
|
/// dangling references left on the parent and on any other dependent
|
||||||
|
/// component. It's important for specialization of Component to respond to
|
||||||
|
/// override [onDependencyRemoved] and clean up any further stored references
|
||||||
|
/// to that component (for example the target of a Constraint).
|
||||||
@override
|
@override
|
||||||
@mustCallSuper
|
@mustCallSuper
|
||||||
void onRemoved() {
|
void onRemoved() {
|
||||||
@ -148,14 +202,22 @@ abstract class Component extends ComponentBase<RuntimeArtboard>
|
|||||||
parentDep._dependents.remove(this);
|
parentDep._dependents.remove(this);
|
||||||
}
|
}
|
||||||
_dependsOn.clear();
|
_dependsOn.clear();
|
||||||
|
|
||||||
for (final dependent in _dependents) {
|
for (final dependent in _dependents) {
|
||||||
dependent.onDependencyRemoved(this);
|
dependent.onDependencyRemoved(this);
|
||||||
}
|
}
|
||||||
_dependents.clear();
|
_dependents.clear();
|
||||||
|
|
||||||
|
// silently clear from the parent in order to not cause any further undo
|
||||||
|
// stack changes
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
parent!.children.remove(this);
|
parent!.children.remove(this);
|
||||||
parent!.childRemoved(this);
|
parent!.childRemoved(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The artboard containing this component will need its dependencies
|
||||||
|
// re-sorted.
|
||||||
|
|
||||||
if (artboard != null) {
|
if (artboard != null) {
|
||||||
context.markDependencyOrderDirty();
|
context.markDependencyOrderDirty();
|
||||||
_changeArtboard(null);
|
_changeArtboard(null);
|
||||||
@ -168,7 +230,10 @@ abstract class Component extends ComponentBase<RuntimeArtboard>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void nameChanged(String from, String to) {}
|
void nameChanged(String from, String to) {
|
||||||
|
/// Changing name doesn't really do anything.
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool import(ImportStack stack) {
|
bool import(ImportStack stack) {
|
||||||
var artboardImporter = stack.latest<ArtboardImporter>(ArtboardBase.typeKey);
|
var artboardImporter = stack.latest<ArtboardImporter>(ArtboardBase.typeKey);
|
||||||
@ -176,6 +241,7 @@ abstract class Component extends ComponentBase<RuntimeArtboard>
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
artboardImporter.addComponent(this);
|
artboardImporter.addComponent(this);
|
||||||
|
|
||||||
return super.import(stack);
|
return super.import(stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,41 @@
|
|||||||
class ComponentDirt {
|
class ComponentDirt {
|
||||||
static const int dependents = 1 << 0;
|
static const int dependents = 1 << 0;
|
||||||
|
|
||||||
|
/// General flag for components are dirty (if this is up, the update cycle
|
||||||
|
/// runs). It gets automatically applied with any other dirt.
|
||||||
static const int components = 1 << 1;
|
static const int components = 1 << 1;
|
||||||
|
|
||||||
|
/// Draw order needs to be re-computed.
|
||||||
static const int drawOrder = 1 << 2;
|
static const int drawOrder = 1 << 2;
|
||||||
|
|
||||||
|
/// Draw order needs to be re-computed.
|
||||||
static const int naturalDrawOrder = 1 << 3;
|
static const int naturalDrawOrder = 1 << 3;
|
||||||
|
|
||||||
|
/// Path is dirty and needs to be rebuilt.
|
||||||
static const int path = 1 << 4;
|
static const int path = 1 << 4;
|
||||||
|
|
||||||
|
/// Vertices have changed, re-order cached lists.
|
||||||
static const int vertices = 1 << 5;
|
static const int vertices = 1 << 5;
|
||||||
|
|
||||||
|
/// Used by any component that needs to recompute their local transform.
|
||||||
|
/// Usually components that have their transform dirty will also have their
|
||||||
|
/// worldTransform dirty.
|
||||||
static const int transform = 1 << 6;
|
static const int transform = 1 << 6;
|
||||||
|
|
||||||
|
/// Used by any component that needs to update its world transform.
|
||||||
static const int worldTransform = 1 << 7;
|
static const int worldTransform = 1 << 7;
|
||||||
|
|
||||||
|
/// Dirt used to mark some stored paint needs to be rebuilt or that we just
|
||||||
|
/// want to trigger an update cycle so painting occurs.
|
||||||
static const int paint = 1 << 8;
|
static const int paint = 1 << 8;
|
||||||
|
|
||||||
|
/// Used by the gradients track when the stops need to be re-ordered.
|
||||||
static const int stops = 1 << 9;
|
static const int stops = 1 << 9;
|
||||||
|
|
||||||
|
/// Used by ClippingShape to help Shape know when to recalculate its list of
|
||||||
|
/// clipping sources.
|
||||||
static const int clip = 1 << 10;
|
static const int clip = 1 << 10;
|
||||||
|
|
||||||
|
/// Set when blend modes need to be updated.
|
||||||
static const int blendMode = 1 << 11;
|
static const int blendMode = 1 << 11;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
class ComponentFlags {
|
class ComponentFlags {
|
||||||
|
/// Whether the component should be drawn (at runtime this only used by
|
||||||
|
/// drawables and paths).
|
||||||
static const int hidden = 1 << 0;
|
static const int hidden = 1 << 0;
|
||||||
|
|
||||||
|
// Whether the component was locked for editing in the editor.
|
||||||
static const int locked = 1 << 1;
|
static const int locked = 1 << 1;
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,11 @@ abstract class ContainerComponent extends ContainerComponentBase {
|
|||||||
|
|
||||||
@mustCallSuper
|
@mustCallSuper
|
||||||
void childAdded(Component child) {}
|
void childAdded(Component child) {}
|
||||||
|
|
||||||
void childRemoved(Component child) {}
|
void childRemoved(Component child) {}
|
||||||
|
|
||||||
|
// Make sure that the current function can be applied to the current
|
||||||
|
// [Component], before descending onto all the children.
|
||||||
bool forAll(DescentCallback cb) {
|
bool forAll(DescentCallback cb) {
|
||||||
if (cb(this) == false) {
|
if (cb(this) == false) {
|
||||||
return false;
|
return false;
|
||||||
@ -28,17 +32,27 @@ abstract class ContainerComponent extends ContainerComponentBase {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Recursively descend onto all the children in the hierarchy tree.
|
||||||
|
// If the callback returns false, it won't recurse down a particular branch.
|
||||||
void forEachChild(DescentCallback cb) {
|
void forEachChild(DescentCallback cb) {
|
||||||
for (final child in children) {
|
for (final child in children) {
|
||||||
if (cb(child) == false) {
|
if (cb(child) == false) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: replace with a more robust check.
|
||||||
if (child is ContainerComponent) {
|
if (child is ContainerComponent) {
|
||||||
child.forEachChild(cb);
|
child.forEachChild(cb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Recursive version of [Component.remove]. This should only be called when
|
||||||
|
/// you know this is the only part of the branch you are removing in your
|
||||||
|
/// operation. If your operation could remove items from the same branch
|
||||||
|
/// multiple times, you should consider building up a list of the individual
|
||||||
|
/// items to remove and then remove them individually to avoid calling remove
|
||||||
|
/// multiple times on children.
|
||||||
void removeRecursive() {
|
void removeRecursive() {
|
||||||
Set<Component> deathRow = {this};
|
Set<Component> deathRow = {this};
|
||||||
forEachChild((child) => deathRow.add(child));
|
forEachChild((child) => deathRow.add(child));
|
||||||
|
@ -7,10 +7,12 @@ export 'package:rive/src/generated/draw_rules_base.dart';
|
|||||||
class DrawRules extends DrawRulesBase {
|
class DrawRules extends DrawRulesBase {
|
||||||
final Set<DrawTarget> _targets = {};
|
final Set<DrawTarget> _targets = {};
|
||||||
Set<DrawTarget> get targets => _targets;
|
Set<DrawTarget> get targets => _targets;
|
||||||
|
|
||||||
DrawTarget? _activeTarget;
|
DrawTarget? _activeTarget;
|
||||||
DrawTarget? get activeTarget => _activeTarget;
|
DrawTarget? get activeTarget => _activeTarget;
|
||||||
set activeTarget(DrawTarget? value) =>
|
set activeTarget(DrawTarget? value) =>
|
||||||
drawTargetId = value?.id ?? Core.missingId;
|
drawTargetId = value?.id ?? Core.missingId;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void drawTargetIdChanged(int from, int to) {
|
void drawTargetIdChanged(int from, int to) {
|
||||||
_activeTarget = context.resolve(to);
|
_activeTarget = context.resolve(to);
|
||||||
@ -25,12 +27,14 @@ class DrawRules extends DrawRulesBase {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void update(int dirt) {}
|
void update(int dirt) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void childAdded(Component child) {
|
void childAdded(Component child) {
|
||||||
super.childAdded(child);
|
super.childAdded(child);
|
||||||
switch (child.coreType) {
|
switch (child.coreType) {
|
||||||
case DrawTargetBase.typeKey:
|
case DrawTargetBase.typeKey:
|
||||||
_targets.add(child as DrawTarget);
|
_targets.add(child as DrawTarget);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -44,6 +48,7 @@ class DrawRules extends DrawRulesBase {
|
|||||||
if (_targets.isEmpty) {
|
if (_targets.isEmpty) {
|
||||||
remove();
|
remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,14 +6,17 @@ export 'package:rive/src/generated/draw_target_base.dart';
|
|||||||
enum DrawTargetPlacement { before, after }
|
enum DrawTargetPlacement { before, after }
|
||||||
|
|
||||||
class DrawTarget extends DrawTargetBase {
|
class DrawTarget extends DrawTargetBase {
|
||||||
|
// Store first and last drawables that are affected by this target.
|
||||||
Drawable? first;
|
Drawable? first;
|
||||||
Drawable? last;
|
Drawable? last;
|
||||||
|
|
||||||
Drawable? _drawable;
|
Drawable? _drawable;
|
||||||
Drawable? get drawable => _drawable;
|
Drawable? get drawable => _drawable;
|
||||||
set drawable(Drawable? value) {
|
set drawable(Drawable? value) {
|
||||||
if (_drawable == value) {
|
if (_drawable == value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_drawable = value;
|
_drawable = value;
|
||||||
drawableId = value?.id ?? Core.missingId;
|
drawableId = value?.id ?? Core.missingId;
|
||||||
}
|
}
|
||||||
@ -21,6 +24,7 @@ class DrawTarget extends DrawTargetBase {
|
|||||||
DrawTargetPlacement get placement =>
|
DrawTargetPlacement get placement =>
|
||||||
DrawTargetPlacement.values[placementValue];
|
DrawTargetPlacement.values[placementValue];
|
||||||
set placement(DrawTargetPlacement value) => placementValue = value.index;
|
set placement(DrawTargetPlacement value) => placementValue = value.index;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void drawableIdChanged(int from, int to) {
|
void drawableIdChanged(int from, int to) {
|
||||||
drawable = context.resolve(to);
|
drawable = context.resolve(to);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:rive/src/rive_core/component_dirt.dart';
|
import 'package:rive/src/rive_core/component_dirt.dart';
|
||||||
import 'package:rive/src/rive_core/component_flags.dart';
|
import 'package:rive/src/rive_core/component_flags.dart';
|
||||||
import 'package:rive/src/rive_core/container_component.dart';
|
import 'package:rive/src/rive_core/container_component.dart';
|
||||||
@ -9,23 +10,37 @@ import 'package:rive/src/rive_core/transform_component.dart';
|
|||||||
export 'package:rive/src/generated/drawable_base.dart';
|
export 'package:rive/src/generated/drawable_base.dart';
|
||||||
|
|
||||||
abstract class Drawable extends DrawableBase {
|
abstract class Drawable extends DrawableBase {
|
||||||
|
/// Flattened rules inherited from parents (or self) so we don't have to look
|
||||||
|
/// up the tree when re-sorting.
|
||||||
DrawRules? flattenedDrawRules;
|
DrawRules? flattenedDrawRules;
|
||||||
|
|
||||||
|
/// The previous drawable in the draw order.
|
||||||
Drawable? prev;
|
Drawable? prev;
|
||||||
|
|
||||||
|
/// The next drawable in the draw order.
|
||||||
Drawable? next;
|
Drawable? next;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void buildDrawOrder(
|
void buildDrawOrder(
|
||||||
List<Drawable> drawables, DrawRules? rules, List<DrawRules> allRules) {
|
List<Drawable> drawables, DrawRules? rules, List<DrawRules> allRules) {
|
||||||
flattenedDrawRules = drawRules ?? rules;
|
flattenedDrawRules = drawRules ?? rules;
|
||||||
|
|
||||||
drawables.add(this);
|
drawables.add(this);
|
||||||
|
|
||||||
super.buildDrawOrder(drawables, rules, allRules);
|
super.buildDrawOrder(drawables, rules, allRules);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Draw the contents of this drawable component in world transform space.
|
||||||
void draw(Canvas canvas);
|
void draw(Canvas canvas);
|
||||||
|
|
||||||
BlendMode get blendMode => BlendMode.values[blendModeValue];
|
BlendMode get blendMode => BlendMode.values[blendModeValue];
|
||||||
set blendMode(BlendMode value) => blendModeValue = value.index;
|
set blendMode(BlendMode value) => blendModeValue = value.index;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void blendModeValueChanged(int from, int to) {}
|
void blendModeValueChanged(int from, int to) {}
|
||||||
|
|
||||||
List<ClippingShape> _clippingShapes = [];
|
List<ClippingShape> _clippingShapes = [];
|
||||||
|
|
||||||
bool clip(Canvas canvas) {
|
bool clip(Canvas canvas) {
|
||||||
if (_clippingShapes.isEmpty) {
|
if (_clippingShapes.isEmpty) {
|
||||||
return false;
|
return false;
|
||||||
@ -43,6 +58,8 @@ abstract class Drawable extends DrawableBase {
|
|||||||
@override
|
@override
|
||||||
void parentChanged(ContainerComponent? from, ContainerComponent? to) {
|
void parentChanged(ContainerComponent? from, ContainerComponent? to) {
|
||||||
super.parentChanged(from, to);
|
super.parentChanged(from, to);
|
||||||
|
// Make sure we re-compute clipping shapes when we change parents. Issue
|
||||||
|
// #1586
|
||||||
addDirt(ComponentDirt.clip);
|
addDirt(ComponentDirt.clip);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,6 +67,7 @@ abstract class Drawable extends DrawableBase {
|
|||||||
void update(int dirt) {
|
void update(int dirt) {
|
||||||
super.update(dirt);
|
super.update(dirt);
|
||||||
if (dirt & ComponentDirt.clip != 0) {
|
if (dirt & ComponentDirt.clip != 0) {
|
||||||
|
// Find clip in parents.
|
||||||
List<ClippingShape> clippingShapes = [];
|
List<ClippingShape> clippingShapes = [];
|
||||||
for (ContainerComponent? p = this; p != null; p = p.parent) {
|
for (ContainerComponent? p = this; p != null; p = p.parent) {
|
||||||
if (p is TransformComponent) {
|
if (p is TransformComponent) {
|
||||||
@ -62,7 +80,9 @@ abstract class Drawable extends DrawableBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When drawable flags change, repaint.
|
||||||
@override
|
@override
|
||||||
void drawableFlagsChanged(int from, int to) => addDirt(ComponentDirt.paint);
|
void drawableFlagsChanged(int from, int to) => addDirt(ComponentDirt.paint);
|
||||||
|
|
||||||
bool get isHidden => (drawableFlags & ComponentFlags.hidden) != 0;
|
bool get isHidden => (drawableFlags & ComponentFlags.hidden) != 0;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
// Just a way to get around the protected notifyListeners so we can use trigger
|
||||||
|
// multiple events from a single object.
|
||||||
class Event extends ChangeNotifier {
|
class Event extends ChangeNotifier {
|
||||||
void notify() => notifyListeners();
|
void notify() => notifyListeners();
|
||||||
}
|
}
|
||||||
|
@ -5,16 +5,19 @@ import 'package:rive/src/rive_core/math/vec2d.dart';
|
|||||||
|
|
||||||
class AABB {
|
class AABB {
|
||||||
Float32List _buffer;
|
Float32List _buffer;
|
||||||
|
|
||||||
Float32List get values {
|
Float32List get values {
|
||||||
return _buffer;
|
return _buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec2D get topLeft => minimum;
|
Vec2D get topLeft => minimum;
|
||||||
|
|
||||||
Vec2D get topRight {
|
Vec2D get topRight {
|
||||||
return Vec2D.fromValues(_buffer[2], _buffer[1]);
|
return Vec2D.fromValues(_buffer[2], _buffer[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec2D get bottomRight => maximum;
|
Vec2D get bottomRight => maximum;
|
||||||
|
|
||||||
Vec2D get bottomLeft {
|
Vec2D get bottomLeft {
|
||||||
return Vec2D.fromValues(_buffer[0], _buffer[3]);
|
return Vec2D.fromValues(_buffer[0], _buffer[3]);
|
||||||
}
|
}
|
||||||
@ -31,10 +34,14 @@ class AABB {
|
|||||||
double get maxX => _buffer[2];
|
double get maxX => _buffer[2];
|
||||||
double get minY => _buffer[1];
|
double get minY => _buffer[1];
|
||||||
double get maxY => _buffer[3];
|
double get maxY => _buffer[3];
|
||||||
|
|
||||||
AABB() : _buffer = Float32List.fromList([0.0, 0.0, 0.0, 0.0]);
|
AABB() : _buffer = Float32List.fromList([0.0, 0.0, 0.0, 0.0]);
|
||||||
|
|
||||||
AABB.clone(AABB a) : _buffer = Float32List.fromList(a.values);
|
AABB.clone(AABB a) : _buffer = Float32List.fromList(a.values);
|
||||||
|
|
||||||
AABB.fromValues(double a, double b, double c, double d)
|
AABB.fromValues(double a, double b, double c, double d)
|
||||||
: _buffer = Float32List.fromList([a, b, c, d]);
|
: _buffer = Float32List.fromList([a, b, c, d]);
|
||||||
|
|
||||||
AABB.empty()
|
AABB.empty()
|
||||||
: _buffer = Float32List.fromList([
|
: _buffer = Float32List.fromList([
|
||||||
double.maxFinite,
|
double.maxFinite,
|
||||||
@ -42,6 +49,7 @@ class AABB {
|
|||||||
-double.maxFinite,
|
-double.maxFinite,
|
||||||
-double.maxFinite
|
-double.maxFinite
|
||||||
]);
|
]);
|
||||||
|
|
||||||
factory AABB.expand(AABB from, double amount) {
|
factory AABB.expand(AABB from, double amount) {
|
||||||
var aabb = AABB.clone(from);
|
var aabb = AABB.clone(from);
|
||||||
if (aabb.width < amount) {
|
if (aabb.width < amount) {
|
||||||
@ -54,6 +62,7 @@ class AABB {
|
|||||||
}
|
}
|
||||||
return aabb;
|
return aabb;
|
||||||
}
|
}
|
||||||
|
|
||||||
factory AABB.pad(AABB from, double amount) {
|
factory AABB.pad(AABB from, double amount) {
|
||||||
var aabb = AABB.clone(from);
|
var aabb = AABB.clone(from);
|
||||||
aabb[0] -= amount;
|
aabb[0] -= amount;
|
||||||
@ -62,7 +71,9 @@ class AABB {
|
|||||||
aabb[3] += amount;
|
aabb[3] += amount;
|
||||||
return aabb;
|
return aabb;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get isEmpty => !AABB.isValid(this);
|
bool get isEmpty => !AABB.isValid(this);
|
||||||
|
|
||||||
Vec2D includePoint(Vec2D point, Mat2D? transform) {
|
Vec2D includePoint(Vec2D point, Mat2D? transform) {
|
||||||
var transformedPoint = transform == null
|
var transformedPoint = transform == null
|
||||||
? point
|
? point
|
||||||
@ -90,12 +101,15 @@ class AABB {
|
|||||||
|
|
||||||
AABB.fromMinMax(Vec2D min, Vec2D max)
|
AABB.fromMinMax(Vec2D min, Vec2D max)
|
||||||
: _buffer = Float32List.fromList([min[0], min[1], max[0], max[1]]);
|
: _buffer = Float32List.fromList([min[0], min[1], max[0], max[1]]);
|
||||||
|
|
||||||
static bool areEqual(AABB a, AABB b) {
|
static bool areEqual(AABB a, AABB b) {
|
||||||
return a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3];
|
return a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
double get width => _buffer[2] - _buffer[0];
|
double get width => _buffer[2] - _buffer[0];
|
||||||
|
|
||||||
double get height => _buffer[3] - _buffer[1];
|
double get height => _buffer[3] - _buffer[1];
|
||||||
|
|
||||||
double operator [](int idx) {
|
double operator [](int idx) {
|
||||||
return _buffer[idx];
|
return _buffer[idx];
|
||||||
}
|
}
|
||||||
@ -162,14 +176,18 @@ class AABB {
|
|||||||
static bool testOverlap(AABB a, AABB b) {
|
static bool testOverlap(AABB a, AABB b) {
|
||||||
double d1x = b[0] - a[2];
|
double d1x = b[0] - a[2];
|
||||||
double d1y = b[1] - a[3];
|
double d1y = b[1] - a[3];
|
||||||
|
|
||||||
double d2x = a[0] - b[2];
|
double d2x = a[0] - b[2];
|
||||||
double d2y = a[1] - b[3];
|
double d2y = a[1] - b[3];
|
||||||
|
|
||||||
if (d1x > 0.0 || d1y > 0.0) {
|
if (d1x > 0.0 || d1y > 0.0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d2x > 0.0 || d2y > 0.0) {
|
if (d2x > 0.0 || d2y > 0.0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,6 +199,7 @@ class AABB {
|
|||||||
|
|
||||||
AABB translate(Vec2D vec) => AABB.fromValues(_buffer[0] + vec[0],
|
AABB translate(Vec2D vec) => AABB.fromValues(_buffer[0] + vec[0],
|
||||||
_buffer[1] + vec[1], _buffer[2] + vec[0], _buffer[3] + vec[1]);
|
_buffer[1] + vec[1], _buffer[2] + vec[0], _buffer[3] + vec[1]);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return _buffer.toString();
|
return _buffer.toString();
|
||||||
@ -195,16 +214,23 @@ class AABB {
|
|||||||
], transform: matrix);
|
], transform: matrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
factory AABB.fromPoints(Iterable<Vec2D> points,
|
/// Compute an AABB from a set of points with an optional [transform] to apply
|
||||||
{Mat2D? transform, double expand = 0}) {
|
/// before computing.
|
||||||
|
factory AABB.fromPoints(
|
||||||
|
Iterable<Vec2D> points, {
|
||||||
|
Mat2D? transform,
|
||||||
|
double expand = 0,
|
||||||
|
}) {
|
||||||
double minX = double.maxFinite;
|
double minX = double.maxFinite;
|
||||||
double minY = double.maxFinite;
|
double minY = double.maxFinite;
|
||||||
double maxX = -double.maxFinite;
|
double maxX = -double.maxFinite;
|
||||||
double maxY = -double.maxFinite;
|
double maxY = -double.maxFinite;
|
||||||
|
|
||||||
for (final point in points) {
|
for (final point in points) {
|
||||||
var p = transform == null
|
var p = transform == null
|
||||||
? point
|
? point
|
||||||
: Vec2D.transformMat2D(Vec2D(), point, transform);
|
: Vec2D.transformMat2D(Vec2D(), point, transform);
|
||||||
|
|
||||||
double x = p[0];
|
double x = p[0];
|
||||||
double y = p[1];
|
double y = p[1];
|
||||||
if (x < minX) {
|
if (x < minX) {
|
||||||
@ -213,6 +239,7 @@ class AABB {
|
|||||||
if (y < minY) {
|
if (y < minY) {
|
||||||
minY = y;
|
minY = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (x > maxX) {
|
if (x > maxX) {
|
||||||
maxX = x;
|
maxX = x;
|
||||||
}
|
}
|
||||||
@ -220,6 +247,8 @@ class AABB {
|
|||||||
maxY = y;
|
maxY = y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure the box is at least this wide/high
|
||||||
if (expand != 0) {
|
if (expand != 0) {
|
||||||
double width = maxX - minX;
|
double width = maxX - minX;
|
||||||
double diff = expand - width;
|
double diff = expand - width;
|
||||||
@ -230,6 +259,7 @@ class AABB {
|
|||||||
}
|
}
|
||||||
double height = maxY - minY;
|
double height = maxY - minY;
|
||||||
diff = expand - height;
|
diff = expand - height;
|
||||||
|
|
||||||
if (diff > 0) {
|
if (diff > 0) {
|
||||||
diff /= 2;
|
diff /= 2;
|
||||||
minY -= diff;
|
minY -= diff;
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
|
/// Use this for perfect rounded corners.
|
||||||
|
/// https://stackoverflow.com/questions/1734745/how-to-create-circle-with-b%C3%A9zier-curves
|
||||||
const circleConstant = 0.552284749831;
|
const circleConstant = 0.552284749831;
|
||||||
const icircleConstant = 1 - circleConstant;
|
const icircleConstant = 1 - circleConstant;
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:rive/src/rive_core/math/transform_components.dart';
|
import 'package:rive/src/rive_core/math/transform_components.dart';
|
||||||
import 'package:rive/src/rive_core/math/vec2d.dart';
|
import 'package:rive/src/rive_core/math/vec2d.dart';
|
||||||
|
|
||||||
|
/// Can't make this constant so we override and disable changing values so we'll
|
||||||
|
/// throw if something tries to change the identity.
|
||||||
class _Identity extends Mat2D {
|
class _Identity extends Mat2D {
|
||||||
@override
|
@override
|
||||||
void operator []=(int index, double value) => throw UnsupportedError(
|
void operator []=(int index, double value) => throw UnsupportedError(
|
||||||
@ -12,6 +15,7 @@ class _Identity extends Mat2D {
|
|||||||
class Mat2D {
|
class Mat2D {
|
||||||
static final Mat2D identity = _Identity();
|
static final Mat2D identity = _Identity();
|
||||||
final Float32List _buffer;
|
final Float32List _buffer;
|
||||||
|
|
||||||
Float32List get values {
|
Float32List get values {
|
||||||
return _buffer;
|
return _buffer;
|
||||||
}
|
}
|
||||||
@ -46,15 +50,20 @@ class Mat2D {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Mat2D() : _buffer = Float32List.fromList([1.0, 0.0, 0.0, 1.0, 0.0, 0.0]);
|
Mat2D() : _buffer = Float32List.fromList([1.0, 0.0, 0.0, 1.0, 0.0, 0.0]);
|
||||||
|
|
||||||
Mat2D.fromTranslation(Vec2D translation)
|
Mat2D.fromTranslation(Vec2D translation)
|
||||||
: _buffer = Float32List.fromList(
|
: _buffer = Float32List.fromList(
|
||||||
[1.0, 0.0, 0.0, 1.0, translation[0], translation[1]]);
|
[1.0, 0.0, 0.0, 1.0, translation[0], translation[1]]);
|
||||||
|
|
||||||
Mat2D.fromScaling(Vec2D scaling)
|
Mat2D.fromScaling(Vec2D scaling)
|
||||||
: _buffer = Float32List.fromList([scaling[0], 0, 0, scaling[1], 0, 0]);
|
: _buffer = Float32List.fromList([scaling[0], 0, 0, scaling[1], 0, 0]);
|
||||||
|
|
||||||
Mat2D.fromMat4(Float64List mat4)
|
Mat2D.fromMat4(Float64List mat4)
|
||||||
: _buffer = Float32List.fromList(
|
: _buffer = Float32List.fromList(
|
||||||
[mat4[0], mat4[1], mat4[4], mat4[5], mat4[12], mat4[13]]);
|
[mat4[0], mat4[1], mat4[4], mat4[5], mat4[12], mat4[13]]);
|
||||||
|
|
||||||
Mat2D.clone(Mat2D copy) : _buffer = Float32List.fromList(copy.values);
|
Mat2D.clone(Mat2D copy) : _buffer = Float32List.fromList(copy.values);
|
||||||
|
|
||||||
static Mat2D fromRotation(Mat2D o, double rad) {
|
static Mat2D fromRotation(Mat2D o, double rad) {
|
||||||
double s = sin(rad);
|
double s = sin(rad);
|
||||||
double c = cos(rad);
|
double c = cos(rad);
|
||||||
@ -163,11 +172,13 @@ class Mat2D {
|
|||||||
|
|
||||||
static bool invert(Mat2D o, Mat2D a) {
|
static bool invert(Mat2D o, Mat2D a) {
|
||||||
double aa = a[0], ab = a[1], ac = a[2], ad = a[3], atx = a[4], aty = a[5];
|
double aa = a[0], ab = a[1], ac = a[2], ad = a[3], atx = a[4], aty = a[5];
|
||||||
|
|
||||||
double det = aa * ad - ab * ac;
|
double det = aa * ad - ab * ac;
|
||||||
if (det == 0.0) {
|
if (det == 0.0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
det = 1.0 / det;
|
det = 1.0 / det;
|
||||||
|
|
||||||
o[0] = ad * det;
|
o[0] = ad * det;
|
||||||
o[1] = -ab * det;
|
o[1] = -ab * det;
|
||||||
o[2] = -ac * det;
|
o[2] = -ac * det;
|
||||||
@ -181,6 +192,7 @@ class Mat2D {
|
|||||||
double x = m[0];
|
double x = m[0];
|
||||||
double y = m[1];
|
double y = m[1];
|
||||||
s[0] = x.sign * sqrt(x * x + y * y);
|
s[0] = x.sign * sqrt(x * x + y * y);
|
||||||
|
|
||||||
x = m[2];
|
x = m[2];
|
||||||
y = m[3];
|
y = m[3];
|
||||||
s[1] = y.sign * sqrt(x * x + y * y);
|
s[1] = y.sign * sqrt(x * x + y * y);
|
||||||
@ -203,11 +215,13 @@ class Mat2D {
|
|||||||
|
|
||||||
static void decompose(Mat2D m, TransformComponents result) {
|
static void decompose(Mat2D m, TransformComponents result) {
|
||||||
double m0 = m[0], m1 = m[1], m2 = m[2], m3 = m[3];
|
double m0 = m[0], m1 = m[1], m2 = m[2], m3 = m[3];
|
||||||
|
|
||||||
double rotation = atan2(m1, m0);
|
double rotation = atan2(m1, m0);
|
||||||
double denom = m0 * m0 + m1 * m1;
|
double denom = m0 * m0 + m1 * m1;
|
||||||
double scaleX = sqrt(denom);
|
double scaleX = sqrt(denom);
|
||||||
double scaleY = (scaleX == 0) ? 0 : ((m0 * m3 - m2 * m1) / scaleX);
|
double scaleY = (scaleX == 0) ? 0 : ((m0 * m3 - m2 * m1) / scaleX);
|
||||||
double skewX = atan2(m0 * m2 + m1 * m3, denom);
|
double skewX = atan2(m0 * m2 + m1 * m3, denom);
|
||||||
|
|
||||||
result[0] = m[4];
|
result[0] = m[4];
|
||||||
result[1] = m[5];
|
result[1] = m[5];
|
||||||
result[2] = scaleX;
|
result[2] = scaleX;
|
||||||
@ -218,6 +232,7 @@ class Mat2D {
|
|||||||
|
|
||||||
static void compose(Mat2D m, TransformComponents result) {
|
static void compose(Mat2D m, TransformComponents result) {
|
||||||
double r = result[4];
|
double r = result[4];
|
||||||
|
|
||||||
if (r != 0.0) {
|
if (r != 0.0) {
|
||||||
Mat2D.fromRotation(m, r);
|
Mat2D.fromRotation(m, r);
|
||||||
} else {
|
} else {
|
||||||
@ -226,6 +241,7 @@ class Mat2D {
|
|||||||
m[4] = result[0];
|
m[4] = result[0];
|
||||||
m[5] = result[1];
|
m[5] = result[1];
|
||||||
Mat2D.scale(m, m, result.scale);
|
Mat2D.scale(m, m, result.scale);
|
||||||
|
|
||||||
double sk = result[5];
|
double sk = result[5];
|
||||||
if (sk != 0.0) {
|
if (sk != 0.0) {
|
||||||
m[2] = m[0] * sk + m[2];
|
m[2] = m[0] * sk + m[2];
|
||||||
|
@ -1,18 +1,39 @@
|
|||||||
import 'package:rive/src/rive_core/math/vec2d.dart';
|
import 'package:rive/src/rive_core/math/vec2d.dart';
|
||||||
|
|
||||||
|
/// Result of projecting a point onto a segment.
|
||||||
class ProjectionResult {
|
class ProjectionResult {
|
||||||
|
/// The distance factor from 0-1 along the segment starting at the
|
||||||
|
/// [Segment2D.start].
|
||||||
final double t;
|
final double t;
|
||||||
|
|
||||||
|
/// The actual 2d point in the same space as [Segment2D.start] and
|
||||||
|
/// [Segment2D.end].
|
||||||
final Vec2D point;
|
final Vec2D point;
|
||||||
|
|
||||||
ProjectionResult(this.t, this.point);
|
ProjectionResult(this.t, this.point);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A line segment with a discrete [start] and [end].
|
||||||
class Segment2D {
|
class Segment2D {
|
||||||
|
/// The starting point of this line segment.
|
||||||
final Vec2D start;
|
final Vec2D start;
|
||||||
|
|
||||||
|
/// The ending point of this line segment.
|
||||||
final Vec2D end;
|
final Vec2D end;
|
||||||
|
|
||||||
|
/// Difference from start to end. Nullable so we can compute it only when we
|
||||||
|
/// need it.
|
||||||
Vec2D? diff;
|
Vec2D? diff;
|
||||||
|
|
||||||
|
/// The squared length of this segment.
|
||||||
double lengthSquared = 0;
|
double lengthSquared = 0;
|
||||||
|
|
||||||
Segment2D(this.start, this.end);
|
Segment2D(this.start, this.end);
|
||||||
|
|
||||||
|
/// Find where the given [point] lies on this segment.
|
||||||
ProjectionResult projectPoint(Vec2D point, {bool clamp = true}) {
|
ProjectionResult projectPoint(Vec2D point, {bool clamp = true}) {
|
||||||
|
// We cache these internally so we can call projectPoint multiple times in
|
||||||
|
// succession performantly.
|
||||||
if (diff == null) {
|
if (diff == null) {
|
||||||
diff = Vec2D.subtract(Vec2D(), start, end);
|
diff = Vec2D.subtract(Vec2D(), start, end);
|
||||||
lengthSquared = Vec2D.squaredLength(diff!);
|
lengthSquared = Vec2D.squaredLength(diff!);
|
||||||
@ -23,7 +44,9 @@ class Segment2D {
|
|||||||
double t = ((point[0] - start[0]) * (end[0] - start[0]) +
|
double t = ((point[0] - start[0]) * (end[0] - start[0]) +
|
||||||
(point[1] - start[1]) * (end[1] - start[1])) /
|
(point[1] - start[1]) * (end[1] - start[1])) /
|
||||||
lengthSquared;
|
lengthSquared;
|
||||||
|
|
||||||
if (clamp) {
|
if (clamp) {
|
||||||
|
// Clamp at edges.
|
||||||
if (t < 0.0) {
|
if (t < 0.0) {
|
||||||
return ProjectionResult(0, start);
|
return ProjectionResult(0, start);
|
||||||
}
|
}
|
||||||
@ -31,9 +54,13 @@ class Segment2D {
|
|||||||
return ProjectionResult(1, end);
|
return ProjectionResult(1, end);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ProjectionResult(
|
return ProjectionResult(
|
||||||
t,
|
t,
|
||||||
Vec2D.fromValues(start[0] + t * (end[0] - start[0]),
|
Vec2D.fromValues(
|
||||||
start[1] + t * (end[1] - start[1])));
|
start[0] + t * (end[0] - start[0]),
|
||||||
|
start[1] + t * (end[1] - start[1]),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:rive/src/rive_core/math/vec2d.dart';
|
import 'package:rive/src/rive_core/math/vec2d.dart';
|
||||||
|
|
||||||
class TransformComponents {
|
class TransformComponents {
|
||||||
final Float32List _buffer;
|
final Float32List _buffer;
|
||||||
|
|
||||||
Float32List get values {
|
Float32List get values {
|
||||||
return _buffer;
|
return _buffer;
|
||||||
}
|
}
|
||||||
@ -18,8 +20,10 @@ class TransformComponents {
|
|||||||
|
|
||||||
TransformComponents()
|
TransformComponents()
|
||||||
: _buffer = Float32List.fromList([1.0, 0.0, 0.0, 1.0, 0.0, 0.0]);
|
: _buffer = Float32List.fromList([1.0, 0.0, 0.0, 1.0, 0.0, 0.0]);
|
||||||
|
|
||||||
TransformComponents.clone(TransformComponents copy)
|
TransformComponents.clone(TransformComponents copy)
|
||||||
: _buffer = Float32List.fromList(copy.values);
|
: _buffer = Float32List.fromList(copy.values);
|
||||||
|
|
||||||
double get x {
|
double get x {
|
||||||
return _buffer[0];
|
return _buffer[0];
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import 'package:rive/src/utilities/utilities.dart';
|
|||||||
|
|
||||||
class Vec2D {
|
class Vec2D {
|
||||||
final Float32List _buffer;
|
final Float32List _buffer;
|
||||||
|
|
||||||
Float32List get values {
|
Float32List get values {
|
||||||
return _buffer;
|
return _buffer;
|
||||||
}
|
}
|
||||||
@ -18,8 +19,11 @@ class Vec2D {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Vec2D() : _buffer = Float32List.fromList([0.0, 0.0]);
|
Vec2D() : _buffer = Float32List.fromList([0.0, 0.0]);
|
||||||
|
|
||||||
Vec2D.clone(Vec2D copy) : _buffer = Float32List.fromList(copy._buffer);
|
Vec2D.clone(Vec2D copy) : _buffer = Float32List.fromList(copy._buffer);
|
||||||
|
|
||||||
Vec2D.fromValues(double x, double y) : _buffer = Float32List.fromList([x, y]);
|
Vec2D.fromValues(double x, double y) : _buffer = Float32List.fromList([x, y]);
|
||||||
|
|
||||||
static void copy(Vec2D o, Vec2D a) {
|
static void copy(Vec2D o, Vec2D a) {
|
||||||
o[0] = a[0];
|
o[0] = a[0];
|
||||||
o[1] = a[1];
|
o[1] = a[1];
|
||||||
@ -99,6 +103,7 @@ class Vec2D {
|
|||||||
static Vec2D negate(Vec2D result, Vec2D a) {
|
static Vec2D negate(Vec2D result, Vec2D a) {
|
||||||
result[0] = -1 * a[0];
|
result[0] = -1 * a[0];
|
||||||
result[1] = -1 * a[1];
|
result[1] = -1 * a[1];
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,9 +148,12 @@ class Vec2D {
|
|||||||
if (t >= 1) {
|
if (t >= 1) {
|
||||||
return Vec2D.squaredDistance(segmentPoint2, pt);
|
return Vec2D.squaredDistance(segmentPoint2, pt);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec2D ptOnSeg = Vec2D.fromValues(
|
Vec2D ptOnSeg = Vec2D.fromValues(
|
||||||
segmentPoint1[0] + t * (segmentPoint2[0] - segmentPoint1[0]),
|
segmentPoint1[0] + t * (segmentPoint2[0] - segmentPoint1[0]),
|
||||||
segmentPoint1[1] + t * (segmentPoint2[1] - segmentPoint1[1]));
|
segmentPoint1[1] + t * (segmentPoint2[1] - segmentPoint1[1]),
|
||||||
|
);
|
||||||
|
|
||||||
return Vec2D.squaredDistance(ptOnSeg, pt);
|
return Vec2D.squaredDistance(ptOnSeg, pt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,6 +173,7 @@ class Vec2D {
|
|||||||
@override
|
@override
|
||||||
bool operator ==(Object o) =>
|
bool operator ==(Object o) =>
|
||||||
o is Vec2D && _buffer[0] == o[0] && _buffer[1] == o[1];
|
o is Vec2D && _buffer[0] == o[0] && _buffer[1] == o[1];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => szudzik(_buffer[0].hashCode, _buffer[1].hashCode);
|
int get hashCode => szudzik(_buffer[0].hashCode, _buffer[1].hashCode);
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,8 @@ class _UnknownNode extends Node {}
|
|||||||
|
|
||||||
class Node extends NodeBase {
|
class Node extends NodeBase {
|
||||||
static final Node unknown = _UnknownNode();
|
static final Node unknown = _UnknownNode();
|
||||||
|
|
||||||
|
/// Sets the position of the Node
|
||||||
set translation(Vec2D pos) {
|
set translation(Vec2D pos) {
|
||||||
x = pos[0];
|
x = pos[0];
|
||||||
y = pos[1];
|
y = pos[1];
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
/// Abstraction for receiving a per frame callback while isPlaying is true to
|
||||||
|
/// apply animation based on an elapsed amount of time.
|
||||||
abstract class RiveAnimationController<T> {
|
abstract class RiveAnimationController<T> {
|
||||||
final _isActive = ValueNotifier<bool>(false);
|
final _isActive = ValueNotifier<bool>(false);
|
||||||
ValueListenable<bool> get isActiveChanged => _isActive;
|
ValueListenable<bool> get isActiveChanged => _isActive;
|
||||||
|
|
||||||
bool get isActive => _isActive.value;
|
bool get isActive => _isActive.value;
|
||||||
set isActive(bool value) {
|
set isActive(bool value) {
|
||||||
if (_isActive.value != value) {
|
if (_isActive.value != value) {
|
||||||
@ -20,7 +23,11 @@ abstract class RiveAnimationController<T> {
|
|||||||
void onActivate() {}
|
void onActivate() {}
|
||||||
@protected
|
@protected
|
||||||
void onDeactivate() {}
|
void onDeactivate() {}
|
||||||
|
|
||||||
|
/// Apply animation to objects registered in [core]. Note that a [core]
|
||||||
|
/// context is specified as animations can be applied to instances.
|
||||||
void apply(T core, double elapsedSeconds);
|
void apply(T core, double elapsedSeconds);
|
||||||
|
|
||||||
bool init(T core) => true;
|
bool init(T core) => true;
|
||||||
void dispose() {}
|
void dispose() {}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
|
|
||||||
|
/// Thrown when a file being read doesn't match the Rive format.
|
||||||
@immutable
|
@immutable
|
||||||
class RiveFormatErrorException implements Exception {
|
class RiveFormatErrorException implements Exception {
|
||||||
final String cause;
|
final String cause;
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
|
|
||||||
|
/// Error that occurs when a file being loaded doesn't match the importer's
|
||||||
|
/// supported version.
|
||||||
@immutable
|
@immutable
|
||||||
class RiveUnsupportedVersionException implements Exception {
|
class RiveUnsupportedVersionException implements Exception {
|
||||||
final int majorVersion;
|
final int majorVersion;
|
||||||
@ -8,6 +10,7 @@ class RiveUnsupportedVersionException implements Exception {
|
|||||||
final int fileMinorVersion;
|
final int fileMinorVersion;
|
||||||
const RiveUnsupportedVersionException(this.majorVersion, this.minorVersion,
|
const RiveUnsupportedVersionException(this.majorVersion, this.minorVersion,
|
||||||
this.fileMajorVersion, this.fileMinorVersion);
|
this.fileMajorVersion, this.fileMinorVersion);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'File contains version $fileMajorVersion.$fileMinorVersion. '
|
return 'File contains version $fileMajorVersion.$fileMinorVersion. '
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
|
|
||||||
import 'package:rive/src/rive_core/runtime/exceptions/rive_format_error_exception.dart';
|
import 'package:rive/src/rive_core/runtime/exceptions/rive_format_error_exception.dart';
|
||||||
import 'package:rive/src/rive_core/runtime/exceptions/rive_unsupported_version_exception.dart';
|
import 'package:rive/src/rive_core/runtime/exceptions/rive_unsupported_version_exception.dart';
|
||||||
import 'package:rive/src/utilities/binary_buffer/binary_reader.dart';
|
import 'package:rive/src/utilities/binary_buffer/binary_reader.dart';
|
||||||
|
|
||||||
|
/// Stores the minor and major version of Rive. Versions with the same major
|
||||||
|
/// value are backwards and forwards compatible.
|
||||||
class RuntimeVersion {
|
class RuntimeVersion {
|
||||||
final int major;
|
final int major;
|
||||||
final int minor;
|
final int minor;
|
||||||
|
|
||||||
const RuntimeVersion(this.major, this.minor);
|
const RuntimeVersion(this.major, this.minor);
|
||||||
String versionString() {
|
String versionString() {
|
||||||
return '$major.$minor';
|
return '$major.$minor';
|
||||||
@ -17,19 +21,26 @@ const riveVersion = RuntimeVersion(7, 0);
|
|||||||
class RuntimeHeader {
|
class RuntimeHeader {
|
||||||
static const String fingerprint = 'RIVE';
|
static const String fingerprint = 'RIVE';
|
||||||
final RuntimeVersion version;
|
final RuntimeVersion version;
|
||||||
|
|
||||||
final int fileId;
|
final int fileId;
|
||||||
|
|
||||||
final HashMap<int, int> propertyToFieldIndex;
|
final HashMap<int, int> propertyToFieldIndex;
|
||||||
RuntimeHeader(
|
|
||||||
{required this.fileId,
|
RuntimeHeader({
|
||||||
required this.version,
|
required this.fileId,
|
||||||
required this.propertyToFieldIndex});
|
required this.version,
|
||||||
|
required this.propertyToFieldIndex,
|
||||||
|
});
|
||||||
|
|
||||||
factory RuntimeHeader.read(BinaryReader reader) {
|
factory RuntimeHeader.read(BinaryReader reader) {
|
||||||
var fingerprint = RuntimeHeader.fingerprint.codeUnits;
|
var fingerprint = RuntimeHeader.fingerprint.codeUnits;
|
||||||
|
|
||||||
for (int i = 0; i < fingerprint.length; i++) {
|
for (int i = 0; i < fingerprint.length; i++) {
|
||||||
if (reader.readUint8() != fingerprint[i]) {
|
if (reader.readUint8() != fingerprint[i]) {
|
||||||
throw const RiveFormatErrorException('Fingerprint doesn\'t match.');
|
throw const RiveFormatErrorException('Fingerprint doesn\'t match.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int readMajorVersion = reader.readVarUint();
|
int readMajorVersion = reader.readVarUint();
|
||||||
int readMinorVersion = reader.readVarUint();
|
int readMinorVersion = reader.readVarUint();
|
||||||
if (readMajorVersion > riveVersion.major) {
|
if (readMajorVersion > riveVersion.major) {
|
||||||
@ -40,7 +51,9 @@ class RuntimeHeader {
|
|||||||
reader.readVarUint();
|
reader.readVarUint();
|
||||||
}
|
}
|
||||||
int fileId = reader.readVarUint();
|
int fileId = reader.readVarUint();
|
||||||
|
|
||||||
var propertyFields = HashMap<int, int>();
|
var propertyFields = HashMap<int, int>();
|
||||||
|
|
||||||
var propertyKeys = <int>[];
|
var propertyKeys = <int>[];
|
||||||
for (int propertyKey = reader.readVarUint();
|
for (int propertyKey = reader.readVarUint();
|
||||||
propertyKey != 0;
|
propertyKey != 0;
|
||||||
@ -58,9 +71,11 @@ class RuntimeHeader {
|
|||||||
propertyFields[propertyKey] = fieldIndex;
|
propertyFields[propertyKey] = fieldIndex;
|
||||||
currentBit += 2;
|
currentBit += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
return RuntimeHeader(
|
return RuntimeHeader(
|
||||||
fileId: fileId,
|
fileId: fileId,
|
||||||
version: RuntimeVersion(readMajorVersion, readMinorVersion),
|
version: RuntimeVersion(readMajorVersion, readMinorVersion),
|
||||||
propertyToFieldIndex: propertyFields);
|
propertyToFieldIndex: propertyFields,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:rive/src/rive_core/component_dirt.dart';
|
import 'package:rive/src/rive_core/component_dirt.dart';
|
||||||
import 'package:rive/src/rive_core/node.dart';
|
import 'package:rive/src/rive_core/node.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/shape.dart';
|
import 'package:rive/src/rive_core/shapes/shape.dart';
|
||||||
@ -10,19 +11,25 @@ class ClippingShape extends ClippingShapeBase {
|
|||||||
final List<Shape> _shapes = [];
|
final List<Shape> _shapes = [];
|
||||||
PathFillType get fillType => PathFillType.values[fillRule];
|
PathFillType get fillType => PathFillType.values[fillRule];
|
||||||
set fillType(PathFillType type) => fillRule = type.index;
|
set fillType(PathFillType type) => fillRule = type.index;
|
||||||
|
|
||||||
Node _source = Node.unknown;
|
Node _source = Node.unknown;
|
||||||
Node get source => _source;
|
Node get source => _source;
|
||||||
set source(Node value) {
|
set source(Node value) {
|
||||||
if (_source == value) {
|
if (_source == value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_source = value;
|
_source = value;
|
||||||
sourceId = value.id;
|
sourceId = value.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void fillRuleChanged(int from, int to) {
|
void fillRuleChanged(int from, int to) {
|
||||||
|
// In the future, if clipOp can change at runtime (animation), we may want
|
||||||
|
// the shapes that use this as a clipping source to make them depend on this
|
||||||
|
// clipping shape so we can add dirt to them directly.
|
||||||
parent?.addDirt(ComponentDirt.clip, recurse: true);
|
parent?.addDirt(ComponentDirt.clip, recurse: true);
|
||||||
|
|
||||||
addDirt(ComponentDirt.path);
|
addDirt(ComponentDirt.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,10 +51,13 @@ class ClippingShape extends ClippingShapeBase {
|
|||||||
_source.forAll((component) {
|
_source.forAll((component) {
|
||||||
if (component is Shape) {
|
if (component is Shape) {
|
||||||
_shapes.add(component);
|
_shapes.add(component);
|
||||||
|
//component.addDependent(this);
|
||||||
component.pathComposer.addDependent(this);
|
component.pathComposer.addDependent(this);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// make sure we rebuild the clipping path.
|
||||||
addDirt(ComponentDirt.path);
|
addDirt(ComponentDirt.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,6 +70,8 @@ class ClippingShape extends ClippingShapeBase {
|
|||||||
@override
|
@override
|
||||||
void update(int dirt) {
|
void update(int dirt) {
|
||||||
if (dirt & (ComponentDirt.worldTransform | ComponentDirt.path) != 0) {
|
if (dirt & (ComponentDirt.worldTransform | ComponentDirt.path) != 0) {
|
||||||
|
// Build the clipping path as one of our dependent shapes changes or we
|
||||||
|
// added a shape.
|
||||||
clippingPath.reset();
|
clippingPath.reset();
|
||||||
clippingPath.fillType = fillType;
|
clippingPath.fillType = fillType;
|
||||||
for (final shape in _shapes) {
|
for (final shape in _shapes) {
|
||||||
@ -75,6 +87,7 @@ class ClippingShape extends ClippingShapeBase {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void isVisibleChanged(bool from, bool to) {
|
void isVisibleChanged(bool from, bool to) {
|
||||||
|
// Redraw
|
||||||
_source.addDirt(ComponentDirt.paint);
|
_source.addDirt(ComponentDirt.paint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:rive/src/core/core.dart';
|
import 'package:rive/src/core/core.dart';
|
||||||
import 'package:rive/src/rive_core/component_dirt.dart';
|
import 'package:rive/src/rive_core/component_dirt.dart';
|
||||||
import 'package:rive/src/rive_core/math/vec2d.dart';
|
import 'package:rive/src/rive_core/math/vec2d.dart';
|
||||||
@ -10,8 +11,10 @@ class CubicAsymmetricVertex extends CubicAsymmetricVertexBase {
|
|||||||
CubicAsymmetricVertex.procedural() {
|
CubicAsymmetricVertex.procedural() {
|
||||||
InternalCoreHelper.markValid(this);
|
InternalCoreHelper.markValid(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec2D? _inPoint;
|
Vec2D? _inPoint;
|
||||||
Vec2D? _outPoint;
|
Vec2D? _outPoint;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Vec2D get outPoint {
|
Vec2D get outPoint {
|
||||||
return _outPoint ??= Vec2D.add(
|
return _outPoint ??= Vec2D.add(
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:rive/src/core/core.dart';
|
import 'package:rive/src/core/core.dart';
|
||||||
import 'package:rive/src/rive_core/component_dirt.dart';
|
import 'package:rive/src/rive_core/component_dirt.dart';
|
||||||
import 'package:rive/src/rive_core/math/vec2d.dart';
|
import 'package:rive/src/rive_core/math/vec2d.dart';
|
||||||
@ -8,16 +9,18 @@ export 'package:rive/src/generated/shapes/cubic_detached_vertex_base.dart';
|
|||||||
class CubicDetachedVertex extends CubicDetachedVertexBase {
|
class CubicDetachedVertex extends CubicDetachedVertexBase {
|
||||||
Vec2D? _inPoint;
|
Vec2D? _inPoint;
|
||||||
Vec2D? _outPoint;
|
Vec2D? _outPoint;
|
||||||
|
|
||||||
CubicDetachedVertex();
|
CubicDetachedVertex();
|
||||||
CubicDetachedVertex.fromValues(
|
CubicDetachedVertex.fromValues({
|
||||||
{required double x,
|
required double x,
|
||||||
required double y,
|
required double y,
|
||||||
double? inX,
|
double? inX,
|
||||||
double? inY,
|
double? inY,
|
||||||
double? outX,
|
double? outX,
|
||||||
double? outY,
|
double? outY,
|
||||||
Vec2D? inPoint,
|
Vec2D? inPoint,
|
||||||
Vec2D? outPoint}) {
|
Vec2D? outPoint,
|
||||||
|
}) {
|
||||||
InternalCoreHelper.markValid(this);
|
InternalCoreHelper.markValid(this);
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
@ -25,12 +28,14 @@ class CubicDetachedVertex extends CubicDetachedVertexBase {
|
|||||||
this.outPoint =
|
this.outPoint =
|
||||||
Vec2D.fromValues(outX ?? outPoint![0], outY ?? outPoint![1]);
|
Vec2D.fromValues(outX ?? outPoint![0], outY ?? outPoint![1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Vec2D get outPoint => _outPoint ??= Vec2D.add(
|
Vec2D get outPoint => _outPoint ??= Vec2D.add(
|
||||||
Vec2D(),
|
Vec2D(),
|
||||||
translation,
|
translation,
|
||||||
Vec2D.fromValues(
|
Vec2D.fromValues(
|
||||||
cos(outRotation) * outDistance, sin(outRotation) * outDistance));
|
cos(outRotation) * outDistance, sin(outRotation) * outDistance));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
set outPoint(Vec2D value) {
|
set outPoint(Vec2D value) {
|
||||||
_outPoint = Vec2D.clone(value);
|
_outPoint = Vec2D.clone(value);
|
||||||
@ -42,6 +47,7 @@ class CubicDetachedVertex extends CubicDetachedVertexBase {
|
|||||||
translation,
|
translation,
|
||||||
Vec2D.fromValues(
|
Vec2D.fromValues(
|
||||||
cos(inRotation) * inDistance, sin(inRotation) * inDistance));
|
cos(inRotation) * inDistance, sin(inRotation) * inDistance));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
set inPoint(Vec2D value) {
|
set inPoint(Vec2D value) {
|
||||||
_inPoint = Vec2D.clone(value);
|
_inPoint = Vec2D.clone(value);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:rive/src/core/core.dart';
|
import 'package:rive/src/core/core.dart';
|
||||||
import 'package:rive/src/rive_core/component_dirt.dart';
|
import 'package:rive/src/rive_core/component_dirt.dart';
|
||||||
import 'package:rive/src/rive_core/math/vec2d.dart';
|
import 'package:rive/src/rive_core/math/vec2d.dart';
|
||||||
@ -7,11 +8,15 @@ export 'package:rive/src/generated/shapes/cubic_mirrored_vertex_base.dart';
|
|||||||
|
|
||||||
class CubicMirroredVertex extends CubicMirroredVertexBase {
|
class CubicMirroredVertex extends CubicMirroredVertexBase {
|
||||||
CubicMirroredVertex();
|
CubicMirroredVertex();
|
||||||
|
|
||||||
|
/// Makes a vertex that is disconnected from core.
|
||||||
CubicMirroredVertex.procedural() {
|
CubicMirroredVertex.procedural() {
|
||||||
InternalCoreHelper.markValid(this);
|
InternalCoreHelper.markValid(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec2D? _inPoint;
|
Vec2D? _inPoint;
|
||||||
Vec2D? _outPoint;
|
Vec2D? _outPoint;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Vec2D get outPoint {
|
Vec2D get outPoint {
|
||||||
return _outPoint ??= Vec2D.add(Vec2D(), translation,
|
return _outPoint ??= Vec2D.add(Vec2D(), translation,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:rive/src/rive_core/bones/weight.dart';
|
import 'package:rive/src/rive_core/bones/weight.dart';
|
||||||
import 'package:rive/src/rive_core/math/mat2d.dart';
|
import 'package:rive/src/rive_core/math/mat2d.dart';
|
||||||
import 'package:rive/src/rive_core/math/vec2d.dart';
|
import 'package:rive/src/rive_core/math/vec2d.dart';
|
||||||
@ -8,15 +9,20 @@ export 'package:rive/src/generated/shapes/cubic_vertex_base.dart';
|
|||||||
abstract class CubicVertex extends CubicVertexBase {
|
abstract class CubicVertex extends CubicVertexBase {
|
||||||
Vec2D get outPoint;
|
Vec2D get outPoint;
|
||||||
Vec2D get inPoint;
|
Vec2D get inPoint;
|
||||||
|
|
||||||
set outPoint(Vec2D value);
|
set outPoint(Vec2D value);
|
||||||
set inPoint(Vec2D value);
|
set inPoint(Vec2D value);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Vec2D get renderTranslation => weight?.translation ?? super.renderTranslation;
|
Vec2D get renderTranslation => weight?.translation ?? super.renderTranslation;
|
||||||
|
|
||||||
Vec2D get renderIn => weight?.inTranslation ?? inPoint;
|
Vec2D get renderIn => weight?.inTranslation ?? inPoint;
|
||||||
Vec2D get renderOut => weight?.outTranslation ?? outPoint;
|
Vec2D get renderOut => weight?.outTranslation ?? outPoint;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void deform(Mat2D world, Float32List boneTransforms) {
|
void deform(Mat2D world, Float32List boneTransforms) {
|
||||||
super.deform(world, boneTransforms);
|
super.deform(world, boneTransforms);
|
||||||
|
|
||||||
Weight.deform(outPoint[0], outPoint[1], weight!.outIndices,
|
Weight.deform(outPoint[0], outPoint[1], weight!.outIndices,
|
||||||
weight!.outValues, world, boneTransforms, weight!.outTranslation);
|
weight!.outValues, world, boneTransforms, weight!.outTranslation);
|
||||||
Weight.deform(inPoint[0], inPoint[1], weight!.inIndices, weight!.inValues,
|
Weight.deform(inPoint[0], inPoint[1], weight!.inIndices, weight!.inValues,
|
||||||
|
@ -2,6 +2,7 @@ import 'package:rive/src/rive_core/math/circle_constant.dart';
|
|||||||
import 'package:rive/src/rive_core/shapes/cubic_detached_vertex.dart';
|
import 'package:rive/src/rive_core/shapes/cubic_detached_vertex.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/path_vertex.dart';
|
import 'package:rive/src/rive_core/shapes/path_vertex.dart';
|
||||||
import 'package:rive/src/generated/shapes/ellipse_base.dart';
|
import 'package:rive/src/generated/shapes/ellipse_base.dart';
|
||||||
|
|
||||||
export 'package:rive/src/generated/shapes/ellipse_base.dart';
|
export 'package:rive/src/generated/shapes/ellipse_base.dart';
|
||||||
|
|
||||||
class Ellipse extends EllipseBase {
|
class Ellipse extends EllipseBase {
|
||||||
@ -9,35 +10,40 @@ class Ellipse extends EllipseBase {
|
|||||||
List<PathVertex> get vertices {
|
List<PathVertex> get vertices {
|
||||||
double ox = -originX * width + radiusX;
|
double ox = -originX * width + radiusX;
|
||||||
double oy = -originY * height + radiusY;
|
double oy = -originY * height + radiusY;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
CubicDetachedVertex.fromValues(
|
CubicDetachedVertex.fromValues(
|
||||||
x: ox,
|
x: ox,
|
||||||
y: oy - radiusY,
|
y: oy - radiusY,
|
||||||
inX: ox - radiusX * circleConstant,
|
inX: ox - radiusX * circleConstant,
|
||||||
inY: oy - radiusY,
|
inY: oy - radiusY,
|
||||||
outX: ox + radiusX * circleConstant,
|
outX: ox + radiusX * circleConstant,
|
||||||
outY: oy - radiusY),
|
outY: oy - radiusY,
|
||||||
|
),
|
||||||
CubicDetachedVertex.fromValues(
|
CubicDetachedVertex.fromValues(
|
||||||
x: ox + radiusX,
|
x: ox + radiusX,
|
||||||
y: oy,
|
y: oy,
|
||||||
inX: ox + radiusX,
|
inX: ox + radiusX,
|
||||||
inY: oy + circleConstant * -radiusY,
|
inY: oy + circleConstant * -radiusY,
|
||||||
outX: ox + radiusX,
|
outX: ox + radiusX,
|
||||||
outY: oy + circleConstant * radiusY),
|
outY: oy + circleConstant * radiusY,
|
||||||
|
),
|
||||||
CubicDetachedVertex.fromValues(
|
CubicDetachedVertex.fromValues(
|
||||||
x: ox,
|
x: ox,
|
||||||
y: oy + radiusY,
|
y: oy + radiusY,
|
||||||
inX: ox + radiusX * circleConstant,
|
inX: ox + radiusX * circleConstant,
|
||||||
inY: oy + radiusY,
|
inY: oy + radiusY,
|
||||||
outX: ox - radiusX * circleConstant,
|
outX: ox - radiusX * circleConstant,
|
||||||
outY: oy + radiusY),
|
outY: oy + radiusY,
|
||||||
|
),
|
||||||
CubicDetachedVertex.fromValues(
|
CubicDetachedVertex.fromValues(
|
||||||
x: ox - radiusX,
|
x: ox - radiusX,
|
||||||
y: oy,
|
y: oy,
|
||||||
inX: ox - radiusX,
|
inX: ox - radiusX,
|
||||||
inY: oy + radiusY * circleConstant,
|
inY: oy + radiusY * circleConstant,
|
||||||
outX: ox - radiusX,
|
outX: ox - radiusX,
|
||||||
outY: oy - radiusY * circleConstant)
|
outY: oy - radiusY * circleConstant,
|
||||||
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,19 +1,28 @@
|
|||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:rive/src/rive_core/component_dirt.dart';
|
import 'package:rive/src/rive_core/component_dirt.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/shape_paint_container.dart';
|
import 'package:rive/src/rive_core/shapes/shape_paint_container.dart';
|
||||||
import 'package:rive/src/generated/shapes/paint/fill_base.dart';
|
import 'package:rive/src/generated/shapes/paint/fill_base.dart';
|
||||||
export 'package:rive/src/generated/shapes/paint/fill_base.dart';
|
export 'package:rive/src/generated/shapes/paint/fill_base.dart';
|
||||||
|
|
||||||
|
/// A fill Shape painter.
|
||||||
class Fill extends FillBase {
|
class Fill extends FillBase {
|
||||||
@override
|
@override
|
||||||
Paint makePaint() => Paint()..style = PaintingStyle.fill;
|
Paint makePaint() => Paint()..style = PaintingStyle.fill;
|
||||||
|
|
||||||
PathFillType get fillType => PathFillType.values[fillRule];
|
PathFillType get fillType => PathFillType.values[fillRule];
|
||||||
set fillType(PathFillType type) => fillRule = type.index;
|
set fillType(PathFillType type) => fillRule = type.index;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void fillRuleChanged(int from, int to) =>
|
void fillRuleChanged(int from, int to) =>
|
||||||
parent?.addDirt(ComponentDirt.paint);
|
parent?.addDirt(ComponentDirt.paint);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void update(int dirt) {}
|
void update(int dirt) {
|
||||||
|
// Intentionally empty, fill doesn't update.
|
||||||
|
// Because Fill never adds dependencies, it'll also never get called.
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onAdded() {
|
void onAdded() {
|
||||||
super.onAdded();
|
super.onAdded();
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import 'dart:ui' as ui;
|
import 'dart:ui' as ui;
|
||||||
|
|
||||||
import 'package:rive/src/rive_core/container_component.dart';
|
import 'package:rive/src/rive_core/container_component.dart';
|
||||||
import 'package:rive/src/generated/shapes/paint/gradient_stop_base.dart';
|
import 'package:rive/src/generated/shapes/paint/gradient_stop_base.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/paint/linear_gradient.dart';
|
import 'package:rive/src/rive_core/shapes/paint/linear_gradient.dart';
|
||||||
|
|
||||||
export 'package:rive/src/generated/shapes/paint/gradient_stop_base.dart';
|
export 'package:rive/src/generated/shapes/paint/gradient_stop_base.dart';
|
||||||
|
|
||||||
class GradientStop extends GradientStopBase {
|
class GradientStop extends GradientStopBase {
|
||||||
@ -24,8 +26,10 @@ class GradientStop extends GradientStopBase {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void update(int dirt) {}
|
void update(int dirt) {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool validate() => super.validate() && _gradient != null;
|
bool validate() => super.validate() && _gradient != null;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void parentChanged(ContainerComponent? from, ContainerComponent? to) {
|
void parentChanged(ContainerComponent? from, ContainerComponent? to) {
|
||||||
super.parentChanged(from, to);
|
super.parentChanged(from, to);
|
||||||
|
@ -1,15 +1,24 @@
|
|||||||
import 'dart:ui' as ui;
|
import 'dart:ui' as ui;
|
||||||
|
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
import 'package:rive/src/rive_core/component.dart';
|
import 'package:rive/src/rive_core/component.dart';
|
||||||
import 'package:rive/src/rive_core/component_dirt.dart';
|
import 'package:rive/src/rive_core/component_dirt.dart';
|
||||||
import 'package:rive/src/rive_core/math/vec2d.dart';
|
import 'package:rive/src/rive_core/math/vec2d.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/paint/gradient_stop.dart';
|
import 'package:rive/src/rive_core/shapes/paint/gradient_stop.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/paint/shape_paint_mutator.dart';
|
import 'package:rive/src/rive_core/shapes/paint/shape_paint_mutator.dart';
|
||||||
|
import 'package:rive/src/rive_core/shapes/shape.dart';
|
||||||
import 'package:rive/src/generated/shapes/paint/linear_gradient_base.dart';
|
import 'package:rive/src/generated/shapes/paint/linear_gradient_base.dart';
|
||||||
export 'package:rive/src/generated/shapes/paint/linear_gradient_base.dart';
|
export 'package:rive/src/generated/shapes/paint/linear_gradient_base.dart';
|
||||||
|
|
||||||
|
/// A core linear gradient. Can be added as a child to a [Shape]'s [Fill] or
|
||||||
|
/// [Stroke] to paint that Fill or Stroke with a gradient. This is the
|
||||||
|
/// foundation for the RadialGradient which is very similar but also has a
|
||||||
|
/// radius value.
|
||||||
class LinearGradient extends LinearGradientBase with ShapePaintMutator {
|
class LinearGradient extends LinearGradientBase with ShapePaintMutator {
|
||||||
|
/// Stored list of core gradient stops are in the hierarchy as children of
|
||||||
|
/// this container.
|
||||||
final List<GradientStop> gradientStops = [];
|
final List<GradientStop> gradientStops = [];
|
||||||
|
|
||||||
bool _paintsInWorldSpace = true;
|
bool _paintsInWorldSpace = true;
|
||||||
bool get paintsInWorldSpace => _paintsInWorldSpace;
|
bool get paintsInWorldSpace => _paintsInWorldSpace;
|
||||||
set paintsInWorldSpace(bool value) {
|
set paintsInWorldSpace(bool value) {
|
||||||
@ -22,8 +31,11 @@ class LinearGradient extends LinearGradientBase with ShapePaintMutator {
|
|||||||
|
|
||||||
Vec2D get start => Vec2D.fromValues(startX, startY);
|
Vec2D get start => Vec2D.fromValues(startX, startY);
|
||||||
Vec2D get end => Vec2D.fromValues(endX, endY);
|
Vec2D get end => Vec2D.fromValues(endX, endY);
|
||||||
|
|
||||||
ui.Offset get startOffset => ui.Offset(startX, startY);
|
ui.Offset get startOffset => ui.Offset(startX, startY);
|
||||||
ui.Offset get endOffset => ui.Offset(endX, endY);
|
ui.Offset get endOffset => ui.Offset(endX, endY);
|
||||||
|
|
||||||
|
/// Gradients depends on their shape.
|
||||||
@override
|
@override
|
||||||
void buildDependencies() {
|
void buildDependencies() {
|
||||||
super.buildDependencies();
|
super.buildDependencies();
|
||||||
@ -35,6 +47,7 @@ class LinearGradient extends LinearGradientBase with ShapePaintMutator {
|
|||||||
super.childAdded(child);
|
super.childAdded(child);
|
||||||
if (child is GradientStop && !gradientStops.contains(child)) {
|
if (child is GradientStop && !gradientStops.contains(child)) {
|
||||||
gradientStops.add(child);
|
gradientStops.add(child);
|
||||||
|
|
||||||
markStopsDirty();
|
markStopsDirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -44,31 +57,48 @@ class LinearGradient extends LinearGradientBase with ShapePaintMutator {
|
|||||||
super.childRemoved(child);
|
super.childRemoved(child);
|
||||||
if (child is GradientStop && gradientStops.contains(child)) {
|
if (child is GradientStop && gradientStops.contains(child)) {
|
||||||
gradientStops.remove(child);
|
gradientStops.remove(child);
|
||||||
|
|
||||||
markStopsDirty();
|
markStopsDirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Mark the gradient stops as changed. This will re-sort the stops and
|
||||||
|
/// rebuild the necessary gradients in the next update cycle.
|
||||||
void markStopsDirty() => addDirt(ComponentDirt.stops | ComponentDirt.paint);
|
void markStopsDirty() => addDirt(ComponentDirt.stops | ComponentDirt.paint);
|
||||||
|
|
||||||
|
/// Mark the gradient as needing to be rebuilt. This is a more efficient
|
||||||
|
/// version of markStopsDirty as it won't re-sort the stops.
|
||||||
void markGradientDirty() => addDirt(ComponentDirt.paint);
|
void markGradientDirty() => addDirt(ComponentDirt.paint);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void update(int dirt) {
|
void update(int dirt) {
|
||||||
|
// Do the stops need to be re-ordered?
|
||||||
bool stopsChanged = dirt & ComponentDirt.stops != 0;
|
bool stopsChanged = dirt & ComponentDirt.stops != 0;
|
||||||
if (stopsChanged) {
|
if (stopsChanged) {
|
||||||
gradientStops.sort((a, b) => a.position.compareTo(b.position));
|
gradientStops.sort((a, b) => a.position.compareTo(b.position));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool worldTransformed = dirt & ComponentDirt.worldTransform != 0;
|
bool worldTransformed = dirt & ComponentDirt.worldTransform != 0;
|
||||||
bool localTransformed = dirt & ComponentDirt.transform != 0;
|
bool localTransformed = dirt & ComponentDirt.transform != 0;
|
||||||
|
|
||||||
|
// We rebuild the gradient if the gradient is dirty or we paint in world
|
||||||
|
// space and the world space transform has changed, or the local transform
|
||||||
|
// has changed. Local transform changes when a stop moves in local space.
|
||||||
var rebuildGradient = dirt & ComponentDirt.paint != 0 ||
|
var rebuildGradient = dirt & ComponentDirt.paint != 0 ||
|
||||||
localTransformed ||
|
localTransformed ||
|
||||||
(paintsInWorldSpace && worldTransformed);
|
(paintsInWorldSpace && worldTransformed);
|
||||||
if (rebuildGradient) {
|
if (rebuildGradient) {
|
||||||
|
// build up the color and positions lists
|
||||||
var colors = <ui.Color>[];
|
var colors = <ui.Color>[];
|
||||||
var colorPositions = <double>[];
|
var colorPositions = <double>[];
|
||||||
for (final stop in gradientStops) {
|
for (final stop in gradientStops) {
|
||||||
colors.add(stop.color);
|
colors.add(stop.color);
|
||||||
colorPositions.add(stop.position);
|
colorPositions.add(stop.position);
|
||||||
}
|
}
|
||||||
|
// Check if we need to update the world space gradient.
|
||||||
if (paintsInWorldSpace) {
|
if (paintsInWorldSpace) {
|
||||||
|
// Get the start and end of the gradient in world coordinates (world
|
||||||
|
// transform of the shape).
|
||||||
var world = shapePaintContainer!.worldTransform;
|
var world = shapePaintContainer!.worldTransform;
|
||||||
var worldStart = Vec2D.transformMat2D(Vec2D(), start, world);
|
var worldStart = Vec2D.transformMat2D(Vec2D(), start, world);
|
||||||
var worldEnd = Vec2D.transformMat2D(Vec2D(), end, world);
|
var worldEnd = Vec2D.transformMat2D(Vec2D(), end, world);
|
||||||
@ -85,6 +115,7 @@ class LinearGradient extends LinearGradientBase with ShapePaintMutator {
|
|||||||
ui.Gradient makeGradient(ui.Offset start, ui.Offset end,
|
ui.Gradient makeGradient(ui.Offset start, ui.Offset end,
|
||||||
List<ui.Color> colors, List<double> colorPositions) =>
|
List<ui.Color> colors, List<double> colorPositions) =>
|
||||||
ui.Gradient.linear(start, end, colors, colorPositions);
|
ui.Gradient.linear(start, end, colors, colorPositions);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void startXChanged(double from, double to) {
|
void startXChanged(double from, double to) {
|
||||||
addDirt(ComponentDirt.transform);
|
addDirt(ComponentDirt.transform);
|
||||||
@ -114,6 +145,8 @@ class LinearGradient extends LinearGradientBase with ShapePaintMutator {
|
|||||||
@override
|
@override
|
||||||
void opacityChanged(double from, double to) {
|
void opacityChanged(double from, double to) {
|
||||||
syncColor();
|
syncColor();
|
||||||
|
// We don't need to rebuild anything, just let our shape know we should
|
||||||
|
// repaint.
|
||||||
shapePaintContainer!.addDirt(ComponentDirt.paint);
|
shapePaintContainer!.addDirt(ComponentDirt.paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,8 @@ import 'package:rive/src/generated/shapes/paint/radial_gradient_base.dart';
|
|||||||
export 'package:rive/src/generated/shapes/paint/radial_gradient_base.dart';
|
export 'package:rive/src/generated/shapes/paint/radial_gradient_base.dart';
|
||||||
|
|
||||||
class RadialGradient extends RadialGradientBase {
|
class RadialGradient extends RadialGradientBase {
|
||||||
|
/// We override the make gradient operation to create a radial gradient
|
||||||
|
/// instead of a linear one.
|
||||||
@override
|
@override
|
||||||
ui.Gradient makeGradient(ui.Offset start, ui.Offset end,
|
ui.Gradient makeGradient(ui.Offset start, ui.Offset end,
|
||||||
List<ui.Color> colors, List<double> colorPositions) =>
|
List<ui.Color> colors, List<double> colorPositions) =>
|
||||||
|
@ -1,34 +1,48 @@
|
|||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
import 'package:rive/src/rive_core/component.dart';
|
import 'package:rive/src/rive_core/component.dart';
|
||||||
import 'package:rive/src/rive_core/component_dirt.dart';
|
import 'package:rive/src/rive_core/component_dirt.dart';
|
||||||
import 'package:rive/src/rive_core/container_component.dart';
|
import 'package:rive/src/rive_core/container_component.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/paint/shape_paint_mutator.dart';
|
import 'package:rive/src/rive_core/shapes/paint/shape_paint_mutator.dart';
|
||||||
|
import 'package:rive/src/rive_core/shapes/shape.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/shape_paint_container.dart';
|
import 'package:rive/src/rive_core/shapes/shape_paint_container.dart';
|
||||||
import 'package:rive/src/generated/shapes/paint/shape_paint_base.dart';
|
import 'package:rive/src/generated/shapes/paint/shape_paint_base.dart';
|
||||||
export 'package:rive/src/generated/shapes/paint/shape_paint_base.dart';
|
export 'package:rive/src/generated/shapes/paint/shape_paint_base.dart';
|
||||||
|
|
||||||
|
/// Generic ShapePaint that abstracts Stroke and Fill. Automatically hooks up
|
||||||
|
/// parent [Shape] to child [ShapePaintMutator]s.
|
||||||
abstract class ShapePaint extends ShapePaintBase {
|
abstract class ShapePaint extends ShapePaintBase {
|
||||||
late Paint _paint;
|
late Paint _paint;
|
||||||
Paint get paint => _paint;
|
Paint get paint => _paint;
|
||||||
ShapePaintMutator? _paintMutator;
|
ShapePaintMutator? _paintMutator;
|
||||||
ShapePaintContainer? get shapePaintContainer =>
|
ShapePaintContainer? get shapePaintContainer =>
|
||||||
parent is ShapePaintContainer ? parent as ShapePaintContainer : null;
|
parent is ShapePaintContainer ? parent as ShapePaintContainer : null;
|
||||||
|
|
||||||
ShapePaint() {
|
ShapePaint() {
|
||||||
_paint = makePaint();
|
_paint = makePaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
BlendMode get blendMode => _paint.blendMode;
|
BlendMode get blendMode => _paint.blendMode;
|
||||||
set blendMode(BlendMode value) => _paint.blendMode = value;
|
set blendMode(BlendMode value) => _paint.blendMode = value;
|
||||||
|
|
||||||
double get renderOpacity => _paintMutator!.renderOpacity;
|
double get renderOpacity => _paintMutator!.renderOpacity;
|
||||||
set renderOpacity(double value) => _paintMutator!.renderOpacity = value;
|
set renderOpacity(double value) => _paintMutator!.renderOpacity = value;
|
||||||
|
|
||||||
ShapePaintMutator? get paintMutator => _paintMutator;
|
ShapePaintMutator? get paintMutator => _paintMutator;
|
||||||
|
|
||||||
void _changeMutator(ShapePaintMutator? mutator) {
|
void _changeMutator(ShapePaintMutator? mutator) {
|
||||||
_paint = makePaint();
|
_paint = makePaint();
|
||||||
_paintMutator = mutator;
|
_paintMutator = mutator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Implementing classes are expected to override this to create a paint
|
||||||
|
/// object. This gets called whenever the mutator is changed in order to not
|
||||||
|
/// require each mutator to manually reset the paint to some canonical state.
|
||||||
|
/// Instead, we simply blow out the old one and make a new one.
|
||||||
@protected
|
@protected
|
||||||
Paint makePaint();
|
Paint makePaint();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void childAdded(Component child) {
|
void childAdded(Component child) {
|
||||||
super.childAdded(child);
|
super.childAdded(child);
|
||||||
@ -53,6 +67,7 @@ abstract class ShapePaint extends ShapePaintBase {
|
|||||||
super.validate() &&
|
super.validate() &&
|
||||||
parent is ShapePaintContainer &&
|
parent is ShapePaintContainer &&
|
||||||
_paintMutator != null;
|
_paintMutator != null;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void isVisibleChanged(bool from, bool to) {
|
void isVisibleChanged(bool from, bool to) {
|
||||||
shapePaintContainer?.addDirt(ComponentDirt.paint);
|
shapePaintContainer?.addDirt(ComponentDirt.paint);
|
||||||
@ -61,6 +76,8 @@ abstract class ShapePaint extends ShapePaintBase {
|
|||||||
@override
|
@override
|
||||||
void childRemoved(Component child) {
|
void childRemoved(Component child) {
|
||||||
super.childRemoved(child);
|
super.childRemoved(child);
|
||||||
|
// Make sure to clean up any references so that they can be garbage
|
||||||
|
// collected.
|
||||||
if (child is ShapePaintMutator &&
|
if (child is ShapePaintMutator &&
|
||||||
_paintMutator == child as ShapePaintMutator) {
|
_paintMutator == child as ShapePaintMutator) {
|
||||||
_changeMutator(null);
|
_changeMutator(null);
|
||||||
@ -69,5 +86,6 @@ abstract class ShapePaint extends ShapePaintBase {
|
|||||||
|
|
||||||
void _initMutator() =>
|
void _initMutator() =>
|
||||||
_paintMutator?.initializePaintMutator(shapePaintContainer!, paint);
|
_paintMutator?.initializePaintMutator(shapePaintContainer!, paint);
|
||||||
|
|
||||||
void draw(Canvas canvas, Path path);
|
void draw(Canvas canvas, Path path);
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/shape_paint_container.dart';
|
import 'package:rive/src/rive_core/shapes/shape_paint_container.dart';
|
||||||
|
|
||||||
abstract class ShapePaintMutator {
|
abstract class ShapePaintMutator {
|
||||||
ShapePaintContainer? _shapePaintContainer;
|
ShapePaintContainer? _shapePaintContainer;
|
||||||
Paint _paint = Paint();
|
Paint _paint = Paint();
|
||||||
|
|
||||||
|
/// The container is usually either a Shape or an Artboard, basically any of
|
||||||
|
/// the various ContainerComponents that can contain Fills or Strokes.
|
||||||
ShapePaintContainer? get shapePaintContainer => _shapePaintContainer;
|
ShapePaintContainer? get shapePaintContainer => _shapePaintContainer;
|
||||||
Paint get paint => _paint;
|
Paint get paint => _paint;
|
||||||
|
|
||||||
double _renderOpacity = 1;
|
double _renderOpacity = 1;
|
||||||
double get renderOpacity => _renderOpacity;
|
double get renderOpacity => _renderOpacity;
|
||||||
set renderOpacity(double value) {
|
set renderOpacity(double value) {
|
||||||
@ -18,6 +23,7 @@ abstract class ShapePaintMutator {
|
|||||||
|
|
||||||
@protected
|
@protected
|
||||||
void syncColor();
|
void syncColor();
|
||||||
|
|
||||||
@mustCallSuper
|
@mustCallSuper
|
||||||
void initializePaintMutator(ShapePaintContainer container, Paint paint) {
|
void initializePaintMutator(ShapePaintContainer container, Paint paint) {
|
||||||
_shapePaintContainer = container;
|
_shapePaintContainer = container;
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:rive/src/rive_core/component_dirt.dart';
|
import 'package:rive/src/rive_core/component_dirt.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/paint/shape_paint.dart';
|
import 'package:rive/src/rive_core/shapes/paint/shape_paint.dart';
|
||||||
import 'package:rive/src/rive_core/shapes/paint/shape_paint_mutator.dart';
|
import 'package:rive/src/rive_core/shapes/paint/shape_paint_mutator.dart';
|
||||||
import 'package:rive/src/generated/shapes/paint/solid_color_base.dart';
|
import 'package:rive/src/generated/shapes/paint/solid_color_base.dart';
|
||||||
export 'package:rive/src/generated/shapes/paint/solid_color_base.dart';
|
export 'package:rive/src/generated/shapes/paint/solid_color_base.dart';
|
||||||
|
|
||||||
|
/// A solid color painter for a shape. Works for both Fill and Stroke.
|
||||||
class SolidColor extends SolidColorBase with ShapePaintMutator {
|
class SolidColor extends SolidColorBase with ShapePaintMutator {
|
||||||
Color get color => Color(colorValue);
|
Color get color => Color(colorValue);
|
||||||
set color(Color c) {
|
set color(Color c) {
|
||||||
@ -13,12 +15,24 @@ class SolidColor extends SolidColorBase with ShapePaintMutator {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void colorValueChanged(int from, int to) {
|
void colorValueChanged(int from, int to) {
|
||||||
|
// Since all we need to do is set the color on the paint, we can just do
|
||||||
|
// this whenever it changes as it's such a lightweight operation. We don't
|
||||||
|
// need to schedule it for the next update cycle, which saves us from adding
|
||||||
|
// SolidColor to the dependencies graph.
|
||||||
syncColor();
|
syncColor();
|
||||||
|
|
||||||
|
// Since we're not in the dependency tree, chuck dirt onto the shape, which
|
||||||
|
// is. This just ensures we'll paint as soon as possible to show the updated
|
||||||
|
// color.
|
||||||
shapePaintContainer?.addDirt(ComponentDirt.paint);
|
shapePaintContainer?.addDirt(ComponentDirt.paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void update(int dirt) {}
|
void update(int dirt) {
|
||||||
|
// Intentionally empty. SolidColor doesn't need an update cycle and doesn't
|
||||||
|
// depend on anything.
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void syncColor() {
|
void syncColor() {
|
||||||
paint.color = color
|
paint.color = color
|
||||||
@ -27,6 +41,7 @@ class SolidColor extends SolidColorBase with ShapePaintMutator {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool validate() => super.validate() && parent is ShapePaint;
|
bool validate() => super.validate() && parent is ShapePaint;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onAdded() {
|
void onAdded() {
|
||||||
super.onAdded();
|
super.onAdded();
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user