blob: 95b0e5a72f5f2cdedf460f88c681c6cabae0c2c3 [file] [log] [blame]
// Copyright 2016 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.
import 'dart:math' as math;
import 'simulation.dart';
import 'tolerance.dart';
class FrictionSimulation extends Simulation {
final double _drag;
final double _dragLog;
final double _x;
final double _v;
FrictionSimulation(double drag, double position, double velocity)
: _drag = drag,
_dragLog = math.log(drag),
_x = position,
_v = velocity;
// Return the drag value for a FrictionSimulation whose x() and dx() values pass
// through the specified start and end position/velocity values.
//
// Total time to reach endVelocity is just: (log(endVelocity) / log(startVelocity)) / log(_drag)
// or (log(v1) - log(v0)) / log(D), given v = v0 * D^t per the dx() function below.
// Solving for D given x(time) is trickier. Algebra courtesy of Wolfram Alpha:
// x1 = x0 + (v0 * D^((log(v1) - log(v0)) / log(D))) / log(D) - v0 / log(D), find D
static double _dragFor(double startPosition, double endPosition, double startVelocity, double endVelocity) {
return math.pow(math.E, (startVelocity - endVelocity) / (startPosition - endPosition));
}
// A friction simulation that starts and ends at the specified positions
// and velocities.
factory FrictionSimulation.through(double startPosition, double endPosition, double startVelocity, double endVelocity) {
return new FrictionSimulation(
_dragFor(startPosition, endPosition, startVelocity, endVelocity),
startPosition,
startVelocity)
.. tolerance = new Tolerance(velocity: endVelocity.abs());
}
double x(double time) => _x + _v * math.pow(_drag, time) / _dragLog - _v / _dragLog;
double dx(double time) => _v * math.pow(_drag, time);
@override
bool isDone(double time) => dx(time).abs() < this.tolerance.velocity;
}
class BoundedFrictionSimulation extends FrictionSimulation {
BoundedFrictionSimulation(
double drag,
double position,
double velocity,
this._minX,
this._maxX
) : super(drag, position, velocity);
final double _minX;
final double _maxX;
double x(double time) {
return super.x(time).clamp(_minX, _maxX);
}
bool isDone(double time) {
return super.isDone(time) ||
(x(time) - _minX).abs() < tolerance.distance ||
(x(time) - _maxX).abs() < tolerance.distance;
}
}