Joystick ordering

Fixes issue with dependencies discussed here:
https://github.com/rive-app/rive/pull/5275

Joysticks now build their own DAG and export in the correct order for runtime.

Diffs=
f2ecb824b Joystick ordering (#5277)
This commit is contained in:
luigi-rosso
2023-05-23 05:23:17 +00:00
parent a0e536a331
commit 6395bceaf7
16 changed files with 535 additions and 114 deletions

View File

@ -1 +1 @@
a4fb3dc7decb50f6965e21ebced098cbdc35883d f2ecb824beea0dadfe7077accacb5cd246cac669

View File

@ -1,3 +1,7 @@
## 0.11.1
- Joysticks with custom handle sources.
## 0.11.0 ## 0.11.0
- Joysticks! - Joysticks!

View File

@ -5,22 +5,16 @@ import 'package:rive/rive.dart';
class SimpleAssetAnimation extends StatelessWidget { class SimpleAssetAnimation extends StatelessWidget {
const SimpleAssetAnimation({Key? key}) : super(key: key); const SimpleAssetAnimation({Key? key}) : super(key: key);
void _onRiveInit(Artboard artboard) {
final controller = StateMachineController.fromArtboard(artboard, 'Coyote');
artboard.addController(controller!);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('Simple Animation'), title: const Text('Simple Animation'),
), ),
body: Center( body: const Center(
child: RiveAnimation.asset( child: RiveAnimation.asset(
'assets/coyote.riv', 'assets/off_road_car.riv',
fit: BoxFit.cover, fit: BoxFit.cover,
onInit: _onRiveInit,
), ),
), ),
); );

View File

@ -15,8 +15,8 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS: SPEC CHECKSUMS:
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
rive_common: 643f5c20ccd60c53136ab5c970eccf0d17e010fe rive_common: fab8476ce8352bf54152a913f393a8696d3dc98c
PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7 PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7
COCOAPODS: 1.11.3 COCOAPODS: 1.12.0

View File

@ -54,6 +54,142 @@ abstract class JoystickBase extends Component {
void yChanged(double from, double to); void yChanged(double from, double to);
/// --------------------------------------------------------------------------
/// PosX field with key 303.
static const double posXInitialValue = 0;
double _posX = posXInitialValue;
static const int posXPropertyKey = 303;
double get posX => _posX;
/// Change the [_posX] field value.
/// [posXChanged] will be invoked only if the field's value has changed.
set posX(double value) {
if (_posX == value) {
return;
}
double from = _posX;
_posX = value;
if (hasValidated) {
posXChanged(from, value);
}
}
void posXChanged(double from, double to);
/// --------------------------------------------------------------------------
/// PosY field with key 304.
static const double posYInitialValue = 0;
double _posY = posYInitialValue;
static const int posYPropertyKey = 304;
double get posY => _posY;
/// Change the [_posY] field value.
/// [posYChanged] will be invoked only if the field's value has changed.
set posY(double value) {
if (_posY == value) {
return;
}
double from = _posY;
_posY = value;
if (hasValidated) {
posYChanged(from, value);
}
}
void posYChanged(double from, double to);
/// --------------------------------------------------------------------------
/// OriginX field with key 307.
static const double originXInitialValue = 0.5;
double _originX = originXInitialValue;
static const int originXPropertyKey = 307;
/// Origin x in normalized coordinates (0.5 = center, 0 = left, 1 = right).
double get originX => _originX;
/// Change the [_originX] field value.
/// [originXChanged] will be invoked only if the field's value has changed.
set originX(double value) {
if (_originX == value) {
return;
}
double from = _originX;
_originX = value;
if (hasValidated) {
originXChanged(from, value);
}
}
void originXChanged(double from, double to);
/// --------------------------------------------------------------------------
/// OriginY field with key 308.
static const double originYInitialValue = 0.5;
double _originY = originYInitialValue;
static const int originYPropertyKey = 308;
/// Origin y in normalized coordinates (0.5 = center, 0 = top, 1 = bottom).
double get originY => _originY;
/// Change the [_originY] field value.
/// [originYChanged] will be invoked only if the field's value has changed.
set originY(double value) {
if (_originY == value) {
return;
}
double from = _originY;
_originY = value;
if (hasValidated) {
originYChanged(from, value);
}
}
void originYChanged(double from, double to);
/// --------------------------------------------------------------------------
/// Width field with key 305.
static const double widthInitialValue = 100;
double _width = widthInitialValue;
static const int widthPropertyKey = 305;
double get width => _width;
/// Change the [_width] field value.
/// [widthChanged] will be invoked only if the field's value has changed.
set width(double value) {
if (_width == value) {
return;
}
double from = _width;
_width = value;
if (hasValidated) {
widthChanged(from, value);
}
}
void widthChanged(double from, double to);
/// --------------------------------------------------------------------------
/// Height field with key 306.
static const double heightInitialValue = 100;
double _height = heightInitialValue;
static const int heightPropertyKey = 306;
double get height => _height;
/// Change the [_height] field value.
/// [heightChanged] will be invoked only if the field's value has changed.
set height(double value) {
if (_height == value) {
return;
}
double from = _height;
_height = value;
if (hasValidated) {
heightChanged(from, value);
}
}
void heightChanged(double from, double to);
/// -------------------------------------------------------------------------- /// --------------------------------------------------------------------------
/// XId field with key 301. /// XId field with key 301.
static const int xIdInitialValue = -1; static const int xIdInitialValue = -1;
@ -79,30 +215,6 @@ abstract class JoystickBase extends Component {
void xIdChanged(int from, int to); void xIdChanged(int from, int to);
/// --------------------------------------------------------------------------
/// XInvert field with key 310.
static const bool xInvertInitialValue = false;
bool _xInvert = xInvertInitialValue;
static const int xInvertPropertyKey = 310;
/// Whether to invert the application of the x axis.
bool get xInvert => _xInvert;
/// Change the [_xInvert] field value.
/// [xInvertChanged] will be invoked only if the field's value has changed.
set xInvert(bool value) {
if (_xInvert == value) {
return;
}
bool from = _xInvert;
_xInvert = value;
if (hasValidated) {
xInvertChanged(from, value);
}
}
void xInvertChanged(bool from, bool to);
/// -------------------------------------------------------------------------- /// --------------------------------------------------------------------------
/// YId field with key 302. /// YId field with key 302.
static const int yIdInitialValue = -1; static const int yIdInitialValue = -1;
@ -129,37 +241,67 @@ abstract class JoystickBase extends Component {
void yIdChanged(int from, int to); void yIdChanged(int from, int to);
/// -------------------------------------------------------------------------- /// --------------------------------------------------------------------------
/// YInvert field with key 311. /// JoystickFlags field with key 312.
static const bool yInvertInitialValue = false; static const int joystickFlagsInitialValue = 0;
bool _yInvert = yInvertInitialValue; int _joystickFlags = joystickFlagsInitialValue;
static const int yInvertPropertyKey = 311; static const int joystickFlagsPropertyKey = 312;
int get joystickFlags => _joystickFlags;
/// Whether to invert the application of the y axis. /// Change the [_joystickFlags] field value.
bool get yInvert => _yInvert; /// [joystickFlagsChanged] will be invoked only if the field's value has
/// changed.
/// Change the [_yInvert] field value. set joystickFlags(int value) {
/// [yInvertChanged] will be invoked only if the field's value has changed. if (_joystickFlags == value) {
set yInvert(bool value) {
if (_yInvert == value) {
return; return;
} }
bool from = _yInvert; int from = _joystickFlags;
_yInvert = value; _joystickFlags = value;
if (hasValidated) { if (hasValidated) {
yInvertChanged(from, value); joystickFlagsChanged(from, value);
} }
} }
void yInvertChanged(bool from, bool to); void joystickFlagsChanged(int from, int to);
/// --------------------------------------------------------------------------
/// HandleSourceId field with key 313.
static const int handleSourceIdInitialValue = -1;
int _handleSourceId = handleSourceIdInitialValue;
static const int handleSourceIdPropertyKey = 313;
/// Identifier used to track the custom handle source of the joystick.
int get handleSourceId => _handleSourceId;
/// Change the [_handleSourceId] field value.
/// [handleSourceIdChanged] will be invoked only if the field's value has
/// changed.
set handleSourceId(int value) {
if (_handleSourceId == value) {
return;
}
int from = _handleSourceId;
_handleSourceId = value;
if (hasValidated) {
handleSourceIdChanged(from, value);
}
}
void handleSourceIdChanged(int from, int to);
@override @override
void copy(covariant JoystickBase source) { void copy(covariant JoystickBase source) {
super.copy(source); super.copy(source);
_x = source._x; _x = source._x;
_y = source._y; _y = source._y;
_posX = source._posX;
_posY = source._posY;
_originX = source._originX;
_originY = source._originY;
_width = source._width;
_height = source._height;
_xId = source._xId; _xId = source._xId;
_xInvert = source._xInvert;
_yId = source._yId; _yId = source._yId;
_yInvert = source._yInvert; _joystickFlags = source._joystickFlags;
_handleSourceId = source._handleSourceId;
} }
} }

View File

@ -1134,24 +1134,54 @@ class RiveCoreContext {
object.y = value; object.y = value;
} }
break; break;
case JoystickBase.posXPropertyKey:
if (object is JoystickBase && value is double) {
object.posX = value;
}
break;
case JoystickBase.posYPropertyKey:
if (object is JoystickBase && value is double) {
object.posY = value;
}
break;
case JoystickBase.originXPropertyKey:
if (object is JoystickBase && value is double) {
object.originX = value;
}
break;
case JoystickBase.originYPropertyKey:
if (object is JoystickBase && value is double) {
object.originY = value;
}
break;
case JoystickBase.widthPropertyKey:
if (object is JoystickBase && value is double) {
object.width = value;
}
break;
case JoystickBase.heightPropertyKey:
if (object is JoystickBase && value is double) {
object.height = value;
}
break;
case JoystickBase.xIdPropertyKey: case JoystickBase.xIdPropertyKey:
if (object is JoystickBase && value is int) { if (object is JoystickBase && value is int) {
object.xId = value; object.xId = value;
} }
break; break;
case JoystickBase.xInvertPropertyKey:
if (object is JoystickBase && value is bool) {
object.xInvert = value;
}
break;
case JoystickBase.yIdPropertyKey: case JoystickBase.yIdPropertyKey:
if (object is JoystickBase && value is int) { if (object is JoystickBase && value is int) {
object.yId = value; object.yId = value;
} }
break; break;
case JoystickBase.yInvertPropertyKey: case JoystickBase.joystickFlagsPropertyKey:
if (object is JoystickBase && value is bool) { if (object is JoystickBase && value is int) {
object.yInvert = value; object.joystickFlags = value;
}
break;
case JoystickBase.handleSourceIdPropertyKey:
if (object is JoystickBase && value is int) {
object.handleSourceId = value;
} }
break; break;
case BoneBase.lengthPropertyKey: case BoneBase.lengthPropertyKey:
@ -1400,6 +1430,8 @@ class RiveCoreContext {
case ArtboardBase.defaultStateMachineIdPropertyKey: case ArtboardBase.defaultStateMachineIdPropertyKey:
case JoystickBase.xIdPropertyKey: case JoystickBase.xIdPropertyKey:
case JoystickBase.yIdPropertyKey: case JoystickBase.yIdPropertyKey:
case JoystickBase.joystickFlagsPropertyKey:
case JoystickBase.handleSourceIdPropertyKey:
case TendonBase.boneIdPropertyKey: case TendonBase.boneIdPropertyKey:
case TextStyleBase.fontAssetIdPropertyKey: case TextStyleBase.fontAssetIdPropertyKey:
case TextStyleAxisBase.tagPropertyKey: case TextStyleAxisBase.tagPropertyKey:
@ -1482,6 +1514,12 @@ class RiveCoreContext {
case ArtboardBase.originYPropertyKey: case ArtboardBase.originYPropertyKey:
case JoystickBase.xPropertyKey: case JoystickBase.xPropertyKey:
case JoystickBase.yPropertyKey: case JoystickBase.yPropertyKey:
case JoystickBase.posXPropertyKey:
case JoystickBase.posYPropertyKey:
case JoystickBase.originXPropertyKey:
case JoystickBase.originYPropertyKey:
case JoystickBase.widthPropertyKey:
case JoystickBase.heightPropertyKey:
case BoneBase.lengthPropertyKey: case BoneBase.lengthPropertyKey:
case RootBoneBase.xPropertyKey: case RootBoneBase.xPropertyKey:
case RootBoneBase.yPropertyKey: case RootBoneBase.yPropertyKey:
@ -1524,8 +1562,6 @@ class RiveCoreContext {
case ClippingShapeBase.isVisiblePropertyKey: case ClippingShapeBase.isVisiblePropertyKey:
case CustomPropertyBooleanBase.propertyValuePropertyKey: case CustomPropertyBooleanBase.propertyValuePropertyKey:
case ArtboardBase.clipPropertyKey: case ArtboardBase.clipPropertyKey:
case JoystickBase.xInvertPropertyKey:
case JoystickBase.yInvertPropertyKey:
return boolType; return boolType;
case KeyFrameColorBase.valuePropertyKey: case KeyFrameColorBase.valuePropertyKey:
case SolidColorBase.colorValuePropertyKey: case SolidColorBase.colorValuePropertyKey:
@ -1685,6 +1721,10 @@ class RiveCoreContext {
return (object as JoystickBase).xId; return (object as JoystickBase).xId;
case JoystickBase.yIdPropertyKey: case JoystickBase.yIdPropertyKey:
return (object as JoystickBase).yId; return (object as JoystickBase).yId;
case JoystickBase.joystickFlagsPropertyKey:
return (object as JoystickBase).joystickFlags;
case JoystickBase.handleSourceIdPropertyKey:
return (object as JoystickBase).handleSourceId;
case TendonBase.boneIdPropertyKey: case TendonBase.boneIdPropertyKey:
return (object as TendonBase).boneId; return (object as TendonBase).boneId;
case TextStyleBase.fontAssetIdPropertyKey: case TextStyleBase.fontAssetIdPropertyKey:
@ -1853,6 +1893,18 @@ class RiveCoreContext {
return (object as JoystickBase).x; return (object as JoystickBase).x;
case JoystickBase.yPropertyKey: case JoystickBase.yPropertyKey:
return (object as JoystickBase).y; return (object as JoystickBase).y;
case JoystickBase.posXPropertyKey:
return (object as JoystickBase).posX;
case JoystickBase.posYPropertyKey:
return (object as JoystickBase).posY;
case JoystickBase.originXPropertyKey:
return (object as JoystickBase).originX;
case JoystickBase.originYPropertyKey:
return (object as JoystickBase).originY;
case JoystickBase.widthPropertyKey:
return (object as JoystickBase).width;
case JoystickBase.heightPropertyKey:
return (object as JoystickBase).height;
case BoneBase.lengthPropertyKey: case BoneBase.lengthPropertyKey:
return (object as BoneBase).length; return (object as BoneBase).length;
case RootBoneBase.xPropertyKey: case RootBoneBase.xPropertyKey:
@ -1941,10 +1993,6 @@ class RiveCoreContext {
return (object as CustomPropertyBooleanBase).propertyValue; return (object as CustomPropertyBooleanBase).propertyValue;
case ArtboardBase.clipPropertyKey: case ArtboardBase.clipPropertyKey:
return (object as ArtboardBase).clip; return (object as ArtboardBase).clip;
case JoystickBase.xInvertPropertyKey:
return (object as JoystickBase).xInvert;
case JoystickBase.yInvertPropertyKey:
return (object as JoystickBase).yInvert;
} }
return false; return false;
} }
@ -2323,6 +2371,16 @@ class RiveCoreContext {
object.yId = value; object.yId = value;
} }
break; break;
case JoystickBase.joystickFlagsPropertyKey:
if (object is JoystickBase) {
object.joystickFlags = value;
}
break;
case JoystickBase.handleSourceIdPropertyKey:
if (object is JoystickBase) {
object.handleSourceId = value;
}
break;
case TendonBase.boneIdPropertyKey: case TendonBase.boneIdPropertyKey:
if (object is TendonBase) { if (object is TendonBase) {
object.boneId = value; object.boneId = value;
@ -2733,6 +2791,36 @@ class RiveCoreContext {
object.y = value; object.y = value;
} }
break; break;
case JoystickBase.posXPropertyKey:
if (object is JoystickBase) {
object.posX = value;
}
break;
case JoystickBase.posYPropertyKey:
if (object is JoystickBase) {
object.posY = value;
}
break;
case JoystickBase.originXPropertyKey:
if (object is JoystickBase) {
object.originX = value;
}
break;
case JoystickBase.originYPropertyKey:
if (object is JoystickBase) {
object.originY = value;
}
break;
case JoystickBase.widthPropertyKey:
if (object is JoystickBase) {
object.width = value;
}
break;
case JoystickBase.heightPropertyKey:
if (object is JoystickBase) {
object.height = value;
}
break;
case BoneBase.lengthPropertyKey: case BoneBase.lengthPropertyKey:
if (object is BoneBase) { if (object is BoneBase) {
object.length = value; object.length = value;
@ -2943,16 +3031,6 @@ class RiveCoreContext {
object.clip = value; object.clip = value;
} }
break; break;
case JoystickBase.xInvertPropertyKey:
if (object is JoystickBase) {
object.xInvert = value;
}
break;
case JoystickBase.yInvertPropertyKey:
if (object is JoystickBase) {
object.yInvert = value;
}
break;
} }
} }

View File

@ -38,6 +38,9 @@ class LinearAnimation extends LinearAnimationBase {
return true; return true;
} }
bool isAnyObjectKeyed(Iterable<Core> objects) =>
objects.any((element) => _keyedObjects.containsKey(element.id));
bool isObjectKeyed(Core object) => _keyedObjects.containsKey(object.id); bool isObjectKeyed(Core object) => _keyedObjects.containsKey(object.id);
bool removeObjectKeys(Core object) { bool removeObjectKeys(Core object) {
var value = _keyedObjects[object.id]; var value = _keyedObjects[object.id];

View File

@ -42,7 +42,9 @@ abstract class ListenerInputChange extends ListenerInputChangeBase {
} }
if (inputId >= 0 && inputId < stateMachineImporter.machine.inputs.length) { if (inputId >= 0 && inputId < stateMachineImporter.machine.inputs.length) {
var found = stateMachineImporter.machine.inputs[inputId]; var found = stateMachineImporter.machine.inputs[inputId];
_input = found; if (found is StateMachineInput) {
_input = found;
}
inputId = found.id; inputId = found.id;
} }

View File

@ -1,19 +1,18 @@
import 'package:rive/src/core/core.dart'; import 'package:rive/src/core/core.dart';
import 'package:rive/src/generated/animation/state_machine_base.dart'; import 'package:rive/src/generated/animation/state_machine_base.dart';
import 'package:rive/src/rive_core/animation/state_machine_input.dart'; import 'package:rive/src/rive_core/animation/state_machine_component.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_machine_listener.dart';
import 'package:rive/src/rive_core/artboard.dart'; import 'package:rive/src/rive_core/artboard.dart';
export 'package:rive/src/generated/animation/state_machine_base.dart'; export 'package:rive/src/generated/animation/state_machine_base.dart';
class StateMachine extends StateMachineBase { class StateMachine extends StateMachineBase {
final StateMachineComponents<StateMachineInput> inputs = final StateMachineComponents<StateMachineComponent> inputs =
StateMachineComponents<StateMachineInput>(); StateMachineComponents<StateMachineComponent>();
final StateMachineComponents<StateMachineLayer> layers = final StateMachineComponents<StateMachineLayer> layers =
StateMachineComponents<StateMachineLayer>(); StateMachineComponents<StateMachineLayer>();
final StateMachineComponents<StateMachineListener> listeners = final StateMachineComponents<StateMachineComponent> listeners =
StateMachineComponents<StateMachineListener>(); StateMachineComponents<StateMachineComponent>();
@override @override
bool import(ImportStack stack) { bool import(ImportStack stack) {

View File

@ -35,9 +35,13 @@ abstract class StateMachineComponent
@override @override
void onAddedDirty() {} void onAddedDirty() {}
@override
void onAdded() {}
@override @override
void onRemoved() { void onRemoved() {
super.onRemoved(); super.onRemoved();
stateMachine = null; stateMachine = null;
} }

View File

@ -163,11 +163,29 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
Iterable<NestedArtboard> get activeNestedArtboards => _activeNestedArtboards; Iterable<NestedArtboard> get activeNestedArtboards => _activeNestedArtboards;
final List<Joystick> _joysticks = []; final List<Joystick> _joysticks = [];
Iterable<Joystick> get joysticks => _joysticks;
void applyJoysticks() { bool canPreApplyJoysticks() {
if (_joysticks.isEmpty) {
return false;
}
if (_joysticks.any((joystick) => joystick.isComplex)) {
return false;
}
return true;
}
bool applyJoysticks() {
if (_joysticks.isEmpty) {
return false;
}
for (final joystick in _joysticks) { for (final joystick in _joysticks) {
if (joystick.isComplex) {
updateComponents();
}
joystick.apply(context); joystick.apply(context);
} }
return true;
} }
/// Update any dirty components in this artboard. /// Update any dirty components in this artboard.
@ -179,11 +197,27 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
didUpdate = true; didUpdate = true;
} }
} }
applyJoysticks();
// Joysticks can be applied before updating components if none of the
// joysticks have "external" control. If they are controlled/moved by some
// other component then they need to apply after the update cycle, which is
// less efficient.
var canApplyJoysticksEarly = canPreApplyJoysticks();
if (canApplyJoysticksEarly) {
applyJoysticks();
}
if (updateComponents() || didUpdate) { if (updateComponents() || didUpdate) {
didUpdate = true; didUpdate = true;
} }
// If joysticks applied, run the update again for the animation changes.
if (!canApplyJoysticksEarly && applyJoysticks()) {
updateComponents();
didUpdate = true;
}
if (nested) { if (nested) {
var active = _activeNestedArtboards.toList(growable: false); var active = _activeNestedArtboards.toList(growable: false);
for (final activeNestedArtboard in active) { for (final activeNestedArtboard in active) {
@ -220,13 +254,13 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
bool resolveArtboard() => true; bool resolveArtboard() => true;
/// Sort the DAG for resolution in order of dependencies such that dependent /// Sort the DAG for resolution in order of dependencies such that dependent
/// compnents process after their dependencies. /// components process after their dependencies.
void sortDependencies() { void sortDependencies() {
var optimistic = DependencySorter<Component>(); var optimistic = DependencyGraphNodeSorter<Component>();
var order = optimistic.sort(this); var order = optimistic.sort(this);
if (order.isEmpty) { if (order.isEmpty) {
// cycle detected, use a more robust solver // cycle detected, use a more robust solver
var robust = TarjansDependencySorter<Component>(); var robust = TarjansDependencyGraphNodeSorter<Component>();
order = robust.sort(this); order = robust.sort(this);
} }
@ -481,7 +515,7 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
} }
} }
var sorter = DependencySorter<Component>(); var sorter = DependencyGraphNodeSorter<Component>();
_sortedDrawRules = sorter.sort(root).cast<DrawTarget>().skip(1).toList(); _sortedDrawRules = sorter.sort(root).cast<DrawTarget>().skip(1).toList();

View File

@ -41,6 +41,8 @@ class FontAsset extends FontAssetBase {
font = null; font = null;
} }
// 'otf' supported as well. This getter isn't being used for fontAssets so
// fine just being ttf for now.
@override @override
String get fileExtension => 'ttf'; String get fileExtension => 'ttf';
} }

View File

@ -158,6 +158,20 @@ abstract class Component extends ComponentBase<RuntimeArtboard>
@override @override
Set<Component> get dependents => _dependents; Set<Component> get dependents => _dependents;
Set<Component> get dependencies {
Set<Component> components = {};
allDependencies(components);
return components;
}
void allDependencies(Set<Component> dependencies) {
for (final dependency in _dependsOn) {
if (dependencies.add(dependency)) {
dependency.allDependencies(dependencies);
}
}
}
/// Mark [dependent] as a component which must update after this. Provide /// Mark [dependent] as a component which must update after this. Provide
/// [via] as the Component registering the dependency when it is not /// [via] as the Component registering the dependency when it is not
/// [dependent] itself. At edit time this allows the editor to rebuild both /// [dependent] itself. At edit time this allows the editor to rebuild both

View File

@ -1,21 +1,81 @@
import 'package:rive/src/generated/joystick_base.dart'; import 'package:rive/src/generated/joystick_base.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/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/transform_component.dart';
import 'package:rive/src/rive_core/world_transform_component.dart';
import 'package:rive_common/math.dart';
export 'package:rive/src/generated/joystick_base.dart'; export 'package:rive/src/generated/joystick_base.dart';
class JoystickFlags {
/// Whether to invert the application of the x axis.
static const int invertX = 1 << 0;
/// Whether to invert the application of the y axis.
static const int invertY = 1 << 1;
/// Whether this Joystick works in world space.
static const int worldSpace = 1 << 2;
}
class Joystick extends JoystickBase { class Joystick extends JoystickBase {
double get clampedX => x.clamp(-1, 1); double get clampedX => x.clamp(-1, 1);
double get clampedY => y.clamp(-1, 1); double get clampedY => y.clamp(-1, 1);
bool get isComplex => handleSource != null;
@override @override
void update(int dirt) {} void update(int dirt) {
if (dirt & (ComponentDirt.transform | ComponentDirt.worldTransform) != 0) {
_worldTransform = computeWorldTransform();
Mat2D.invert(_inverseWorldTransform, _worldTransform);
if (handleSource != null) {
var pos = _inverseWorldTransform * handleSource!.worldTranslation;
var local = localBounds.factorFrom(pos);
// In the editor we don't want to notify changes while we're in the
// update cycle.
x = local.x;
y = local.y;
}
}
}
bool get invertX => (joystickFlags & JoystickFlags.invertX) != 0;
set invertX(bool value) {
if (value) {
joystickFlags |= JoystickFlags.invertX;
} else {
joystickFlags &= ~JoystickFlags.invertX;
}
}
bool get invertY => (joystickFlags & JoystickFlags.invertY) != 0;
set invertY(bool value) {
if (value) {
joystickFlags |= JoystickFlags.invertY;
} else {
joystickFlags &= ~JoystickFlags.invertY;
}
}
bool get inWorldSpace =>
(joystickFlags & JoystickFlags.worldSpace) != 0 || handleSource != null;
set inWorldSpace(bool value) {
if (value) {
joystickFlags |= JoystickFlags.worldSpace;
} else {
joystickFlags &= ~JoystickFlags.worldSpace;
}
}
void apply(CoreContext context) { void apply(CoreContext context) {
var animation = _xAnimation; var animation = _xAnimation;
if (animation != null) { if (animation != null) {
var value = xInvert ? -x : x; var value = invertX ? -x : x;
animation.apply( animation.apply(
(value + 1) / 2 * animation.durationSeconds, (value + 1) / 2 * animation.durationSeconds,
coreContext: context, coreContext: context,
@ -23,7 +83,7 @@ class Joystick extends JoystickBase {
} }
animation = _yAnimation; animation = _yAnimation;
if (animation != null) { if (animation != null) {
var value = yInvert ? -y : y; var value = invertY ? -y : y;
animation.apply( animation.apply(
(value + 1) / 2 * animation.durationSeconds, (value + 1) / 2 * animation.durationSeconds,
coreContext: context, coreContext: context,
@ -36,6 +96,26 @@ class Joystick extends JoystickBase {
context.markNeedsAdvance(); context.markNeedsAdvance();
} }
Vec2D get position => Vec2D.fromValues(posX, posY);
Mat2D _worldTransform = Mat2D();
final Mat2D _inverseWorldTransform = Mat2D();
Mat2D computeWorldTransform() {
var local = Mat2D.fromTranslation(position);
if (parent is WorldTransformComponent) {
var world = Mat2D.multiply(
Mat2D(), (parent as WorldTransformComponent).worldTransform, local);
if (!inWorldSpace) {
return Mat2D.fromTranslation(world.translation);
}
return world;
}
return local;
}
Mat2D get worldTransform =>
inWorldSpace ? _worldTransform : computeWorldTransform();
@override @override
void yChanged(double from, double to) { void yChanged(double from, double to) {
context.markNeedsAdvance(); context.markNeedsAdvance();
@ -90,13 +170,76 @@ class Joystick extends JoystickBase {
} }
} }
@override void _transformChanged() {
void xInvertChanged(bool from, bool to) { markTransformDirty();
context.markNeedsAdvance();
} }
@override @override
void yInvertChanged(bool from, bool to) { void heightChanged(double from, double to) {}
@override
void posXChanged(double from, double to) => _transformChanged();
@override
void posYChanged(double from, double to) => _transformChanged();
@override
void widthChanged(double from, double to) {}
static const double minStageSize = 0;
bool get isSliderY => xAnimation == null && yAnimation != null;
bool get isSliderX => yAnimation == null && xAnimation != null;
double get stageWidth => isSliderY ? minStageSize : width;
double get stageHeight => isSliderX ? minStageSize : height;
AABB get localBounds => AABB.fromValues(
-stageWidth * originX,
-stageHeight * originY,
-stageWidth * originX + stageWidth,
-stageHeight * originY + stageHeight,
);
Mat2D get transform => Mat2D.fromTranslation(position);
@override
void originXChanged(double from, double to) => _transformChanged();
@override
void originYChanged(double from, double to) => _transformChanged();
@override
void joystickFlagsChanged(int from, int to) {
context.markNeedsAdvance(); context.markNeedsAdvance();
markTransformDirty();
}
TransformComponent? _handleSource;
TransformComponent? get handleSource => _handleSource;
set handleSource(TransformComponent? value) {
if (_handleSource == value) {
return;
}
_handleSource = value;
handleSourceId = value?.id ?? Core.missingId;
markTransformDirty();
}
@override
void handleSourceIdChanged(int from, int to) {
handleSource = context.resolve(to);
markRebuildDependencies();
}
void markTransformDirty() {
if (!inWorldSpace) {
return;
}
if (!addDirt(ComponentDirt.transform)) {
return;
}
addDirt(ComponentDirt.worldTransform, recurse: true);
} }
} }

View File

@ -290,23 +290,25 @@ class StateMachineController extends RiveAnimationController<CoreContext> {
// Initialize all events. // Initialize all events.
HashMap<Shape, _HitShape> hitShapeLookup = HashMap<Shape, _HitShape>(); HashMap<Shape, _HitShape> hitShapeLookup = HashMap<Shape, _HitShape>();
for (final event in stateMachine.listeners) { for (final event in stateMachine.listeners) {
// Resolve target on this artboard instance. if (event is StateMachineListener) {
var node = core.resolve<Node>(event.targetId); // Resolve target on this artboard instance.
if (node == null) { var node = core.resolve<Node>(event.targetId);
continue; if (node == null) {
} continue;
node.forAll((component) {
if (component is Shape) {
var hitShape = hitShapeLookup[component];
if (hitShape == null) {
hitShapeLookup[component] = hitShape = _HitShape(component);
}
hitShape.events.add(event);
} }
// Keep iterating so we find all shapes.
return true; node.forAll((component) {
}); if (component is Shape) {
var hitShape = hitShapeLookup[component];
if (hitShape == null) {
hitShapeLookup[component] = hitShape = _HitShape(component);
}
hitShape.events.add(event);
}
// Keep iterating so we find all shapes.
return true;
});
}
} }
hitShapes = hitShapeLookup.values.toList(); hitShapes = hitShapeLookup.values.toList();

View File

@ -1,5 +1,5 @@
name: rive name: rive
version: 0.11.0 version: 0.11.1
homepage: https://rive.app homepage: https://rive.app
description: Rive 2 Flutter Runtime. This package provides runtime functionality for playing back and interacting with animations built with the Rive editor available at https://rive.app. description: Rive 2 Flutter Runtime. This package provides runtime functionality for playing back and interacting with animations built with the Rive editor available at https://rive.app.
repository: https://github.com/rive-app/rive-flutter repository: https://github.com/rive-app/rive-flutter
@ -15,7 +15,7 @@ dependencies:
http: ^0.13.3 http: ^0.13.3
meta: ^1.3.0 meta: ^1.3.0
plugin_platform_interface: ^2.0.2 plugin_platform_interface: ^2.0.2
rive_common: ^0.0.6 rive_common: 0.0.7
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter