-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathmovemodel.d
145 lines (115 loc) · 3.86 KB
/
movemodel.d
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/+ Моделирование движения материальной точки под действием сил
сборка и запуск
rdmd movemodel.d vector.d
+/
import vector : Vec2;
// состояние системы на момент времени
struct State
{
Vec2 pos, vel;
float time;
}
// модель расчитывает силу (ускорение при единичной массе) от состояния
alias Model = Vec2 delegate(ref const State);
// абстракция метода интегрирования, меняющая исходное состояние
alias Integrator = void delegate(ref State, Model, float dt);
// интегрирование по Эйлеру
void euler(ref State s, Model model, float dt)
{
auto acc = model(s);
with (s)
{
pos += vel * dt;
vel += acc * dt;
time += dt;
}
}
void main()
{
// локальные импорты иногда ускоряют сборку и не засоряют
// пространство имён, являются хорошим тоном
import std.stdio : writeln;
import std.algorithm : each;
import std.range : take;
import std.functional : toDelegate;
// начальное состояние системы
auto initial = State(Vec2(0, 10), Vec2(5, 0), 0);
// превратим заранее функцию в делегат для удобства
auto edg = toDelegate(&euler);
// симуляция вращения вокруг центра масс
auto sim1 = simulator(initial, toDelegate(¢erMass), edg, 10, 0.01);
// берём первые 30 значений симуляции и выводим по измерению на строку
sim1.take(30).each!writeln;
writeln();
// постоянное линейное ускорение
auto lineAcc = LineAcc(Vec2(0, -9.81));
auto sim2 = simulator(initial, &lineAcc.model, edg, 10, 0.01);
sim2.take(20).each!writeln;
writeln();
auto distLim = DistLim(Vec2(0, 10), 10, 100);
auto friction = Friction(0.2);
auto sim3model = modelSum(&lineAcc.model, // берём линейное ускорение
&distLim.model, // берём ограничение по длине от точки
&friction.model); // добавляем трение
auto sim3 = simulator(initial, sim3model, edg, 10, 0.1);
sim3.take(200).each!writeln;
}
Vec2 centerMass(ref const State s)
{
const center = Vec2(0,0);
const fk = 100;
auto d = center - s.pos;
return d * fk / d.len2;
}
struct LineAcc
{
Vec2 acc;
Vec2 model(ref const State s) { return acc; }
}
// ограничение на расстояние от точки (верёвка)
struct DistLim
{
Vec2 pos;
float dist, k;
Vec2 model(ref const State s)
{
import std.math : abs;
auto d = pos - s.pos;
if (dist < d.len)
return d/d.len * k * (d.len - dist);
else
return Vec2(0,0);
}
}
// трение воздуха
struct Friction
{
// Cx0 * rho * S / 2
float k = 0.5;
Vec2 model(ref const State s) { return -s.vel * s.vel.len * k; }
}
// расчёт суперпозиции сил
auto modelSum(Model[] funcs...)
{
return (ref const State s)
{
auto res = Vec2(0,0);
foreach (f; funcs)
res += f(s);
return res;
};
}
auto simulator(State initial, Model model, Integrator integ, float time, float step)
{
struct Result
{
State front; Model mdl; Integrator integ; float rest, dt;
bool empty() const @property { return rest < 0; }
void popFront()
{
integ(front, mdl, dt);
rest -= dt;
}
}
return Result(initial, model, integ, time, step);
}