Unverified Commit 4bb50465 authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

More toStrings and tests for physics (#82503)

parent 2f3f5f09
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/foundation.dart';
import 'simulation.dart'; import 'simulation.dart';
/// A simulation that applies limits to another simulation. /// A simulation that applies limits to another simulation.
...@@ -15,8 +17,13 @@ import 'simulation.dart'; ...@@ -15,8 +17,13 @@ import 'simulation.dart';
/// difference would just be that the position would be reported as pinned to /// difference would just be that the position would be reported as pinned to
/// the maximum value for the times that it would otherwise have been reported /// the maximum value for the times that it would otherwise have been reported
/// as higher. /// as higher.
///
/// Similarly, this means that the [x] value will change at a rate that does not
/// match the reported [dx] value while one or the other is being clamped.
///
/// The [isDone] logic is unaffected by the clamping; it reflects the logic of
/// the underlying simulation.
class ClampedSimulation extends Simulation { class ClampedSimulation extends Simulation {
/// Creates a [ClampedSimulation] that clamps the given simulation. /// Creates a [ClampedSimulation] that clamps the given simulation.
/// ///
/// The named arguments specify the ranges for the clamping behavior, as /// The named arguments specify the ranges for the clamping behavior, as
...@@ -55,4 +62,7 @@ class ClampedSimulation extends Simulation { ...@@ -55,4 +62,7 @@ class ClampedSimulation extends Simulation {
@override @override
bool isDone(double time) => simulation.isDone(time); bool isDone(double time) => simulation.isDone(time);
@override
String toString() => '${objectRuntimeType(this, 'ClampedSimulation')}(simulation: $simulation, x: ${xMin.toStringAsFixed(1)}..${xMax.toStringAsFixed(1)}, dx: ${dxMin.toStringAsFixed(1)}..${dxMax.toStringAsFixed(1)})';
} }
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
import 'dart:math' as math; import 'dart:math' as math;
import 'package:flutter/foundation.dart';
import 'simulation.dart'; import 'simulation.dart';
import 'tolerance.dart'; import 'tolerance.dart';
...@@ -15,8 +17,8 @@ import 'tolerance.dart'; ...@@ -15,8 +17,8 @@ import 'tolerance.dart';
/// the current velocity [tolerance]). /// the current velocity [tolerance]).
class FrictionSimulation extends Simulation { class FrictionSimulation extends Simulation {
/// Creates a [FrictionSimulation] with the given arguments, namely: the fluid /// Creates a [FrictionSimulation] with the given arguments, namely: the fluid
/// drag coefficient, a unitless value; the initial position, in the same /// drag coefficient _cₓ_, a unitless value; the initial position _x₀_, in the same
/// length units as used for [x]; and the initial velocity, in the same /// length units as used for [x]; and the initial velocity _dx₀_, in the same
/// velocity units as used for [dx]. /// velocity units as used for [dx].
FrictionSimulation( FrictionSimulation(
double drag, double drag,
...@@ -29,7 +31,7 @@ class FrictionSimulation extends Simulation { ...@@ -29,7 +31,7 @@ class FrictionSimulation extends Simulation {
_v = velocity, _v = velocity,
super(tolerance: tolerance); super(tolerance: tolerance);
/// Creates a new friction simulation with its fluid drag coefficient set so /// Creates a new friction simulation with its fluid drag coefficient (_cₓ_) set so
/// as to ensure that the simulation starts and ends at the specified /// as to ensure that the simulation starts and ends at the specified
/// positions and velocities. /// positions and velocities.
/// ///
...@@ -90,14 +92,20 @@ class FrictionSimulation extends Simulation { ...@@ -90,14 +92,20 @@ class FrictionSimulation extends Simulation {
@override @override
bool isDone(double time) => dx(time).abs() < tolerance.velocity; bool isDone(double time) => dx(time).abs() < tolerance.velocity;
@override
String toString() => '${objectRuntimeType(this, 'FrictionSimulation')}(cₓ: ${_drag.toStringAsFixed(1)}, x₀: ${_x.toStringAsFixed(1)}, dx₀: ${_v.toStringAsFixed(1)})';
} }
/// A [FrictionSimulation] that clamps the modeled particle to a specific range /// A [FrictionSimulation] that clamps the modeled particle to a specific range
/// of values. /// of values.
///
/// Only the position is clamped. The velocity [dx] will continue to report
/// unbounded simulated velocities once the particle has reached the bounds.
class BoundedFrictionSimulation extends FrictionSimulation { class BoundedFrictionSimulation extends FrictionSimulation {
/// Creates a [BoundedFrictionSimulation] with the given arguments, namely: /// Creates a [BoundedFrictionSimulation] with the given arguments, namely:
/// the fluid drag coefficient, a unitless value; the initial position, in the /// the fluid drag coefficient _cₓ_, a unitless value; the initial position _x₀_, in the
/// same length units as used for [x]; the initial velocity, in the same /// same length units as used for [x]; the initial velocity _dx₀_, in the same
/// velocity units as used for [dx], the minimum value for the position, and /// velocity units as used for [dx], the minimum value for the position, and
/// the maximum value for the position. The minimum and maximum values must be /// the maximum value for the position. The minimum and maximum values must be
/// in the same units as the initial position, and the initial position must /// in the same units as the initial position, and the initial position must
...@@ -125,4 +133,7 @@ class BoundedFrictionSimulation extends FrictionSimulation { ...@@ -125,4 +133,7 @@ class BoundedFrictionSimulation extends FrictionSimulation {
(x(time) - _minX).abs() < tolerance.distance || (x(time) - _minX).abs() < tolerance.distance ||
(x(time) - _maxX).abs() < tolerance.distance; (x(time) - _maxX).abs() < tolerance.distance;
} }
@override
String toString() => '${objectRuntimeType(this, 'BoundedFrictionSimulation')}(cₓ: ${_drag.toStringAsFixed(1)}, x₀: ${_x.toStringAsFixed(1)}, dx₀: ${_v.toStringAsFixed(1)}, x: ${_minX.toStringAsFixed(1)}..${_maxX.toStringAsFixed(1)})';
} }
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/foundation.dart';
import 'simulation.dart'; import 'simulation.dart';
// Examples can assume: // Examples can assume:
...@@ -10,7 +12,7 @@ import 'simulation.dart'; ...@@ -10,7 +12,7 @@ import 'simulation.dart';
/// A simulation that applies a constant accelerating force. /// A simulation that applies a constant accelerating force.
/// ///
/// Models a particle that follows Newton's second law of motion. The simulation /// Models a particle that follows Newton's second law of motion. The simulation
/// ends when the position reaches a defined point. /// ends when the position exceeds a defined threshold.
/// ///
/// {@tool snippet} /// {@tool snippet}
/// ///
...@@ -32,6 +34,18 @@ import 'simulation.dart'; ...@@ -32,6 +34,18 @@ import 'simulation.dart';
/// This [AnimationController] could be used with an [AnimatedBuilder] to /// This [AnimationController] could be used with an [AnimatedBuilder] to
/// animate the position of a child as if it was falling. /// animate the position of a child as if it was falling.
/// ///
/// The end distance threshold (the constructor's third argument) must be
/// specified as a positive number but can be reached in either the positive or
/// negative direction. For example (assuming negative numbers represent higher
/// physical positions than positive numbers, as is the case with the normal
/// [Canvas] coordinate system), if the acceleration is positive ("down") the
/// starting velocity is negative ("up"), and the starting distance is zero, the
/// particle will climb from the origin, reach a plateau, then fall back towards
/// and past the origin. If the end distance threshold is less than the height
/// of the plateau, then the simulation will end during the climb; otherwise, it
/// will end during the fall, after the particle travels below the origin by
/// that distance.
///
/// See also: /// See also:
/// ///
/// * [Curves.bounceOut], a [Curve] that has a similar aesthetics but includes /// * [Curves.bounceOut], a [Curve] that has a similar aesthetics but includes
...@@ -79,4 +93,7 @@ class GravitySimulation extends Simulation { ...@@ -79,4 +93,7 @@ class GravitySimulation extends Simulation {
@override @override
bool isDone(double time) => x(time).abs() >= _end; bool isDone(double time) => x(time).abs() >= _end;
@override
String toString() => '${objectRuntimeType(this, 'GravitySimulation')}(g: ${_a.toStringAsFixed(1)}, x₀: ${_x.toStringAsFixed(1)}, dx₀: ${_v.toStringAsFixed(1)}, xₘₐₓ: ±${_end.toStringAsFixed(1)})';
} }
...@@ -121,7 +121,7 @@ class SpringSimulation extends Simulation { ...@@ -121,7 +121,7 @@ class SpringSimulation extends Simulation {
} }
@override @override
String toString() => '${objectRuntimeType(this, 'SpringSimulation')}(end: $_endPosition, $type)'; String toString() => '${objectRuntimeType(this, 'SpringSimulation')}(end: ${_endPosition.toStringAsFixed(1)}, $type)';
} }
/// A [SpringSimulation] where the value of [x] is guaranteed to have exactly the /// A [SpringSimulation] where the value of [x] is guaranteed to have exactly the
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/foundation.dart';
/// Structure that specifies maximum allowable magnitudes for distances, /// Structure that specifies maximum allowable magnitudes for distances,
/// durations, and velocity differences to be considered equal. /// durations, and velocity differences to be considered equal.
class Tolerance { class Tolerance {
...@@ -42,5 +44,5 @@ class Tolerance { ...@@ -42,5 +44,5 @@ class Tolerance {
final double velocity; final double velocity;
@override @override
String toString() => 'Tolerance(distance: ±$distance, time: ±$time, velocity: ±$velocity)'; String toString() => '${objectRuntimeType(this, 'Tolerance')}(distance: ±$distance, time: ±$time, velocity: ±$velocity)';
} }
...@@ -6,7 +6,7 @@ import 'package:flutter/physics.dart'; ...@@ -6,7 +6,7 @@ import 'package:flutter/physics.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
void main() { void main() {
test('Clamped simulation', () { test('Clamped simulation 1', () {
final GravitySimulation gravity = GravitySimulation(9.81, 10.0, 0.0, 0.0); final GravitySimulation gravity = GravitySimulation(9.81, 10.0, 0.0, 0.0);
final ClampedSimulation clamped = ClampedSimulation(gravity, xMin: 20.0, xMax: 100.0, dxMin: 7.0, dxMax: 11.0); final ClampedSimulation clamped = ClampedSimulation(gravity, xMin: 20.0, xMax: 100.0, dxMin: 7.0, dxMax: 11.0);
...@@ -16,4 +16,25 @@ void main() { ...@@ -16,4 +16,25 @@ void main() {
expect(clamped.x(100.0), equals(100.0)); expect(clamped.x(100.0), equals(100.0));
expect(clamped.dx(100.0), equals(11.0)); expect(clamped.dx(100.0), equals(11.0));
}); });
test('Clamped simulation 2', () {
final GravitySimulation gravity = GravitySimulation(-10, 0.0, 6.0, 10.0);
final ClampedSimulation clamped = ClampedSimulation(gravity, xMin: 0.0, xMax: 2.5, dxMin: -1.0, dxMax: 1.0);
expect(clamped.x(0.0), equals(0.0));
expect(clamped.dx(0.0), equals(1.0));
expect(clamped.isDone(0.0), isFalse);
expect(clamped.x(1.0), equals(2.5));
expect(clamped.dx(1.0), equals(0.0));
expect(clamped.isDone(0.2), isFalse);
expect(clamped.x(2.0), equals(0.0));
expect(clamped.dx(2.0), equals(-1.0));
expect(clamped.isDone(2.0), isFalse);
expect(clamped.x(3.0), equals(0.0));
expect(clamped.dx(3.0), equals(-1.0));
expect(clamped.isDone(3.0), isTrue);
});
} }
...@@ -6,8 +6,28 @@ import 'package:flutter/physics.dart'; ...@@ -6,8 +6,28 @@ import 'package:flutter/physics.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
void main() { void main() {
test('gravity simulation', () { test('Gravity simulation 1', () {
expect(GravitySimulation(9.81, 10.0, 0.0, 0.0), hasOneLineDescription); expect(GravitySimulation(9.81, 10.0, 0.0, 0.0), hasOneLineDescription);
expect(GravitySimulation(9.81, 10.0, 0.0, 0.0).x(10.0), moreOrLessEquals(50.0 * 9.81 + 10.0)); expect(GravitySimulation(9.81, 10.0, 0.0, 0.0).x(10.0), moreOrLessEquals(50.0 * 9.81 + 10.0));
}); });
test('Gravity simulation 2', () {
final GravitySimulation gravity = GravitySimulation(-10, 0.0, 6.0, 10.0);
expect(gravity.x(0.0), equals(0.0));
expect(gravity.dx(0.0), equals(10.0));
expect(gravity.isDone(0.0), isFalse);
expect(gravity.x(1.0), equals(5.0));
expect(gravity.dx(1.0), equals(0.0));
expect(gravity.isDone(0.2), isFalse);
expect(gravity.x(2.0), equals(0.0));
expect(gravity.dx(2.0), equals(-10.0));
expect(gravity.isDone(2.0), isFalse);
expect(gravity.x(3.0), equals(-15.0));
expect(gravity.dx(3.0), equals(-20.0));
expect(gravity.isDone(3.0), isTrue);
});
} }
// 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 'package:flutter/physics.dart';
import 'package:flutter_test/flutter_test.dart';
class TestSimulation extends Simulation {
@override
double x(double t) => 0.0;
@override
double dx(double t) => 0.0;
@override
bool isDone(double t) => true;
}
void main() {
test('Simulation.toString', () {
expect(ClampedSimulation(TestSimulation(), xMin: -1.0, xMax: 2.0, dxMin: -3.0, dxMax: 4.0).toString(), 'ClampedSimulation(simulation: TestSimulation, x: -1.0..2.0, dx: -3.0..4.0)');
expect(TestSimulation().toString(), 'TestSimulation');
expect(GravitySimulation(1.0, -2.0, 3.0, -4.0).toString(), 'GravitySimulation(g: 1.0, x₀: -2.0, dx₀: -4.0, xₘₐₓ: ±3.0)');
expect(FrictionSimulation(1.0, -2.0, 3.0).toString(), 'FrictionSimulation(cₓ: 1.0, x₀: -2.0, dx₀: 3.0)');
expect(BoundedFrictionSimulation(1.0, -2.0, 3.0, -4.0, 5.0).toString(), 'BoundedFrictionSimulation(cₓ: 1.0, x₀: -2.0, dx₀: 3.0, x: -4.0..5.0)');
expect(const SpringDescription(mass: 1.0, stiffness: -2.0, damping: 3.0).toString(), 'SpringDescription(mass: 1.0, stiffness: -2.0, damping: 3.0)');
expect(SpringDescription.withDampingRatio(mass: 1.0, stiffness: 9.0).toString(), 'SpringDescription(mass: 1.0, stiffness: 9.0, damping: 6.0)');
expect(SpringSimulation(const SpringDescription(mass: 1.0, stiffness: 2.0, damping: 3.0), 0.0, 1.0, 2.0).toString(), 'SpringSimulation(end: 1.0, SpringType.overDamped)');
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment