mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Implement simulation groups for kinetic scrolling
This commit is contained in:
parent
5a439792f1
commit
8c91b9b7c7
@ -7,9 +7,9 @@ library newton;
|
||||
import 'dart:math' as Math;
|
||||
|
||||
part 'src/simulation.dart';
|
||||
part 'src/simulation_group.dart';
|
||||
part 'src/utils.dart';
|
||||
|
||||
part 'src/fall.dart';
|
||||
part 'src/friction.dart';
|
||||
part 'src/gravity.dart';
|
||||
part 'src/scroll.dart';
|
||||
|
@ -1,8 +0,0 @@
|
||||
// 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;
|
||||
|
||||
// TODO(csg): Composite simulation
|
||||
class Fall {}
|
@ -4,5 +4,48 @@
|
||||
|
||||
part of newton;
|
||||
|
||||
// TODO(csg): Composite simulation
|
||||
class Scroll {}
|
||||
/// Simulates kinetic scrolling behavior between a leading and trailing
|
||||
/// boundary. Friction is applied within the extends and a spring action applied
|
||||
/// at the boundaries. This simulation can only step forward.
|
||||
class Scroll extends SimulationGroup {
|
||||
final double _leadingExtent;
|
||||
final double _trailingExtent;
|
||||
final SpringDesc _springDesc;
|
||||
final double _drag;
|
||||
|
||||
bool _isSpringing = false;
|
||||
Simulation _currentSimulation;
|
||||
|
||||
Scroll(double position, double velocity, double leading, double trailing,
|
||||
SpringDesc spring, double drag)
|
||||
: _leadingExtent = leading,
|
||||
_trailingExtent = trailing,
|
||||
_springDesc = spring,
|
||||
_drag = drag {
|
||||
_chooseSimulation(position, velocity);
|
||||
}
|
||||
|
||||
@override
|
||||
void step(double time) => _chooseSimulation(
|
||||
_currentSimulation.x(time), _currentSimulation.dx(time));
|
||||
|
||||
@override
|
||||
Simulation get currentSimulation => _currentSimulation;
|
||||
|
||||
void _chooseSimulation(double position, double velocity) {
|
||||
/// This simulation can only step forward
|
||||
if (!_isSpringing) {
|
||||
if (position > _trailingExtent) {
|
||||
_isSpringing = true;
|
||||
_currentSimulation =
|
||||
new Spring(_springDesc, position, _trailingExtent, velocity);
|
||||
} else if (position < _leadingExtent) {
|
||||
_isSpringing = true;
|
||||
_currentSimulation =
|
||||
new Spring(_springDesc, position, _leadingExtent, velocity);
|
||||
}
|
||||
} else if (_currentSimulation == null) {
|
||||
_currentSimulation = new Friction(_drag, position, velocity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,9 +16,7 @@ abstract class Simulatable {
|
||||
/// instance of a simulation and query the same for the position and velocity
|
||||
/// of the body at a given interval.
|
||||
///
|
||||
/// Note: All operation on subclasses of Simulation are idempotent. Composite
|
||||
/// simulations are not guaranteed to be idempotent however. FIXME(csg): How do
|
||||
/// I make this apparent?
|
||||
/// Note: All operations on subclasses of Simulation are idempotent.
|
||||
abstract class Simulation implements Simulatable {
|
||||
|
||||
/// Returns if the simulation is done at a given time
|
||||
|
42
packages/newton/lib/src/simulation_group.dart
Normal file
42
packages/newton/lib/src/simulation_group.dart
Normal file
@ -0,0 +1,42 @@
|
||||
// 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;
|
||||
|
||||
/// The abstract base class of all composite simulations. Concrete subclasses
|
||||
/// must implement the appropriate methods to select the appropriate simulation
|
||||
/// at a given time interval. The simulation group takes care to call the `step`
|
||||
/// method at appropriate intervals. If more fine grained control over the the
|
||||
/// step is necessary, subclasses may override the `Simulatable` methods.
|
||||
abstract class SimulationGroup extends Simulation {
|
||||
Simulation get currentSimulation;
|
||||
|
||||
void step(double time);
|
||||
|
||||
double x(double time) {
|
||||
_stepIfNecessary(time);
|
||||
return currentSimulation.x(time);
|
||||
}
|
||||
|
||||
double dx(double time) {
|
||||
_stepIfNecessary(time);
|
||||
return currentSimulation.dx(time);
|
||||
}
|
||||
|
||||
@override
|
||||
bool isDone(double time) {
|
||||
_stepIfNecessary(time);
|
||||
return currentSimulation.isDone(time);
|
||||
}
|
||||
|
||||
double _lastStep = -1.0;
|
||||
void _stepIfNecessary(double time) {
|
||||
if (_nearEqual(_lastStep, time)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_lastStep = time;
|
||||
step(time);
|
||||
}
|
||||
}
|
@ -23,63 +23,87 @@ abstract class _SpringSolution implements Simulatable {
|
||||
}
|
||||
|
||||
class _CriticalSolution implements _SpringSolution {
|
||||
double r, c1, c2;
|
||||
final double _r, _c1, _c2;
|
||||
|
||||
_CriticalSolution(SpringDesc desc, double distance, double velocity) {
|
||||
r = -desc.damping / (2.0 * desc.mass);
|
||||
c1 = distance;
|
||||
c2 = velocity / (r * distance);
|
||||
factory _CriticalSolution(SpringDesc desc, double distance, double velocity) {
|
||||
final double r = -desc.damping / (2.0 * desc.mass);
|
||||
final double c1 = distance;
|
||||
final double c2 = velocity / (r * distance);
|
||||
return new _CriticalSolution.withArgs(r, c1, c2);
|
||||
}
|
||||
|
||||
double x(double time) => (c1 + c2 * time) * Math.pow(Math.E, r * time);
|
||||
_CriticalSolution.withArgs(double r, double c1, double c2)
|
||||
: _r = r,
|
||||
_c1 = c1,
|
||||
_c2 = c2;
|
||||
|
||||
double x(double time) => (_c1 + _c2 * time) * Math.pow(Math.E, _r * time);
|
||||
|
||||
double dx(double time) {
|
||||
final double power = Math.pow(Math.E, r * time);
|
||||
return r * (c1 + c2 * time) * power + c2 * power;
|
||||
final double power = Math.pow(Math.E, _r * time);
|
||||
return _r * (_c1 + _c2 * time) * power + _c2 * power;
|
||||
}
|
||||
}
|
||||
|
||||
class _OverdampedSolution implements _SpringSolution {
|
||||
double r1, r2, c1, c2;
|
||||
final double _r1, _r2, _c1, _c2;
|
||||
|
||||
_OverdampedSolution(SpringDesc desc, double distance, double velocity) {
|
||||
double cmk =
|
||||
factory _OverdampedSolution(
|
||||
SpringDesc desc, double distance, double velocity) {
|
||||
final double cmk =
|
||||
desc.damping * desc.damping - 4 * desc.mass * desc.springConstant;
|
||||
|
||||
r1 = (-desc.damping - Math.sqrt(cmk)) / (2.0 * desc.mass);
|
||||
r2 = (-desc.damping + Math.sqrt(cmk)) / (2.0 * desc.mass);
|
||||
c2 = (velocity - r1 * distance) / (r2 - r1);
|
||||
c1 = distance - c2;
|
||||
final double r1 = (-desc.damping - Math.sqrt(cmk)) / (2.0 * desc.mass);
|
||||
final double r2 = (-desc.damping + Math.sqrt(cmk)) / (2.0 * desc.mass);
|
||||
final double c2 = (velocity - r1 * distance) / (r2 - r1);
|
||||
final double c1 = distance - c2;
|
||||
|
||||
return new _OverdampedSolution.withArgs(r1, r2, c1, c2);
|
||||
}
|
||||
|
||||
double x(double time) =>
|
||||
(c1 * Math.pow(Math.E, r1 * time) + c2 * Math.pow(Math.E, r2 * time));
|
||||
_OverdampedSolution.withArgs(double r1, double r2, double c1, double c2)
|
||||
: _r1 = r1,
|
||||
_r2 = r2,
|
||||
_c1 = c1,
|
||||
_c2 = c2;
|
||||
|
||||
double dx(double time) => (c1 * r1 * Math.pow(Math.E, r1 * time) +
|
||||
c2 * r2 * Math.pow(Math.E, r2 * time));
|
||||
double x(double time) =>
|
||||
(_c1 * Math.pow(Math.E, _r1 * time) + _c2 * Math.pow(Math.E, _r2 * time));
|
||||
|
||||
double dx(double time) => (_c1 * _r1 * Math.pow(Math.E, _r1 * time) +
|
||||
_c2 * _r2 * Math.pow(Math.E, _r2 * time));
|
||||
}
|
||||
|
||||
class _UnderdampedSolution implements _SpringSolution {
|
||||
double w, r, c1, c2;
|
||||
final double _w, _r, _c1, _c2;
|
||||
|
||||
_UnderdampedSolution(SpringDesc desc, double distance, double velocity) {
|
||||
w = Math.sqrt(4.0 * desc.mass * desc.springConstant -
|
||||
factory _UnderdampedSolution(
|
||||
SpringDesc desc, double distance, double velocity) {
|
||||
final double w = Math.sqrt(4.0 * desc.mass * desc.springConstant -
|
||||
desc.damping * desc.damping) /
|
||||
(2.0 * desc.mass);
|
||||
r = -(desc.damping / 2.0 * desc.mass);
|
||||
c1 = distance;
|
||||
c2 = (velocity - r * distance) / w;
|
||||
final double r = -(desc.damping / 2.0 * desc.mass);
|
||||
final double c1 = distance;
|
||||
final double c2 = (velocity - r * distance) / w;
|
||||
|
||||
return new _UnderdampedSolution.withArgs(w, r, c1, c2);
|
||||
}
|
||||
|
||||
double x(double time) => Math.pow(Math.E, r * time) *
|
||||
(c1 * Math.cos(w * time) + c2 * Math.sin(w * time));
|
||||
_UnderdampedSolution.withArgs(double w, double r, double c1, double c2)
|
||||
: _w = w,
|
||||
_r = r,
|
||||
_c1 = c1,
|
||||
_c2 = c2;
|
||||
|
||||
double x(double time) => Math.pow(Math.E, _r * time) *
|
||||
(_c1 * Math.cos(_w * time) + _c2 * Math.sin(_w * time));
|
||||
|
||||
double dx(double time) {
|
||||
final double power = Math.pow(Math.E, r * time);
|
||||
final double cosine = Math.cos(w * time);
|
||||
final double sine = Math.sin(w * time);
|
||||
final double power = Math.pow(Math.E, _r * time);
|
||||
final double cosine = Math.cos(_w * time);
|
||||
final double sine = Math.sin(_w * time);
|
||||
|
||||
return power * (c2 * w * cosine - c1 * w * sine) +
|
||||
r * power * (c2 * sine + c1 * cosine);
|
||||
return power * (_c2 * _w * cosine - _c1 * _w * sine) +
|
||||
_r * power * (_c2 * sine + _c1 * cosine);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user