mirror of
https://github.com/rive-app/rive-flutter.git
synced 2025-08-06 16:40:27 +08:00
Follow path distance support all values & fix scaling issue.
Fixes the following as discussed: - FP not respecting scale values of constrained component - FP not handling distance values <0 and >100 This video shows 4 scenarios (all 4 show car being scaled from 100-500% and back): TL - distance animating from 0 to 200 with orient on TR - distance animating from 0 to -100 with orient on BL - distance animating from 0 to 100 with orient on and animating path BR - distance animating from 0 to 100 with orient off and car rotating 720 degrees https://github.com/rive-app/rive/assets/186340/15156cec-ac90-4b67-8bfb-b4a60e99090f Diffs= 24aaadf9a Follow path distance support all values & fix scaling issue. (#5659) Co-authored-by: Philip Chung <philterdesign@gmail.com>
This commit is contained in:
@ -1 +1 @@
|
|||||||
f49673939ec46d865ef28395dea3a525b83c7115
|
24aaadf9aeb0d6beb3faa1dec9f2d490d5d67afa
|
||||||
|
@ -29,7 +29,11 @@ class FollowPathConstraint extends FollowPathConstraintBase {
|
|||||||
for (final metric in metrics) {
|
for (final metric in metrics) {
|
||||||
totalLength += metric.length;
|
totalLength += metric.length;
|
||||||
}
|
}
|
||||||
double distanceUnits = totalLength * distance.clamp(0, 1);
|
// Normalize distance value to 0-1 since we need to support values
|
||||||
|
// <0 and >1
|
||||||
|
// Negative values follow path in reverse direction
|
||||||
|
var actualDistance = distance % 1;
|
||||||
|
double distanceUnits = totalLength * actualDistance.clamp(0, 1);
|
||||||
var itr = metrics.iterator;
|
var itr = metrics.iterator;
|
||||||
|
|
||||||
// We already checked it wasn't empty.
|
// We already checked it wasn't empty.
|
||||||
@ -51,27 +55,18 @@ class FollowPathConstraint extends FollowPathConstraintBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Vec2D position = Vec2D.fromValues(tangent.position.dx, tangent.position.dy);
|
Vec2D position = Vec2D.fromValues(tangent.position.dx, tangent.position.dy);
|
||||||
|
|
||||||
Mat2D transformB = Mat2D.clone(target!.worldTransform);
|
Mat2D transformB = Mat2D.clone(target!.worldTransform);
|
||||||
|
|
||||||
transformB[4] = position.x;
|
|
||||||
transformB[5] = position.y;
|
|
||||||
if (offset) {
|
|
||||||
Mat2D.multiply(transformB, transformB, constrainedComponent!.transform);
|
|
||||||
}
|
|
||||||
if (orient) {
|
if (orient) {
|
||||||
Mat2D.multiply(
|
Mat2D.fromRotation(
|
||||||
transformB,
|
transformB, atan2(tangent.vector.dy, tangent.vector.dx));
|
||||||
transformB,
|
|
||||||
Mat2D.fromRotation(
|
|
||||||
Mat2D(), atan2(tangent.vector.dy, tangent.vector.dx)));
|
|
||||||
} else {
|
|
||||||
// If orient is off, respect the constrained component's rotation
|
|
||||||
var comp = TransformComponents();
|
|
||||||
Mat2D.decompose(constrainedComponent!.worldTransform, comp);
|
|
||||||
Mat2D.multiply(
|
|
||||||
transformB, transformB, Mat2D.fromRotation(Mat2D(), comp.rotation));
|
|
||||||
}
|
}
|
||||||
|
final offsetPosition = offset
|
||||||
|
? Vec2D.fromValues(constrainedComponent!.transform[4],
|
||||||
|
constrainedComponent!.transform[5])
|
||||||
|
: Vec2D();
|
||||||
|
transformB[4] = position.x + offsetPosition.x;
|
||||||
|
transformB[5] = position.y + offsetPosition.y;
|
||||||
return transformB;
|
return transformB;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,7 +75,9 @@ class FollowPathConstraint extends FollowPathConstraintBase {
|
|||||||
if (target == null) {
|
if (target == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Constrained component world transform
|
||||||
var transformA = component.worldTransform;
|
var transformA = component.worldTransform;
|
||||||
|
// Target transform
|
||||||
var transformB = Mat2D.clone(targetTransform);
|
var transformB = Mat2D.clone(targetTransform);
|
||||||
if (sourceSpace == TransformSpace.local) {
|
if (sourceSpace == TransformSpace.local) {
|
||||||
var targetParentWorld = parentWorld(target!);
|
var targetParentWorld = parentWorld(target!);
|
||||||
@ -99,24 +96,21 @@ class FollowPathConstraint extends FollowPathConstraintBase {
|
|||||||
Mat2D.decompose(transformA, componentsA);
|
Mat2D.decompose(transformA, componentsA);
|
||||||
Mat2D.decompose(transformB, componentsB);
|
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 t = strength;
|
||||||
var ti = 1 - t;
|
var ti = 1 - t;
|
||||||
|
|
||||||
componentsB[4] = angleA + diff * t;
|
// If orient is on, use the rotation value we calculated when getting the
|
||||||
|
// tanget of the path, otherwise respect constrained component's rotation
|
||||||
|
if (!orient) {
|
||||||
|
componentsB[4] = componentsA[4] % (pi * 2);
|
||||||
|
}
|
||||||
|
// Merge x/y position based on strength value
|
||||||
componentsB[0] = componentsA[0] * ti + componentsB[0] * t;
|
componentsB[0] = componentsA[0] * ti + componentsB[0] * t;
|
||||||
componentsB[1] = componentsA[1] * ti + componentsB[1] * t;
|
componentsB[1] = componentsA[1] * ti + componentsB[1] * t;
|
||||||
componentsB[2] = componentsA[2] * ti + componentsB[2] * t;
|
// Maintain scale & skew of constrained component
|
||||||
componentsB[3] = componentsA[3] * ti + componentsB[3] * t;
|
componentsB[2] = componentsA[2];
|
||||||
componentsB[5] = componentsA[5] * ti + componentsB[5] * t;
|
componentsB[3] = componentsA[3];
|
||||||
|
componentsB[5] = componentsA[5];
|
||||||
|
|
||||||
Mat2D.compose(component.worldTransform, componentsB);
|
Mat2D.compose(component.worldTransform, componentsB);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user