blob: 9086e6e6fab434d476e8b7cc328a8cff63d39243 [file] [log] [blame]
 // Copyright 2014 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'dart:math' as math; import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/animation.dart'; import 'package:flutter/widgets.dart'; void main() { test('toString control test', () { expect(Curves.linear, hasOneLineDescription); expect(const SawTooth(3), hasOneLineDescription); expect(const Interval(0.25, 0.75), hasOneLineDescription); expect(const Interval(0.25, 0.75, curve: Curves.ease), hasOneLineDescription); }); test('Curve flipped control test', () { const Curve ease = Curves.ease; final Curve flippedEase = ease.flipped; expect(flippedEase.transform(0.0), lessThan(0.001)); expect(flippedEase.transform(0.5), lessThan(ease.transform(0.5))); expect(flippedEase.transform(1.0), greaterThan(0.999)); expect(flippedEase, hasOneLineDescription); }); test('Threshold has a threshold', () { const Curve step = Threshold(0.25); expect(step.transform(0.0), 0.0); expect(step.transform(0.24), 0.0); expect(step.transform(0.25), 1.0); expect(step.transform(0.26), 1.0); expect(step.transform(1.0), 1.0); }); void assertMaximumSlope(Curve curve, double maximumSlope) { const double delta = 0.005; for (double x = 0.0; x < 1.0 - delta; x += delta) { final double deltaY = curve.transform(x) - curve.transform(x + delta); assert(deltaY.abs() < delta * maximumSlope, '\${curve.toString()} discontinuous at \$x'); } } test('Curve is continuous', () { assertMaximumSlope(Curves.linear, 20.0); assertMaximumSlope(Curves.decelerate, 20.0); assertMaximumSlope(Curves.fastOutSlowIn, 20.0); assertMaximumSlope(Curves.slowMiddle, 20.0); assertMaximumSlope(Curves.bounceIn, 20.0); assertMaximumSlope(Curves.bounceOut, 20.0); assertMaximumSlope(Curves.bounceInOut, 20.0); assertMaximumSlope(Curves.elasticOut, 20.0); assertMaximumSlope(Curves.elasticInOut, 20.0); assertMaximumSlope(Curves.ease, 20.0); assertMaximumSlope(Curves.easeIn, 20.0); assertMaximumSlope(Curves.easeInSine, 20.0); assertMaximumSlope(Curves.easeInQuad, 20.0); assertMaximumSlope(Curves.easeInCubic, 20.0); assertMaximumSlope(Curves.easeInQuart, 20.0); assertMaximumSlope(Curves.easeInQuint, 20.0); assertMaximumSlope(Curves.easeInExpo, 20.0); assertMaximumSlope(Curves.easeInCirc, 20.0); assertMaximumSlope(Curves.easeOut, 20.0); assertMaximumSlope(Curves.easeOutSine, 20.0); assertMaximumSlope(Curves.easeOutQuad, 20.0); assertMaximumSlope(Curves.easeOutCubic, 20.0); assertMaximumSlope(Curves.easeOutQuart, 20.0); assertMaximumSlope(Curves.easeOutQuint, 20.0); assertMaximumSlope(Curves.easeOutExpo, 20.0); assertMaximumSlope(Curves.easeOutCirc, 20.0); // Curves.easeInOutExpo is discontinuous at its midpoint, so not included // here assertMaximumSlope(Curves.easeInOut, 20.0); assertMaximumSlope(Curves.easeInOutSine, 20.0); assertMaximumSlope(Curves.easeInOutQuad, 20.0); assertMaximumSlope(Curves.easeInOutCubic, 20.0); assertMaximumSlope(Curves.easeInOutQuart, 20.0); assertMaximumSlope(Curves.easeInOutQuint, 20.0); assertMaximumSlope(Curves.easeInOutCirc, 20.0); }); void expectStaysInBounds(Curve curve) { expect(curve.transform(0.0), inInclusiveRange(0.0, 1.0)); expect(curve.transform(0.1), inInclusiveRange(0.0, 1.0)); expect(curve.transform(0.2), inInclusiveRange(0.0, 1.0)); expect(curve.transform(0.3), inInclusiveRange(0.0, 1.0)); expect(curve.transform(0.4), inInclusiveRange(0.0, 1.0)); expect(curve.transform(0.5), inInclusiveRange(0.0, 1.0)); expect(curve.transform(0.6), inInclusiveRange(0.0, 1.0)); expect(curve.transform(0.7), inInclusiveRange(0.0, 1.0)); expect(curve.transform(0.8), inInclusiveRange(0.0, 1.0)); expect(curve.transform(0.9), inInclusiveRange(0.0, 1.0)); expect(curve.transform(1.0), inInclusiveRange(0.0, 1.0)); } test('Bounce stays in bounds', () { expectStaysInBounds(Curves.bounceIn); expectStaysInBounds(Curves.bounceOut); expectStaysInBounds(Curves.bounceInOut); }); List estimateBounds(Curve curve) { final List values = [ curve.transform(0.0), curve.transform(0.1), curve.transform(0.2), curve.transform(0.3), curve.transform(0.4), curve.transform(0.5), curve.transform(0.6), curve.transform(0.7), curve.transform(0.8), curve.transform(0.9), curve.transform(1.0), ]; return [ values.reduce(math.min), values.reduce(math.max), ]; } test('Ellastic overshoots its bounds', () { expect(Curves.elasticIn, hasOneLineDescription); expect(Curves.elasticOut, hasOneLineDescription); expect(Curves.elasticInOut, hasOneLineDescription); List bounds; bounds = estimateBounds(Curves.elasticIn); expect(bounds[0], lessThan(0.0)); expect(bounds[1], lessThanOrEqualTo(1.0)); bounds = estimateBounds(Curves.elasticOut); expect(bounds[0], greaterThanOrEqualTo(0.0)); expect(bounds[1], greaterThan(1.0)); bounds = estimateBounds(Curves.elasticInOut); expect(bounds[0], lessThan(0.0)); expect(bounds[1], greaterThan(1.0)); }); test('Back overshoots its bounds', () { expect(Curves.easeInBack, hasOneLineDescription); expect(Curves.easeOutBack, hasOneLineDescription); expect(Curves.easeInOutBack, hasOneLineDescription); List bounds; bounds = estimateBounds(Curves.easeInBack); expect(bounds[0], lessThan(0.0)); expect(bounds[1], lessThanOrEqualTo(1.0)); bounds = estimateBounds(Curves.easeOutBack); expect(bounds[0], greaterThanOrEqualTo(0.0)); expect(bounds[1], greaterThan(1.0)); bounds = estimateBounds(Curves.easeInOutBack); expect(bounds[0], lessThan(0.0)); expect(bounds[1], greaterThan(1.0)); }); test('Decelerate does so', () { expect(Curves.decelerate, hasOneLineDescription); final List bounds = estimateBounds(Curves.decelerate); expect(bounds[0], greaterThanOrEqualTo(0.0)); expect(bounds[1], lessThanOrEqualTo(1.0)); final double d1 = Curves.decelerate.transform(0.2) - Curves.decelerate.transform(0.0); final double d2 = Curves.decelerate.transform(1.0) - Curves.decelerate.transform(0.8); expect(d2, lessThan(d1)); }); test('Invalid transform parameter should assert', () { expect(() => const SawTooth(2).transform(-0.0001), throwsAssertionError); expect(() => const SawTooth(2).transform(1.0001), throwsAssertionError); expect(() => const Interval(0.0, 1.0).transform(-0.0001), throwsAssertionError); expect(() => const Interval(0.0, 1.0).transform(1.0001), throwsAssertionError); expect(() => const Threshold(0.5).transform(-0.0001), throwsAssertionError); expect(() => const Threshold(0.5).transform(1.0001), throwsAssertionError); expect(() => const ElasticInCurve().transform(-0.0001), throwsAssertionError); expect(() => const ElasticInCurve().transform(1.0001), throwsAssertionError); expect(() => const ElasticOutCurve().transform(-0.0001), throwsAssertionError); expect(() => const ElasticOutCurve().transform(1.0001), throwsAssertionError); expect(() => const Cubic(0.42, 0.0, 0.58, 1.0).transform(-0.0001), throwsAssertionError); expect(() => const Cubic(0.42, 0.0, 0.58, 1.0).transform(1.0001), throwsAssertionError); expect(() => Curves.decelerate.transform(-0.0001), throwsAssertionError); expect(() => Curves.decelerate.transform(1.0001), throwsAssertionError); expect(() => Curves.bounceIn.transform(-0.0001), throwsAssertionError); expect(() => Curves.bounceIn.transform(1.0001), throwsAssertionError); expect(() => Curves.bounceOut.transform(-0.0001), throwsAssertionError); expect(() => Curves.bounceOut.transform(1.0001), throwsAssertionError); expect(() => Curves.bounceInOut.transform(-0.0001), throwsAssertionError); expect(() => Curves.bounceInOut.transform(1.0001), throwsAssertionError); }); test('Curve transform method should return 0.0 for t=0.0 and 1.0 for t=1.0', () { expect(const SawTooth(2).transform(0), 0); expect(const SawTooth(2).transform(1), 1); expect(const Interval(0, 1).transform(0), 0); expect(const Interval(0, 1).transform(1), 1); expect(const Threshold(0.5).transform(0), 0); expect(const Threshold(0.5).transform(1), 1); expect(const ElasticInCurve().transform(0), 0); expect(const ElasticInCurve().transform(1), 1); expect(const ElasticOutCurve().transform(0), 0); expect(const ElasticOutCurve().transform(1), 1); expect(const ElasticInOutCurve().transform(0), 0); expect(const ElasticInOutCurve().transform(1), 1); expect(Curves.linear.transform(0), 0); expect(Curves.linear.transform(1), 1); expect(Curves.easeInOutExpo.transform(0), 0); expect(Curves.easeInOutExpo.transform(1), 1); expect(const FlippedCurve(Curves.easeInOutExpo).transform(0), 0); expect(const FlippedCurve(Curves.easeInOutExpo).transform(1), 1); expect(Curves.decelerate.transform(0), 0); expect(Curves.decelerate.transform(1), 1); expect(Curves.bounceIn.transform(0), 0); expect(Curves.bounceIn.transform(1), 1); expect(Curves.bounceOut.transform(0), 0); expect(Curves.bounceOut.transform(1), 1); expect(Curves.bounceInOut.transform(0), 0); expect(Curves.bounceInOut.transform(1), 1); }); test('CatmullRomSpline interpolates values properly', () { final CatmullRomSpline curve = CatmullRomSpline( const [ Offset(0.0, 0.0), Offset(0.01, 0.25), Offset(0.2, 0.25), Offset(0.33, 0.25), Offset(0.5, 1.0), Offset(0.66, 0.75), Offset(1.0, 1.0), ], tension: 0.0, startHandle: const Offset(0.0, -0.3), endHandle: const Offset(1.3, 1.3), ); expect(curve.transform(0.0).dx, closeTo(0.0, 1e-6)); expect(curve.transform(0.0).dy, closeTo(0.0, 1e-6)); expect(curve.transform(0.25).dx, closeTo(0.0966945, 1e-6)); expect(curve.transform(0.25).dy, closeTo(0.2626806, 1e-6)); expect(curve.transform(0.5).dx, closeTo(0.33, 1e-6)); expect(curve.transform(0.5).dy, closeTo(0.25, 1e-6)); expect(curve.transform(0.75).dx, closeTo(0.570260, 1e-6)); expect(curve.transform(0.75).dy, closeTo(0.883085, 1e-6)); expect(curve.transform(1.0).dx, closeTo(1.0, 1e-6)); expect(curve.transform(1.0).dy, closeTo(1.0, 1e-6)); }); test('CatmullRomSpline enforces contract', () { expect(() { CatmullRomSpline(null); }, throwsAssertionError); expect(() { CatmullRomSpline(const []); }, throwsAssertionError); expect(() { CatmullRomSpline(const [Offset.zero]); }, throwsAssertionError); expect(() { CatmullRomSpline(const [Offset.zero, Offset.zero]); }, throwsAssertionError); expect(() { CatmullRomSpline(const [Offset.zero, Offset.zero, Offset.zero]); }, throwsAssertionError); expect(() { CatmullRomSpline(const [Offset.zero, Offset.zero, Offset.zero, Offset.zero], tension: -1.0); }, throwsAssertionError); expect(() { CatmullRomSpline(const [Offset.zero, Offset.zero, Offset.zero, Offset.zero], tension: 2.0); }, throwsAssertionError); }); test('CatmullRomSpline interpolates values properly when precomputed', () { final CatmullRomSpline curve = CatmullRomSpline.precompute( const [ Offset(0.0, 0.0), Offset(0.01, 0.25), Offset(0.2, 0.25), Offset(0.33, 0.25), Offset(0.5, 1.0), Offset(0.66, 0.75), Offset(1.0, 1.0), ], tension: 0.0, startHandle: const Offset(0.0, -0.3), endHandle: const Offset(1.3, 1.3), ); expect(curve.transform(0.0).dx, closeTo(0.0, 1e-6)); expect(curve.transform(0.0).dy, closeTo(0.0, 1e-6)); expect(curve.transform(0.25).dx, closeTo(0.0966945, 1e-6)); expect(curve.transform(0.25).dy, closeTo(0.2626806, 1e-6)); expect(curve.transform(0.5).dx, closeTo(0.33, 1e-6)); expect(curve.transform(0.5).dy, closeTo(0.25, 1e-6)); expect(curve.transform(0.75).dx, closeTo(0.570260, 1e-6)); expect(curve.transform(0.75).dy, closeTo(0.883085, 1e-6)); expect(curve.transform(1.0).dx, closeTo(1.0, 1e-6)); expect(curve.transform(1.0).dy, closeTo(1.0, 1e-6)); }); test('CatmullRomSpline enforces contract when precomputed', () { expect(() { CatmullRomSpline.precompute(null); }, throwsAssertionError); expect(() { CatmullRomSpline.precompute(const []); }, throwsAssertionError); expect(() { CatmullRomSpline.precompute(const [Offset.zero]); }, throwsAssertionError); expect(() { CatmullRomSpline.precompute(const [Offset.zero, Offset.zero]); }, throwsAssertionError); expect(() { CatmullRomSpline.precompute(const [Offset.zero, Offset.zero, Offset.zero]); }, throwsAssertionError); expect(() { CatmullRomSpline.precompute(const [Offset.zero, Offset.zero, Offset.zero, Offset.zero], tension: -1.0); }, throwsAssertionError); expect(() { CatmullRomSpline.precompute(const [Offset.zero, Offset.zero, Offset.zero, Offset.zero], tension: 2.0); }, throwsAssertionError); }); test('CatmullRomCurve interpolates given points correctly', () { final CatmullRomCurve curve = CatmullRomCurve( const [ Offset(0.2, 0.25), Offset(0.33, 0.25), Offset(0.5, 1.0), Offset(0.8, 0.75), ], ); // These values are approximations. const double tolerance = 1e-6; expect(curve.transform(0.0), closeTo(0.0, tolerance)); expect(curve.transform(0.01), closeTo(0.012874734350170863, tolerance)); expect(curve.transform(0.2), closeTo(0.24989646045277542, tolerance)); expect(curve.transform(0.33), closeTo(0.250037698527661, tolerance)); expect(curve.transform(0.5), closeTo(0.9999057323235939, tolerance)); expect(curve.transform(0.6), closeTo(0.9357294964536621, tolerance)); expect(curve.transform(0.8), closeTo(0.7500423402378034, tolerance)); expect(curve.transform(1.0), closeTo(1.0, tolerance)); }); test('CatmullRomCurve interpolates given points correctly when precomputed', () { final CatmullRomCurve curve = CatmullRomCurve.precompute( const [ Offset(0.2, 0.25), Offset(0.33, 0.25), Offset(0.5, 1.0), Offset(0.8, 0.75), ], ); // These values are approximations. const double tolerance = 1e-6; expect(curve.transform(0.0), closeTo(0.0, tolerance)); expect(curve.transform(0.01), closeTo(0.012874734350170863, tolerance)); expect(curve.transform(0.2), closeTo(0.24989646045277542, tolerance)); expect(curve.transform(0.33), closeTo(0.250037698527661, tolerance)); expect(curve.transform(0.5), closeTo(0.9999057323235939, tolerance)); expect(curve.transform(0.6), closeTo(0.9357294964536621, tolerance)); expect(curve.transform(0.8), closeTo(0.7500423402378034, tolerance)); expect(curve.transform(1.0), closeTo(1.0, tolerance)); }); test('CatmullRomCurve enforces contract', () { expect(() { CatmullRomCurve(null); }, throwsAssertionError); expect(() { CatmullRomCurve(const []); }, throwsAssertionError); expect(() { CatmullRomCurve(const [Offset.zero]); }, throwsAssertionError); expect(() { CatmullRomCurve(const [Offset.zero, Offset.zero]); }, throwsAssertionError); // Monotonically increasing in X. expect( CatmullRomCurve.validateControlPoints( const [ Offset(0.2, 0.25), Offset(0.01, 0.25), ], tension: 0.0, ), isFalse); expect(() { CatmullRomCurve( const [ Offset(0.2, 0.25), Offset(0.01, 0.25), ], tension: 0.0, ); }, throwsAssertionError); // X within range (0.0, 1.0). expect( CatmullRomCurve.validateControlPoints( const [ Offset(0.2, 0.25), Offset(1.01, 0.25), ], tension: 0.0, ), isFalse); expect(() { CatmullRomCurve( const [ Offset(0.2, 0.25), Offset(1.01, 0.25), ], tension: 0.0, ); }, throwsAssertionError); // Not multi-valued in Y at x=0.0. expect( CatmullRomCurve.validateControlPoints( const [ Offset(0.05, 0.50), Offset(0.50, 0.50), Offset(0.75, 0.75), ], tension: 0.0, ), isFalse, ); expect(() { CatmullRomCurve( const [ Offset(0.05, 0.50), Offset(0.50, 0.50), Offset(0.75, 0.75), ], tension: 0.0, ); }, throwsAssertionError); // Not multi-valued in Y at x=1.0. expect( CatmullRomCurve.validateControlPoints( const [ Offset(0.25, 0.25), Offset(0.50, 0.50), Offset(0.95, 0.51), ], tension: 0.0, ), isFalse, ); expect(() { CatmullRomCurve( const [ Offset(0.25, 0.25), Offset(0.50, 0.50), Offset(0.95, 0.51), ], tension: 0.0, ); }, throwsAssertionError); // Not multi-valued in Y in between x = 0.0 and x = 1.0. expect( CatmullRomCurve.validateControlPoints( const [ Offset(0.5, 0.05), Offset(0.5, 0.95), ], tension: 0.0, ), isFalse, ); expect(() { CatmullRomCurve( const [ Offset(0.5, 0.05), Offset(0.5, 0.95), ], tension: 0.0, ); }, throwsAssertionError); }); test('CatmullRomCurve enforces contract when precomputed', () { expect(() { CatmullRomCurve.precompute(null); }, throwsAssertionError); expect(() { CatmullRomCurve.precompute(const []); }, throwsAssertionError); expect(() { CatmullRomCurve.precompute(const [Offset.zero]); }, throwsAssertionError); expect(() { CatmullRomCurve.precompute(const [Offset.zero, Offset.zero]); }, throwsAssertionError); // Monotonically increasing in X. expect(() { CatmullRomCurve.precompute( const [ Offset(0.2, 0.25), Offset(0.01, 0.25), ], tension: 0.0, ); }, throwsAssertionError); // X within range (0.0, 1.0). expect(() { CatmullRomCurve.precompute( const [ Offset(0.2, 0.25), Offset(1.01, 0.25), ], tension: 0.0, ); }, throwsAssertionError); // Not multi-valued in Y at x=0.0. expect(() { CatmullRomCurve.precompute( const [ Offset(0.05, 0.50), Offset(0.50, 0.50), Offset(0.75, 0.75), ], tension: 0.0, ); }, throwsAssertionError); // Not multi-valued in Y at x=1.0. expect(() { CatmullRomCurve.precompute( const [ Offset(0.25, 0.25), Offset(0.50, 0.50), Offset(0.95, 0.51), ], tension: 0.0, ); }, throwsAssertionError); // Not multi-valued in Y in between x = 0.0 and x = 1.0. expect(() { CatmullRomCurve.precompute( const [ Offset(0.5, 0.05), Offset(0.5, 0.95), ], tension: 0.0, ); }, throwsAssertionError); }); }