blob: 656d75825199dc489614cb60680d85100288426f [file] [edit]
// Copyright 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.
import 'dart:math' as math;
const double kGravity = -0.980;
abstract class System {
void update(double deltaT);
}
class Particle extends System {
final double mass;
double velocity;
double position;
Particle({this.mass: 1.0, this.velocity: 0.0, this.position: 0.0});
void applyImpulse(double impulse) {
velocity += impulse / mass;
}
void update(double deltaT) {
position += velocity * deltaT;
}
double get energy => 0.5 * mass * velocity * velocity;
set energy(double e) {
assert(e >= 0.0);
velocity = math.sqrt(2 * e / mass);
}
}
class Box {
final double min;
final double max;
Box({this.min, this.max}) {
assert(min == null || max == null || min <= max);
}
void confine(Particle p) {
if (min != null) {
p.position = math.max(min, p.position);
if (p.position == min)
p.velocity = math.max(0.0, p.velocity);
}
if (max != null) {
p.position = math.min(max, p.position);
if (p.position == max)
p.velocity = math.min(0.0, p.velocity);
}
}
}
class ParticleInBox extends System {
final Particle particle;
final Box box;
ParticleInBox({this.particle, this.box}) {
box.confine(particle);
}
void update(double deltaT) {
particle.update(deltaT);
box.confine(particle);
}
}
class ParticleInBoxWithFriction extends ParticleInBox {
final double friction;
final double _sign;
ParticleInBoxWithFriction({Particle particle, Box box, this.friction})
: super(particle: particle, box: box),
_sign = particle.velocity.sign;
void update(double deltaT) {
double force = -_sign * friction;
particle.applyImpulse(force * deltaT);
if (particle.velocity.sign != _sign)
particle.velocity = 0.0;
super.update(deltaT);
}
}
class Spring {
final double k;
double displacement;
Spring(this.k, {this.displacement: 0.0});
double get force => -k * displacement;
}
class ParticleAndSpringInBox extends System {
final Particle particle;
final Spring spring;
final Box box;
ParticleAndSpringInBox({this.particle, this.spring, this.box}) {
_applyInvariants();
}
void update(double deltaT) {
particle.applyImpulse(spring.force * deltaT);
particle.update(deltaT);
_applyInvariants();
}
void _applyInvariants() {
box.confine(particle);
spring.displacement = particle.position;
}
}
class ParticleClimbingRamp extends System {
final Particle particle;
final Box box;
final double slope;
ParticleClimbingRamp({
this.particle,
this.box,
this.slope,
double targetPosition}) {
double deltaPosition = targetPosition - particle.position;
particle.energy = -kGravity * slope * deltaPosition * particle.mass;
box.confine(particle);
}
void update(double deltaT) {
particle.update(deltaT);
// Note that we apply the impulse from gravity after updating the particle's
// position so that we overestimate the distance traveled by the particle.
// That ensures that we actually hit the edge of the box and don't wind up
// reversing course.
particle.applyImpulse(kGravity * slope * deltaT);
box.confine(particle);
}
}