diff --git a/.rive_head b/.rive_head index 9c8e55f..592c7e0 100644 --- a/.rive_head +++ b/.rive_head @@ -1 +1 @@ -a4fb3dc7decb50f6965e21ebced098cbdc35883d +f2ecb824beea0dadfe7077accacb5cd246cac669 diff --git a/CHANGELOG.md b/CHANGELOG.md index 036de61..e228ffe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.11.1 + +- Joysticks with custom handle sources. + ## 0.11.0 - Joysticks! diff --git a/example/lib/simple_animation.dart b/example/lib/simple_animation.dart index 0422813..930098b 100644 --- a/example/lib/simple_animation.dart +++ b/example/lib/simple_animation.dart @@ -5,22 +5,16 @@ import 'package:rive/rive.dart'; class SimpleAssetAnimation extends StatelessWidget { const SimpleAssetAnimation({Key? key}) : super(key: key); - void _onRiveInit(Artboard artboard) { - final controller = StateMachineController.fromArtboard(artboard, 'Coyote'); - artboard.addController(controller!); - } - @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Simple Animation'), ), - body: Center( + body: const Center( child: RiveAnimation.asset( - 'assets/coyote.riv', + 'assets/off_road_car.riv', fit: BoxFit.cover, - onInit: _onRiveInit, ), ), ); diff --git a/example/macos/Podfile.lock b/example/macos/Podfile.lock index 43321f4..a881b2d 100644 --- a/example/macos/Podfile.lock +++ b/example/macos/Podfile.lock @@ -15,8 +15,8 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 - rive_common: 643f5c20ccd60c53136ab5c970eccf0d17e010fe + rive_common: fab8476ce8352bf54152a913f393a8696d3dc98c PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7 -COCOAPODS: 1.11.3 +COCOAPODS: 1.12.0 diff --git a/lib/src/generated/joystick_base.dart b/lib/src/generated/joystick_base.dart index c0dd77c..1f38edb 100644 --- a/lib/src/generated/joystick_base.dart +++ b/lib/src/generated/joystick_base.dart @@ -54,6 +54,142 @@ abstract class JoystickBase extends Component { 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. static const int xIdInitialValue = -1; @@ -79,30 +215,6 @@ abstract class JoystickBase extends Component { 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. static const int yIdInitialValue = -1; @@ -129,37 +241,67 @@ abstract class JoystickBase extends Component { void yIdChanged(int from, int to); /// -------------------------------------------------------------------------- - /// YInvert field with key 311. - static const bool yInvertInitialValue = false; - bool _yInvert = yInvertInitialValue; - static const int yInvertPropertyKey = 311; + /// JoystickFlags field with key 312. + static const int joystickFlagsInitialValue = 0; + int _joystickFlags = joystickFlagsInitialValue; + static const int joystickFlagsPropertyKey = 312; + int get joystickFlags => _joystickFlags; - /// Whether to invert the application of the y axis. - bool get yInvert => _yInvert; - - /// Change the [_yInvert] field value. - /// [yInvertChanged] will be invoked only if the field's value has changed. - set yInvert(bool value) { - if (_yInvert == value) { + /// Change the [_joystickFlags] field value. + /// [joystickFlagsChanged] will be invoked only if the field's value has + /// changed. + set joystickFlags(int value) { + if (_joystickFlags == value) { return; } - bool from = _yInvert; - _yInvert = value; + int from = _joystickFlags; + _joystickFlags = value; 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 void copy(covariant JoystickBase source) { super.copy(source); _x = source._x; _y = source._y; + _posX = source._posX; + _posY = source._posY; + _originX = source._originX; + _originY = source._originY; + _width = source._width; + _height = source._height; _xId = source._xId; - _xInvert = source._xInvert; _yId = source._yId; - _yInvert = source._yInvert; + _joystickFlags = source._joystickFlags; + _handleSourceId = source._handleSourceId; } } diff --git a/lib/src/generated/rive_core_context.dart b/lib/src/generated/rive_core_context.dart index 9b2cffe..ac3fefd 100644 --- a/lib/src/generated/rive_core_context.dart +++ b/lib/src/generated/rive_core_context.dart @@ -1134,24 +1134,54 @@ class RiveCoreContext { object.y = value; } 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: if (object is JoystickBase && value is int) { object.xId = value; } break; - case JoystickBase.xInvertPropertyKey: - if (object is JoystickBase && value is bool) { - object.xInvert = value; - } - break; case JoystickBase.yIdPropertyKey: if (object is JoystickBase && value is int) { object.yId = value; } break; - case JoystickBase.yInvertPropertyKey: - if (object is JoystickBase && value is bool) { - object.yInvert = value; + case JoystickBase.joystickFlagsPropertyKey: + if (object is JoystickBase && value is int) { + object.joystickFlags = value; + } + break; + case JoystickBase.handleSourceIdPropertyKey: + if (object is JoystickBase && value is int) { + object.handleSourceId = value; } break; case BoneBase.lengthPropertyKey: @@ -1400,6 +1430,8 @@ class RiveCoreContext { case ArtboardBase.defaultStateMachineIdPropertyKey: case JoystickBase.xIdPropertyKey: case JoystickBase.yIdPropertyKey: + case JoystickBase.joystickFlagsPropertyKey: + case JoystickBase.handleSourceIdPropertyKey: case TendonBase.boneIdPropertyKey: case TextStyleBase.fontAssetIdPropertyKey: case TextStyleAxisBase.tagPropertyKey: @@ -1482,6 +1514,12 @@ class RiveCoreContext { case ArtboardBase.originYPropertyKey: case JoystickBase.xPropertyKey: 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 RootBoneBase.xPropertyKey: case RootBoneBase.yPropertyKey: @@ -1524,8 +1562,6 @@ class RiveCoreContext { case ClippingShapeBase.isVisiblePropertyKey: case CustomPropertyBooleanBase.propertyValuePropertyKey: case ArtboardBase.clipPropertyKey: - case JoystickBase.xInvertPropertyKey: - case JoystickBase.yInvertPropertyKey: return boolType; case KeyFrameColorBase.valuePropertyKey: case SolidColorBase.colorValuePropertyKey: @@ -1685,6 +1721,10 @@ class RiveCoreContext { return (object as JoystickBase).xId; case JoystickBase.yIdPropertyKey: return (object as JoystickBase).yId; + case JoystickBase.joystickFlagsPropertyKey: + return (object as JoystickBase).joystickFlags; + case JoystickBase.handleSourceIdPropertyKey: + return (object as JoystickBase).handleSourceId; case TendonBase.boneIdPropertyKey: return (object as TendonBase).boneId; case TextStyleBase.fontAssetIdPropertyKey: @@ -1853,6 +1893,18 @@ class RiveCoreContext { return (object as JoystickBase).x; case JoystickBase.yPropertyKey: 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: return (object as BoneBase).length; case RootBoneBase.xPropertyKey: @@ -1941,10 +1993,6 @@ class RiveCoreContext { return (object as CustomPropertyBooleanBase).propertyValue; case ArtboardBase.clipPropertyKey: return (object as ArtboardBase).clip; - case JoystickBase.xInvertPropertyKey: - return (object as JoystickBase).xInvert; - case JoystickBase.yInvertPropertyKey: - return (object as JoystickBase).yInvert; } return false; } @@ -2323,6 +2371,16 @@ class RiveCoreContext { object.yId = value; } 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: if (object is TendonBase) { object.boneId = value; @@ -2733,6 +2791,36 @@ class RiveCoreContext { object.y = value; } 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: if (object is BoneBase) { object.length = value; @@ -2943,16 +3031,6 @@ class RiveCoreContext { object.clip = value; } break; - case JoystickBase.xInvertPropertyKey: - if (object is JoystickBase) { - object.xInvert = value; - } - break; - case JoystickBase.yInvertPropertyKey: - if (object is JoystickBase) { - object.yInvert = value; - } - break; } } diff --git a/lib/src/rive_core/animation/linear_animation.dart b/lib/src/rive_core/animation/linear_animation.dart index 11e86a9..eae2775 100644 --- a/lib/src/rive_core/animation/linear_animation.dart +++ b/lib/src/rive_core/animation/linear_animation.dart @@ -38,6 +38,9 @@ class LinearAnimation extends LinearAnimationBase { return true; } + bool isAnyObjectKeyed(Iterable objects) => + objects.any((element) => _keyedObjects.containsKey(element.id)); + bool isObjectKeyed(Core object) => _keyedObjects.containsKey(object.id); bool removeObjectKeys(Core object) { var value = _keyedObjects[object.id]; diff --git a/lib/src/rive_core/animation/listener_input_change.dart b/lib/src/rive_core/animation/listener_input_change.dart index b331afa..e58d135 100644 --- a/lib/src/rive_core/animation/listener_input_change.dart +++ b/lib/src/rive_core/animation/listener_input_change.dart @@ -42,7 +42,9 @@ abstract class ListenerInputChange extends ListenerInputChangeBase { } if (inputId >= 0 && inputId < stateMachineImporter.machine.inputs.length) { var found = stateMachineImporter.machine.inputs[inputId]; - _input = found; + if (found is StateMachineInput) { + _input = found; + } inputId = found.id; } diff --git a/lib/src/rive_core/animation/state_machine.dart b/lib/src/rive_core/animation/state_machine.dart index 1acc950..2035920 100644 --- a/lib/src/rive_core/animation/state_machine.dart +++ b/lib/src/rive_core/animation/state_machine.dart @@ -1,19 +1,18 @@ import 'package:rive/src/core/core.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_listener.dart'; import 'package:rive/src/rive_core/artboard.dart'; export 'package:rive/src/generated/animation/state_machine_base.dart'; class StateMachine extends StateMachineBase { - final StateMachineComponents inputs = - StateMachineComponents(); + final StateMachineComponents inputs = + StateMachineComponents(); final StateMachineComponents layers = StateMachineComponents(); - final StateMachineComponents listeners = - StateMachineComponents(); + final StateMachineComponents listeners = + StateMachineComponents(); @override bool import(ImportStack stack) { diff --git a/lib/src/rive_core/animation/state_machine_component.dart b/lib/src/rive_core/animation/state_machine_component.dart index c300ab2..1d451cf 100644 --- a/lib/src/rive_core/animation/state_machine_component.dart +++ b/lib/src/rive_core/animation/state_machine_component.dart @@ -35,9 +35,13 @@ abstract class StateMachineComponent @override void onAddedDirty() {} + @override + void onAdded() {} + @override void onRemoved() { super.onRemoved(); + stateMachine = null; } diff --git a/lib/src/rive_core/artboard.dart b/lib/src/rive_core/artboard.dart index 8246f01..68fe552 100644 --- a/lib/src/rive_core/artboard.dart +++ b/lib/src/rive_core/artboard.dart @@ -163,11 +163,29 @@ class Artboard extends ArtboardBase with ShapePaintContainer { Iterable get activeNestedArtboards => _activeNestedArtboards; final List _joysticks = []; + Iterable 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) { + if (joystick.isComplex) { + updateComponents(); + } joystick.apply(context); } + return true; } /// Update any dirty components in this artboard. @@ -179,11 +197,27 @@ class Artboard extends ArtboardBase with ShapePaintContainer { 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) { didUpdate = true; } + // If joysticks applied, run the update again for the animation changes. + if (!canApplyJoysticksEarly && applyJoysticks()) { + updateComponents(); + + didUpdate = true; + } + if (nested) { var active = _activeNestedArtboards.toList(growable: false); for (final activeNestedArtboard in active) { @@ -220,13 +254,13 @@ class Artboard extends ArtboardBase with ShapePaintContainer { bool resolveArtboard() => true; /// Sort the DAG for resolution in order of dependencies such that dependent - /// compnents process after their dependencies. + /// components process after their dependencies. void sortDependencies() { - var optimistic = DependencySorter(); + var optimistic = DependencyGraphNodeSorter(); var order = optimistic.sort(this); if (order.isEmpty) { // cycle detected, use a more robust solver - var robust = TarjansDependencySorter(); + var robust = TarjansDependencyGraphNodeSorter(); order = robust.sort(this); } @@ -481,7 +515,7 @@ class Artboard extends ArtboardBase with ShapePaintContainer { } } - var sorter = DependencySorter(); + var sorter = DependencyGraphNodeSorter(); _sortedDrawRules = sorter.sort(root).cast().skip(1).toList(); diff --git a/lib/src/rive_core/assets/font_asset.dart b/lib/src/rive_core/assets/font_asset.dart index d160c00..0fd099c 100644 --- a/lib/src/rive_core/assets/font_asset.dart +++ b/lib/src/rive_core/assets/font_asset.dart @@ -41,6 +41,8 @@ class FontAsset extends FontAssetBase { font = null; } + // 'otf' supported as well. This getter isn't being used for fontAssets so + // fine just being ttf for now. @override String get fileExtension => 'ttf'; } diff --git a/lib/src/rive_core/component.dart b/lib/src/rive_core/component.dart index 344b860..6d085ec 100644 --- a/lib/src/rive_core/component.dart +++ b/lib/src/rive_core/component.dart @@ -158,6 +158,20 @@ abstract class Component extends ComponentBase @override Set get dependents => _dependents; + Set get dependencies { + Set components = {}; + allDependencies(components); + return components; + } + + void allDependencies(Set dependencies) { + for (final dependency in _dependsOn) { + if (dependencies.add(dependency)) { + dependency.allDependencies(dependencies); + } + } + } + /// Mark [dependent] as a component which must update after this. Provide /// [via] as the Component registering the dependency when it is not /// [dependent] itself. At edit time this allows the editor to rebuild both diff --git a/lib/src/rive_core/joystick.dart b/lib/src/rive_core/joystick.dart index 67afb52..b1c5fa5 100644 --- a/lib/src/rive_core/joystick.dart +++ b/lib/src/rive_core/joystick.dart @@ -1,21 +1,81 @@ import 'package:rive/src/generated/joystick_base.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/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'; +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 { double get clampedX => x.clamp(-1, 1); double get clampedY => y.clamp(-1, 1); + bool get isComplex => handleSource != null; + @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) { var animation = _xAnimation; if (animation != null) { - var value = xInvert ? -x : x; + var value = invertX ? -x : x; animation.apply( (value + 1) / 2 * animation.durationSeconds, coreContext: context, @@ -23,7 +83,7 @@ class Joystick extends JoystickBase { } animation = _yAnimation; if (animation != null) { - var value = yInvert ? -y : y; + var value = invertY ? -y : y; animation.apply( (value + 1) / 2 * animation.durationSeconds, coreContext: context, @@ -36,6 +96,26 @@ class Joystick extends JoystickBase { 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 void yChanged(double from, double to) { context.markNeedsAdvance(); @@ -90,13 +170,76 @@ class Joystick extends JoystickBase { } } - @override - void xInvertChanged(bool from, bool to) { - context.markNeedsAdvance(); + void _transformChanged() { + markTransformDirty(); } @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(); + + 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); } } diff --git a/lib/src/rive_core/state_machine_controller.dart b/lib/src/rive_core/state_machine_controller.dart index f870799..9b73d30 100644 --- a/lib/src/rive_core/state_machine_controller.dart +++ b/lib/src/rive_core/state_machine_controller.dart @@ -290,23 +290,25 @@ class StateMachineController extends RiveAnimationController { // Initialize all events. HashMap hitShapeLookup = HashMap(); for (final event in stateMachine.listeners) { - // Resolve target on this artboard instance. - var node = core.resolve(event.targetId); - 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); + if (event is StateMachineListener) { + // Resolve target on this artboard instance. + var node = core.resolve(event.targetId); + if (node == null) { + continue; } - // 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(); diff --git a/pubspec.yaml b/pubspec.yaml index e6d2cff..385a630 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: rive -version: 0.11.0 +version: 0.11.1 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. repository: https://github.com/rive-app/rive-flutter @@ -15,7 +15,7 @@ dependencies: http: ^0.13.3 meta: ^1.3.0 plugin_platform_interface: ^2.0.2 - rive_common: ^0.0.6 + rive_common: 0.0.7 dev_dependencies: flutter_test: sdk: flutter