From 1ad857b53b95ffca1bd75b94bc84d159ea383e27 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 14 Jul 2015 16:00:00 -0700 Subject: [PATCH] Allow explicitly setting tolerances on simulations --- packages/newton/lib/newton.dart | 1 + .../newton/lib/src/friction_simulation.dart | 2 +- packages/newton/lib/src/scroll_simulation.dart | 12 +++++++----- packages/newton/lib/src/simulation.dart | 3 +-- packages/newton/lib/src/simulation_group.dart | 15 ++++++++++++--- packages/newton/lib/src/spring_simulation.dart | 3 ++- packages/newton/lib/src/tolerance.dart | 17 +++++++++++++++++ packages/newton/lib/src/utils.dart | 8 +++----- packages/newton/test/newton_test.dart | 12 ++++++++++++ 9 files changed, 56 insertions(+), 17 deletions(-) create mode 100644 packages/newton/lib/src/tolerance.dart diff --git a/packages/newton/lib/newton.dart b/packages/newton/lib/newton.dart index 47de84a7904..c70ca3cb027 100644 --- a/packages/newton/lib/newton.dart +++ b/packages/newton/lib/newton.dart @@ -8,6 +8,7 @@ import 'dart:math' as math; part 'src/simulation.dart'; part 'src/simulation_group.dart'; +part 'src/tolerance.dart'; part 'src/utils.dart'; part 'src/friction_simulation.dart'; diff --git a/packages/newton/lib/src/friction_simulation.dart b/packages/newton/lib/src/friction_simulation.dart index 3aa9ce8d0f2..590c5e408dd 100644 --- a/packages/newton/lib/src/friction_simulation.dart +++ b/packages/newton/lib/src/friction_simulation.dart @@ -22,5 +22,5 @@ class FrictionSimulation extends Simulation { double dx(double time) => _v * math.pow(_drag, time); @override - bool isDone(double time) => dx(time).abs() < 1.0; + bool isDone(double time) => dx(time).abs() < this.tolerance.velocity; } diff --git a/packages/newton/lib/src/scroll_simulation.dart b/packages/newton/lib/src/scroll_simulation.dart index 123e8d1b411..b3229454144 100644 --- a/packages/newton/lib/src/scroll_simulation.dart +++ b/packages/newton/lib/src/scroll_simulation.dart @@ -27,7 +27,7 @@ class ScrollSimulation extends SimulationGroup { } @override - void step(double time) => _chooseSimulation( + bool step(double time) => _chooseSimulation( _currentSimulation.x(time - _offset), _currentSimulation.dx(time - _offset), time); @@ -37,7 +37,7 @@ class ScrollSimulation extends SimulationGroup { @override double get currentIntervalOffset => _offset; - void _chooseSimulation( + bool _chooseSimulation( double position, double velocity, double intervalOffset) { /// This simulation can only step forward. @@ -47,19 +47,21 @@ class ScrollSimulation extends SimulationGroup { _offset = intervalOffset; _currentSimulation = new SpringSimulation( _springDesc, position, _trailingExtent, velocity); - return; + return true; } else if (position < _leadingExtent) { _isSpringing = true; _offset = intervalOffset; _currentSimulation = new SpringSimulation( _springDesc, position, _leadingExtent, velocity); - return; + return true; } } if (_currentSimulation == null) { _currentSimulation = new FrictionSimulation(_drag, position, velocity); - return; + return true; } + + return false; } } diff --git a/packages/newton/lib/src/simulation.dart b/packages/newton/lib/src/simulation.dart index ca06f4811ad..e374c9d5ed7 100644 --- a/packages/newton/lib/src/simulation.dart +++ b/packages/newton/lib/src/simulation.dart @@ -15,9 +15,8 @@ abstract class Simulatable { /// The base class for all simulations. The user is meant to instantiate an /// instance of a simulation and query the same for the position and velocity /// of the body at a given interval. -/// -/// Note: All operations on subclasses of Simulation are idempotent. abstract class Simulation implements Simulatable { + Tolerance tolerance = toleranceDefault; /// Returns if the simulation is done at a given time bool isDone(double time); diff --git a/packages/newton/lib/src/simulation_group.dart b/packages/newton/lib/src/simulation_group.dart index 9fb6f9c3654..44e7bb8673e 100644 --- a/packages/newton/lib/src/simulation_group.dart +++ b/packages/newton/lib/src/simulation_group.dart @@ -19,7 +19,8 @@ abstract class SimulationGroup extends Simulation { /// Called when a significant change in the interval is detected. Subclasses /// must decide if the the current simulation must be switched (or updated). - void step(double time); + /// The result is whether the simulation was switched in this step. + bool step(double time); double x(double time) { _stepIfNecessary(time); @@ -31,6 +32,12 @@ abstract class SimulationGroup extends Simulation { return currentSimulation.dx(time - currentIntervalOffset); } + @override + void set tolerance(Tolerance t) { + this.currentSimulation.tolerance = t; + super.tolerance = t; + } + @override bool isDone(double time) { _stepIfNecessary(time); @@ -39,11 +46,13 @@ abstract class SimulationGroup extends Simulation { double _lastStep = -1.0; void _stepIfNecessary(double time) { - if (_nearEqual(_lastStep, time)) { + if (_nearEqual(_lastStep, time, toleranceDefault.time)) { return; } _lastStep = time; - step(time); + if (step(time)) { + this.currentSimulation.tolerance = this.tolerance; + } } } diff --git a/packages/newton/lib/src/spring_simulation.dart b/packages/newton/lib/src/spring_simulation.dart index 0edf828284f..5b267b0daa1 100644 --- a/packages/newton/lib/src/spring_simulation.dart +++ b/packages/newton/lib/src/spring_simulation.dart @@ -58,5 +58,6 @@ class SpringSimulation extends Simulation { @override bool isDone(double time) => - _nearEqual(x(time), _endPosition) && _nearZero(dx(time)); + _nearEqual(x(time), _endPosition, this.tolerance.distance) && + _nearZero(dx(time), this.tolerance.velocity); } diff --git a/packages/newton/lib/src/tolerance.dart b/packages/newton/lib/src/tolerance.dart new file mode 100644 index 00000000000..b52d075e9c2 --- /dev/null +++ b/packages/newton/lib/src/tolerance.dart @@ -0,0 +1,17 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +part of newton; + +class Tolerance { + final double distance; + final double time; + final double velocity; + + const Tolerance({this.distance: epsilonDefault, this.time: epsilonDefault, + this.velocity: epsilonDefault}); +} + +const double epsilonDefault = 1e-3; +const Tolerance toleranceDefault = const Tolerance(); diff --git a/packages/newton/lib/src/utils.dart b/packages/newton/lib/src/utils.dart index e6f997fe425..75709a3a65e 100644 --- a/packages/newton/lib/src/utils.dart +++ b/packages/newton/lib/src/utils.dart @@ -4,9 +4,7 @@ part of newton; -const double _simulationEpsilon = 0.2; +bool _nearEqual(double a, double b, double epsilon) => + (a > (b - epsilon)) && (a < (b + epsilon)); -bool _nearEqual(double a, double b) => - (a > (b - _simulationEpsilon)) && (a < (b + _simulationEpsilon)); - -bool _nearZero(double a) => _nearEqual(a, 0.0); +bool _nearZero(double a, double epsilon) => _nearEqual(a, 0.0, epsilon); diff --git a/packages/newton/test/newton_test.dart b/packages/newton/test/newton_test.dart index c7b6323d72c..44c80501145 100644 --- a/packages/newton/test/newton_test.dart +++ b/packages/newton/test/newton_test.dart @@ -12,6 +12,8 @@ void main() { test('test_friction', () { var friction = new FrictionSimulation(0.3, 100.0, 400.0); + friction.tolerance = const Tolerance(velocity: 1.0); + expect(friction.isDone(0.0), false); expect(friction.x(0.0), 100); expect(friction.dx(0.0), 400.0); @@ -84,6 +86,9 @@ void main() { test('crit_spring', () { var crit = new SpringSimulation(new SpringDescription.withDampingRatio( mass: 1.0, springConstant: 100.0, ratio: 1.0), 0.0, 500.0, 0.0); + + crit.tolerance = const Tolerance(distance: 0.01, velocity: 0.01); + expect(crit.type, SpringType.criticallyDamped); expect(crit.isDone(0.0), false); @@ -106,6 +111,9 @@ void main() { test('overdamped_spring', () { var over = new SpringSimulation(new SpringDescription.withDampingRatio( mass: 1.0, springConstant: 100.0, ratio: 1.25), 0.0, 500.0, 0.0); + + over.tolerance = const Tolerance(distance: 0.01, velocity: 0.01); + expect(over.type, SpringType.overDamped); expect(over.isDone(0.0), false); @@ -145,6 +153,8 @@ void main() { var scroll = new ScrollSimulation(100.0, 800.0, 0.0, 300.0, spring, 0.3); + scroll.tolerance = const Tolerance(velocity: 0.01, distance: 0.01); + expect(scroll.isDone(0.0), false); expect(scroll.isDone(3.5), true); @@ -159,6 +169,8 @@ void main() { var scroll = new ScrollSimulation(100.0, 400.0, 0.0, double.INFINITY, spring, 0.3); + scroll.tolerance = const Tolerance(velocity: 1.0); + expect(scroll.isDone(0.0), false); expect(scroll.x(0.0), 100); expect(scroll.dx(0.0), 400.0);