Source file Integrate3d.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
type method_ = Explicit_euler | Semi_implicit_euler | Verlet | Rk4
type spin_law = Momentum | Spin_gyroscopic | Spin_naive
type turn = First_order | Exact
type force = Vec3.t -> Vec3.t -> Vec3.t
let methods = [ Explicit_euler; Semi_implicit_euler; Verlet; Rk4 ]
let name = function
| Explicit_euler -> "explicit Euler"
| Semi_implicit_euler -> "semi-implicit Euler"
| Verlet -> "Verlet"
| Rk4 -> "RK4"
let no_force _ _ = (0., 0., 0.)
let explicit_euler ~force ~dt (b : Body3d.t) =
let a = force b.pos b.vel in
{ b with pos = Vec3.add b.pos (Vec3.scale dt b.vel); vel = Vec3.add b.vel (Vec3.scale dt a) }
let semi_implicit_euler ~force ~dt (b : Body3d.t) =
let a = force b.pos b.vel in
let vel = Vec3.add b.vel (Vec3.scale dt a) in
{ b with vel; pos = Vec3.add b.pos (Vec3.scale dt vel) }
let verlet ~force ~dt (b : Body3d.t) =
let a = force b.pos b.vel in
let pos = Vec3.add (Vec3.add b.pos (Vec3.scale dt b.vel)) (Vec3.scale (0.5 *. dt *. dt) a) in
let predicted = Vec3.add b.vel (Vec3.scale dt a) in
let a' = force pos predicted in
{ b with pos; vel = Vec3.add b.vel (Vec3.scale (0.5 *. dt) (Vec3.add a a')) }
let rk4 ~force ~dt (b : Body3d.t) =
let deriv (pos, vel) = (vel, force pos vel) in
let advance (p, v) s (dp, dv) = (Vec3.add p (Vec3.scale s dp), Vec3.add v (Vec3.scale s dv)) in
let y = (b.pos, b.vel) in
let k1 = deriv y in
let k2 = deriv (advance y (dt /. 2.) k1) in
let k3 = deriv (advance y (dt /. 2.) k2) in
let k4 = deriv (advance y dt k3) in
let sixth x1 x2 x3 x4 = Vec3.scale (1. /. 6.) (Vec3.add (Vec3.add x1 (Vec3.scale 2. x2)) (Vec3.add (Vec3.scale 2. x3) x4)) in
let dpos = sixth (fst k1) (fst k2) (fst k3) (fst k4) in
let dvel = sixth (snd k1) (snd k2) (snd k3) (snd k4) in
{ b with pos = Vec3.add b.pos (Vec3.scale dt dpos); vel = Vec3.add b.vel (Vec3.scale dt dvel) }
let linear = function
| Explicit_euler -> explicit_euler
| Semi_implicit_euler -> semi_implicit_euler
| Verlet -> verlet
| Rk4 -> rk4
let turn_by ~turn ~spin ~dt (q : Quat.t) =
match turn with First_order -> Quat.integrate ~spin ~dt q | Exact -> Quat.turned_by ~spin ~dt q
let spin_step ?(law = Momentum) ?(turn = Exact) ~torque ~dt (b : Body3d.t) =
if b.Body3d.inv_inertia = Mat3.zero then
{ b with orientation = turn_by ~turn ~spin:b.Body3d.spin ~dt b.Body3d.orientation }
else
match law with
| Momentum ->
let l = Vec3.add (Mat3.mul_vec (Body3d.inertia_world b) b.Body3d.spin) (Vec3.scale dt torque) in
let b = { b with Body3d.orientation = turn_by ~turn ~spin:b.Body3d.spin ~dt b.Body3d.orientation } in
{ b with Body3d.spin = Mat3.mul_vec (Body3d.inv_inertia_world b) l }
| Spin_gyroscopic | Spin_naive ->
let i_world = Body3d.inertia_world b in
let gyro = if law = Spin_gyroscopic then Vec3.cross b.Body3d.spin (Mat3.mul_vec i_world b.Body3d.spin) else (0., 0., 0.) in
let alpha = Mat3.mul_vec (Body3d.inv_inertia_world b) (Vec3.sub torque gyro) in
let spin = Vec3.add b.Body3d.spin (Vec3.scale dt alpha) in
{ b with Body3d.spin; orientation = turn_by ~turn ~spin ~dt b.Body3d.orientation }
let step m ?law ?turn ?(torque = (0., 0., 0.)) ~force ~dt b =
spin_step ?law ?turn ~torque ~dt (linear m ~force ~dt b)