这很容易解释:您正在使用欧拉正向作为求解ODE的求解器,在机械系统中,欧拉正向的系统误差会增加能量。欧拉反向会减少能量,因此交替使用显式和隐式欧拉方法的组合将使能量略微更加恒定。
但是,您可以使用同样甚至更少的工作量实现保持能量和其他物理不变量的二阶辛普森方法(隐式中点方法或Verlet-(Stoermer-Cromer-...-Newton-)方法)。
甚至还有更高阶的龙格-库塔方法,尽管不是辛普森方法,但也会以更高阶保持能量。有关Stoermer-Verlet-...-Newton方法的Hairer文章,请参见后印刷品或预印刷版以及使用C ++的“Moving stars around”教程文本或Ruby。
关于物理学的说明:总体而言,实现非常简单且易于阅读。但是,引力作用是
g*m1*m2*(p2-p1)/norm(p2-p1)^3
作为负梯度
g*m1*m2/norm(p2-p1)
你只是在使用范数的平方,其中力应为重力势能的负梯度。
g*m1*m2*ln(norm(p2-p1))
这适用于平面物理,但不适用于三维空间的二维截面。
可行代码
使用速度Verlet和能量守恒:
向圆对象添加一个新字段a=Vector(),并将update()函数中的一大堆内容替换为以下专用函数集合。
function compute_forces() {
for (var i = 0; i < particles.length; i++) {
var p = particles[i];
p.a.set(0);
for (var j = 0; j < i; j++) {
var p2 = particles[j];
var d = p.c.sub(p2.c);
var norm = Math.sqrt(100.0 + d.lengthSq());
var mag = gravity / (norm * norm * norm);
p.a.set(p.a.sub(d.mul(mag * p2.m)));
p2.a.set(p2.a.add(d.mul(mag * p.m)));
}
}
}
function do_collisions() {
for (var i = 0; i < particles.length; i++) {
var p = particles[i];
for (var j = 0; j < i; j++) {
var p2 = particles[j];
if (checkCCCol(p, p2)) {
resCCCol(p, p2);
}
}
}
}
function do_physics(dt) {
for (var i1 = 0; i1 < particles.length; i1++) {
var p1 = particles[i1];
p1.c.set(p1.c.add(p1.v.mul(0.5 * dt)));
}
compute_forces();
for (var i2 = 0; i2 < particles.length; i2++) {
var p2 = particles[i2];
p2.v.set(p2.v.add(p2.a.mul(dt)));
}
for (var i3 = 0; i3 < particles.length; i3++) {
var p3 = particles[i3];
p3.c.set(p3.c.add(p3.v.mul(0.5 * dt)));
}
do_collisions();
}
function update() {
for (var k = 0; k < 4; k++) {
do_physics(1.0 / 4);
}
render();
RAF(update);
}
请查看http://jsfiddle.net/4XVPH/
基于粒子的质量着色的修改示例(希望更好地显示它们的相互作用),修复了一个错误,并添加了一些附加注释:http://jsfiddle.net/24mg6ctg/12/