From 8214b5cee82ee9a8c392aa43ab142e60c597f05a Mon Sep 17 00:00:00 2001 From: Luigi Rosso Date: Wed, 28 Jul 2021 18:16:53 -0700 Subject: [PATCH] Adding support for TransformConstraints. --- .../transform_constraint_base.dart | 78 +++++++++++++++++++ lib/src/generated/rive_core_context.dart | 30 +++++++ lib/src/rive_core/constraints/constraint.dart | 18 +++++ .../constraints/distance_constraint.dart | 4 +- .../rive_core/constraints/ik_constraint.dart | 24 ++---- .../constraints/transform_constraint.dart | 74 ++++++++++++++++++ lib/src/rive_core/transform_component.dart | 3 + 7 files changed, 211 insertions(+), 20 deletions(-) create mode 100644 lib/src/generated/constraints/transform_constraint_base.dart create mode 100644 lib/src/rive_core/constraints/transform_constraint.dart diff --git a/lib/src/generated/constraints/transform_constraint_base.dart b/lib/src/generated/constraints/transform_constraint_base.dart new file mode 100644 index 0000000..e7b0fea --- /dev/null +++ b/lib/src/generated/constraints/transform_constraint_base.dart @@ -0,0 +1,78 @@ +/// Core automatically generated +/// lib/src/generated/constraints/transform_constraint_base.dart. +/// Do not modify manually. + +import 'package:rive/src/generated/component_base.dart'; +import 'package:rive/src/generated/constraints/constraint_base.dart'; +import 'package:rive/src/generated/constraints/targeted_constraint_base.dart'; +import 'package:rive/src/rive_core/constraints/targeted_constraint.dart'; + +abstract class TransformConstraintBase extends TargetedConstraint { + static const int typeKey = 83; + @override + int get coreType => TransformConstraintBase.typeKey; + @override + Set get coreTypes => { + TransformConstraintBase.typeKey, + TargetedConstraintBase.typeKey, + ConstraintBase.typeKey, + ComponentBase.typeKey + }; + + /// -------------------------------------------------------------------------- + /// SourceSpaceValue field with key 179. + static const int sourceSpaceValueInitialValue = 0; + int _sourceSpaceValue = sourceSpaceValueInitialValue; + static const int sourceSpaceValuePropertyKey = 179; + + /// The source transform space. + int get sourceSpaceValue => _sourceSpaceValue; + + /// Change the [_sourceSpaceValue] field value. + /// [sourceSpaceValueChanged] will be invoked only if the field's value has + /// changed. + set sourceSpaceValue(int value) { + if (_sourceSpaceValue == value) { + return; + } + int from = _sourceSpaceValue; + _sourceSpaceValue = value; + if (hasValidated) { + sourceSpaceValueChanged(from, value); + } + } + + void sourceSpaceValueChanged(int from, int to); + + /// -------------------------------------------------------------------------- + /// DestSpaceValue field with key 180. + static const int destSpaceValueInitialValue = 0; + int _destSpaceValue = destSpaceValueInitialValue; + static const int destSpaceValuePropertyKey = 180; + + /// The destination transform space. + int get destSpaceValue => _destSpaceValue; + + /// Change the [_destSpaceValue] field value. + /// [destSpaceValueChanged] will be invoked only if the field's value has + /// changed. + set destSpaceValue(int value) { + if (_destSpaceValue == value) { + return; + } + int from = _destSpaceValue; + _destSpaceValue = value; + if (hasValidated) { + destSpaceValueChanged(from, value); + } + } + + void destSpaceValueChanged(int from, int to); + + @override + void copy(TransformConstraintBase source) { + super.copy(source); + _sourceSpaceValue = source._sourceSpaceValue; + _destSpaceValue = source._destSpaceValue; + } +} diff --git a/lib/src/generated/rive_core_context.dart b/lib/src/generated/rive_core_context.dart index 9147962..ee7fc4f 100644 --- a/lib/src/generated/rive_core_context.dart +++ b/lib/src/generated/rive_core_context.dart @@ -49,6 +49,7 @@ import 'package:rive/src/generated/constraints/constraint_base.dart'; import 'package:rive/src/generated/constraints/distance_constraint_base.dart'; import 'package:rive/src/generated/constraints/ik_constraint_base.dart'; import 'package:rive/src/generated/constraints/targeted_constraint_base.dart'; +import 'package:rive/src/generated/constraints/transform_constraint_base.dart'; import 'package:rive/src/generated/draw_rules_base.dart'; import 'package:rive/src/generated/draw_target_base.dart'; import 'package:rive/src/generated/drawable_base.dart'; @@ -113,6 +114,7 @@ import 'package:rive/src/rive_core/bones/tendon.dart'; import 'package:rive/src/rive_core/bones/weight.dart'; import 'package:rive/src/rive_core/constraints/distance_constraint.dart'; import 'package:rive/src/rive_core/constraints/ik_constraint.dart'; +import 'package:rive/src/rive_core/constraints/transform_constraint.dart'; import 'package:rive/src/rive_core/draw_rules.dart'; import 'package:rive/src/rive_core/draw_target.dart'; import 'package:rive/src/rive_core/node.dart'; @@ -146,6 +148,8 @@ class RiveCoreContext { return DistanceConstraint(); case IKConstraintBase.typeKey: return IKConstraint(); + case TransformConstraintBase.typeKey: + return TransformConstraint(); case AnimationStateBase.typeKey: return AnimationState(); case KeyedObjectBase.typeKey: @@ -313,6 +317,16 @@ class RiveCoreContext { object.parentBoneCount = value; } break; + case TransformConstraintBase.sourceSpaceValuePropertyKey: + if (object is TransformConstraintBase && value is int) { + object.sourceSpaceValue = value; + } + break; + case TransformConstraintBase.destSpaceValuePropertyKey: + if (object is TransformConstraintBase && value is int) { + object.destSpaceValue = value; + } + break; case AnimationStateBase.animationIdPropertyKey: if (object is AnimationStateBase && value is int) { object.animationId = value; @@ -933,6 +947,8 @@ class RiveCoreContext { case TargetedConstraintBase.targetIdPropertyKey: case DistanceConstraintBase.modeValuePropertyKey: case IKConstraintBase.parentBoneCountPropertyKey: + case TransformConstraintBase.sourceSpaceValuePropertyKey: + case TransformConstraintBase.destSpaceValuePropertyKey: case AnimationStateBase.animationIdPropertyKey: case KeyedObjectBase.objectIdPropertyKey: case BlendAnimationBase.animationIdPropertyKey: @@ -1089,6 +1105,10 @@ class RiveCoreContext { return (object as DistanceConstraintBase).modeValue; case IKConstraintBase.parentBoneCountPropertyKey: return (object as IKConstraintBase).parentBoneCount; + case TransformConstraintBase.sourceSpaceValuePropertyKey: + return (object as TransformConstraintBase).sourceSpaceValue; + case TransformConstraintBase.destSpaceValuePropertyKey: + return (object as TransformConstraintBase).destSpaceValue; case AnimationStateBase.animationIdPropertyKey: return (object as AnimationStateBase).animationId; case KeyedObjectBase.objectIdPropertyKey: @@ -1405,6 +1425,16 @@ class RiveCoreContext { object.parentBoneCount = value; } break; + case TransformConstraintBase.sourceSpaceValuePropertyKey: + if (object is TransformConstraintBase) { + object.sourceSpaceValue = value; + } + break; + case TransformConstraintBase.destSpaceValuePropertyKey: + if (object is TransformConstraintBase) { + object.destSpaceValue = value; + } + break; case AnimationStateBase.animationIdPropertyKey: if (object is AnimationStateBase) { object.animationId = value; diff --git a/lib/src/rive_core/constraints/constraint.dart b/lib/src/rive_core/constraints/constraint.dart index 5e2d8e6..452fc2e 100644 --- a/lib/src/rive_core/constraints/constraint.dart +++ b/lib/src/rive_core/constraints/constraint.dart @@ -1,3 +1,6 @@ +import 'package:rive/src/rive_core/artboard.dart'; +import 'package:rive/src/rive_core/component.dart'; +import 'package:rive/src/rive_core/math/mat2d.dart'; import 'package:rive/src/generated/constraints/constraint_base.dart'; import 'package:rive/src/rive_core/transform_component.dart'; export 'package:rive/src/generated/constraints/constraint_base.dart'; @@ -29,4 +32,19 @@ abstract class Constraint extends ConstraintBase { void update(int dirt) {} void markConstraintDirty() => constrainedComponent?.markTransformDirty(); + + @override + void onDirty(int mask) => markConstraintDirty(); +} + +/// Get the parent's world transform. Takes into consideration when the parent +/// is an artboard. +Mat2D parentWorld(TransformComponent component) { + var parent = component.parent; + if (parent is Artboard) { + return parent.worldTransform; + } else if (parent is TransformComponent) { + return parent.worldTransform; + } + return Mat2D(); } diff --git a/lib/src/rive_core/constraints/distance_constraint.dart b/lib/src/rive_core/constraints/distance_constraint.dart index 319f3ee..c7d8984 100644 --- a/lib/src/rive_core/constraints/distance_constraint.dart +++ b/lib/src/rive_core/constraints/distance_constraint.dart @@ -3,6 +3,7 @@ import 'package:rive/src/generated/constraints/distance_constraint_base.dart'; import 'package:rive/src/rive_core/transform_component.dart'; export 'package:rive/src/generated/constraints/distance_constraint_base.dart'; +/// [DistanceConstraint]'s logical distancing method. enum DistanceConstraintMode { closer, further, exact } const _distanceEpsilon = 0.001; @@ -57,7 +58,4 @@ class DistanceConstraint extends DistanceConstraintBase { DistanceConstraintMode get mode => DistanceConstraintMode.values[modeValue]; set mode(DistanceConstraintMode value) => modeValue = value.index; - - @override - bool validate() => super.validate() && parent is TransformComponent; } diff --git a/lib/src/rive_core/constraints/ik_constraint.dart b/lib/src/rive_core/constraints/ik_constraint.dart index 148891a..b7538f1 100644 --- a/lib/src/rive_core/constraints/ik_constraint.dart +++ b/lib/src/rive_core/constraints/ik_constraint.dart @@ -1,6 +1,6 @@ import 'dart:math'; -import 'package:rive/src/rive_core/artboard.dart'; import 'package:rive/src/rive_core/bones/bone.dart'; +import 'package:rive/src/rive_core/constraints/constraint.dart'; import 'package:rive/src/rive_core/math/mat2d.dart'; import 'package:rive/src/rive_core/math/transform_components.dart'; import 'package:rive/src/rive_core/math/vec2d.dart'; @@ -68,7 +68,6 @@ class IKConstraint extends IKConstraintBase { } // Now put them in FK order (top to bottom). for (final bone in bones.reversed) { - bone.addPeerConstraint(this); nextFKChain.add(_BoneChainLink( index: nextFKChain.length, bone: bone, @@ -117,9 +116,9 @@ class IKConstraint extends IKConstraintBase { // Decompose the chain. for (final item in _fkChain) { var bone = item.bone; - Mat2D parentWorld = _parentWorld(bone); + Mat2D parentWorldTransform = parentWorld(bone); - Mat2D.invert(item.parentWorldInverse, parentWorld); + Mat2D.invert(item.parentWorldInverse, parentWorldTransform); var boneTransform = bone.transform; Mat2D.multiply( @@ -145,7 +144,7 @@ class IKConstraint extends IKConstraintBase { j < end; j++) { var fk = _fkChain[j]; - Mat2D.invert(fk.parentWorldInverse, _parentWorld(fk.bone)); + Mat2D.invert(fk.parentWorldInverse, parentWorld(fk.bone)); } } break; @@ -249,7 +248,7 @@ class IKConstraint extends IKConstraintBase { _constrainRotation(firstChild, r2); if (firstChild != fk2) { var bone = fk2.bone; - Mat2D.multiply(bone.worldTransform, _parentWorld(bone), bone.transform); + Mat2D.multiply(bone.worldTransform, parentWorld(bone), bone.transform); } // Simple storage, need this for interpolation. @@ -273,7 +272,7 @@ class _BoneChainLink { void _constrainRotation(_BoneChainLink link, double rotation) { var bone = link.bone; - Mat2D parentWorld = _parentWorld(bone); + Mat2D parentWorldTransform = parentWorld(bone); var boneTransform = bone.transform; if (rotation == 0) { Mat2D.setIdentity(boneTransform); @@ -295,14 +294,5 @@ void _constrainRotation(_BoneChainLink link, double rotation) { boneTransform[2] = boneTransform[0] * skew + boneTransform[2]; boneTransform[3] = boneTransform[1] * skew + boneTransform[3]; } - Mat2D.multiply(bone.worldTransform, parentWorld, boneTransform); -} - -Mat2D _parentWorld(TransformComponent component) { - var parent = component.parent; - if (parent is Artboard) { - return parent.worldTransform; - } else { - return (parent as TransformComponent).worldTransform; - } + Mat2D.multiply(bone.worldTransform, parentWorldTransform, boneTransform); } diff --git a/lib/src/rive_core/constraints/transform_constraint.dart b/lib/src/rive_core/constraints/transform_constraint.dart new file mode 100644 index 0000000..819a1fd --- /dev/null +++ b/lib/src/rive_core/constraints/transform_constraint.dart @@ -0,0 +1,74 @@ +import 'dart:math'; + +import 'package:rive/src/rive_core/constraints/constraint.dart'; +import 'package:rive/src/rive_core/math/mat2d.dart'; +import 'package:rive/src/rive_core/math/transform_components.dart'; +import 'package:rive/src/generated/constraints/transform_constraint_base.dart'; +import 'package:rive/src/rive_core/transform_component.dart'; +import 'package:rive/src/rive_core/transform_space.dart'; +export 'package:rive/src/generated/constraints/transform_constraint_base.dart'; + +/// A constraint copies the transform from the target component to the +/// constrained component in world or local space. +class TransformConstraint extends TransformConstraintBase { + final TransformComponents componentsA = TransformComponents(); + final TransformComponents componentsB = TransformComponents(); + + TransformSpace get destSpace => TransformSpace.values[destSpaceValue]; + set destSpace(TransformSpace value) => destSpaceValue = value.index; + + TransformSpace get sourceSpace => TransformSpace.values[sourceSpaceValue]; + set sourceSpace(TransformSpace value) => sourceSpaceValue = value.index; + + @override + void destSpaceValueChanged(int from, int to) => markConstraintDirty(); + + @override + void sourceSpaceValueChanged(int from, int to) => markConstraintDirty(); + + @override + void constrain(TransformComponent component) { + if (target == null) { + return; + } + var transformA = component.worldTransform; + var transformB = Mat2D.clone(target!.worldTransform); + if (sourceSpace == TransformSpace.local) { + var targetParentWorld = parentWorld(target!); + + var inverse = Mat2D(); + if (!Mat2D.invert(inverse, targetParentWorld)) { + return; + } + Mat2D.multiply(transformB, inverse, transformB); + } + if (destSpace == TransformSpace.local && component.parent != null) { + var targetParentWorld = parentWorld(component); + Mat2D.multiply(transformB, targetParentWorld, transformB); + } + + Mat2D.decompose(transformA, componentsA); + Mat2D.decompose(transformB, componentsB); + + var angleA = componentsA[4] % (pi * 2); + var angleB = componentsB[4] % (pi * 2); + var diff = angleB - angleA; + if (diff > pi) { + diff -= pi * 2; + } else if (diff < -pi) { + diff += pi * 2; + } + + var t = strength; + var ti = 1 - t; + + componentsB[4] = angleA + diff * t; + componentsB[0] = componentsA[0] * ti + componentsB[0] * t; + componentsB[1] = componentsA[1] * ti + componentsB[1] * t; + componentsB[2] = componentsA[2] * ti + componentsB[2] * t; + componentsB[3] = componentsA[3] * ti + componentsB[3] * t; + componentsB[5] = componentsA[5] * ti + componentsB[5] * t; + + Mat2D.compose(component.worldTransform, componentsB); + } +} diff --git a/lib/src/rive_core/transform_component.dart b/lib/src/rive_core/transform_component.dart index 651e819..c570fef 100644 --- a/lib/src/rive_core/transform_component.dart +++ b/lib/src/rive_core/transform_component.dart @@ -3,6 +3,7 @@ import 'package:rive/src/rive_core/component.dart'; import 'package:rive/src/rive_core/component_dirt.dart'; import 'package:rive/src/rive_core/constraints/constraint.dart'; import 'package:rive/src/rive_core/constraints/ik_constraint.dart'; +import 'package:rive/src/rive_core/constraints/transform_constraint.dart'; import 'package:rive/src/rive_core/container_component.dart'; import 'package:rive/src/rive_core/draw_rules.dart'; import 'package:rive/src/rive_core/drawable.dart'; @@ -165,6 +166,7 @@ abstract class TransformComponent extends TransformComponentBase { addDirt(ComponentDirt.clip, recurse: true); break; + case TransformConstraintBase.typeKey: case IKConstraintBase.typeKey: case DistanceConstraintBase.typeKey: _constraints.add(child as Constraint); @@ -187,6 +189,7 @@ abstract class TransformComponent extends TransformComponentBase { addDirt(ComponentDirt.clip, recurse: true); } break; + case TransformConstraintBase.typeKey: case IKConstraintBase.typeKey: case DistanceConstraintBase.typeKey: _constraints.remove(child as Constraint);