friction_simulation.dart 4.33 KB
Newer Older
1
// Copyright 2016 The Chromium Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6 7 8
import 'dart:math' as math;

import 'simulation.dart';
import 'tolerance.dart';
9

Ian Hickson's avatar
Ian Hickson committed
10 11 12 13 14 15
/// A simulation that applies a drag to slow a particle down.
///
/// Models a particle affected by fluid drag, e.g. air resistance.
///
/// The simulation ends when the velocity of the particle drops to zero (within
/// the current velocity [tolerance]).
16
class FrictionSimulation extends Simulation {
Ian Hickson's avatar
Ian Hickson committed
17 18 19 20
  /// Creates a [FrictionSimulation] with the given arguments, namely: the fluid
  /// drag coefficient, a unitless value; the initial position, in the same
  /// length units as used for [x]; and the initial velocity, in the same
  /// velocity units as used for [dx].
Ian Hickson's avatar
Ian Hickson committed
21 22 23 24 25 26
  FrictionSimulation(double drag, double position, double velocity)
    : _drag = drag,
      _dragLog = math.log(drag),
      _x = position,
      _v = velocity;

Ian Hickson's avatar
Ian Hickson committed
27 28 29 30 31 32 33 34 35 36 37
  /// Creates a new friction simulation with its fluid drag coefficient set so
  /// as to ensure that the simulation starts and ends at the specified
  /// positions and velocities.
  ///
  /// The positions must use the same units as expected from [x], and the
  /// velocities must use the same units as expected from [dx].
  ///
  /// The sign of the start and end velocities must be the same, the magnitude
  /// of the start velocity must be greater than the magnitude of the end
  /// velocity, and the velocities must be in the direction appropriate for the
  /// particle to start from the start position and reach the end position.
Ian Hickson's avatar
Ian Hickson committed
38
  factory FrictionSimulation.through(double startPosition, double endPosition, double startVelocity, double endVelocity) {
39
    assert(startVelocity == 0.0 || endVelocity == 0.0 || startVelocity.sign == endVelocity.sign);
Ian Hickson's avatar
Ian Hickson committed
40 41
    assert(startVelocity.abs() >= endVelocity.abs());
    assert((endPosition - startPosition).sign == startVelocity.sign);
Ian Hickson's avatar
Ian Hickson committed
42 43 44
    return new FrictionSimulation(
      _dragFor(startPosition, endPosition, startVelocity, endVelocity),
      startPosition,
Ian Hickson's avatar
Ian Hickson committed
45 46
      startVelocity
    )..tolerance = new Tolerance(velocity: endVelocity.abs());
Ian Hickson's avatar
Ian Hickson committed
47 48
  }

49
  final double _drag;
50
  final double _dragLog;
51 52 53
  final double _x;
  final double _v;

54 55 56 57 58 59 60 61 62 63 64
  // 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));
  }

65
  @override
66
  double x(double time) => _x + _v * math.pow(_drag, time) / _dragLog - _v / _dragLog;
67

68
  @override
69
  double dx(double time) => _v * math.pow(_drag, time);
70 71

  @override
72
  bool isDone(double time) => dx(time).abs() < tolerance.velocity;
73
}
74

75
/// A [FrictionSimulation] that clamps the modeled particle to a specific range
Ian Hickson's avatar
Ian Hickson committed
76
/// of values.
77
class BoundedFrictionSimulation extends FrictionSimulation {
Ian Hickson's avatar
Ian Hickson committed
78 79 80 81 82 83 84
  /// Creates a [BoundedFrictionSimulation] with the given arguments, namely:
  /// the fluid drag coefficient, a unitless value; the initial position, in the
  /// same length units as used for [x]; the initial velocity, in the same
  /// 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
  /// in the same units as the initial position, and the initial position must
  /// be within the given range.
85 86 87 88
  BoundedFrictionSimulation(
    double drag,
    double position,
    double velocity,
Hixie's avatar
Hixie committed
89 90
    this._minX,
    this._maxX
Ian Hickson's avatar
Ian Hickson committed
91 92 93
  ) : super(drag, position, velocity) {
    assert(position.clamp(_minX, _maxX) == position);
  }
94 95 96 97

  final double _minX;
  final double _maxX;

98
  @override
99 100 101 102
  double x(double time) {
    return super.x(time).clamp(_minX, _maxX);
  }

103
  @override
104 105 106 107 108 109
  bool isDone(double time) {
    return super.isDone(time) ||
      (x(time) - _minX).abs() < tolerance.distance ||
      (x(time) - _maxX).abs() < tolerance.distance;
  }
}