КП: Многочастичный симулятор
Курсовой проект по Теоретической механике
Исполнитель: Старобинский Егор
Группа: 09 (23604)
Семестр: весна 2015
Содержание
Аннотация проекта
Формулировка задачи
Цель работы
Создание интернет-сайта, позволяющего пользователю моделировать многоточечную систему онлайн.
Решаемые задачи
- решение уравнения движения;
- визуализация.
Общие сведения по теме
Уравнение движения
Пусть мы наблюдаем тело в момент времени
.Хотим знать, где окажется тело через малое изменение времени -
.Рассмотрим базовый метод интегрирования Верле:
, где
- позиция точки,
- равнодействующая всех сил, действующих на тело,
- масса тела,
- текущий момент времени,
- малое изменение времени.
Метод Верле позволяет вычислять траекторию по упрощённой схеме: зная предыдущее и текущее положения (
и соответственно) и мгновенное значение равнодействующей приложенных сил в текущем положении .Достоинства метода: самокоррекция и бóльшая точность по сравнению с численным методом Эйлера.
Язык реализации: JavaScript.
Визуализация
Язык рализации: pure SCSS.
Обработка событий: JavaScript.
Отказ от WebGL продиктован выбором методов оптимизации для возможности работы с тысячами частиц.
Решение
Результат
Вы можете попробовать возможности симулятора прямо здесь, либо перейдя на полноразмерную страницу решения.
В пункте 4.7 представлен пример готовой команды для создания трёхатомной молекулы.
Элементы системы
- Частицы;
- Стержни и пружины[1];
- Стенки;
- Поле сил;
- Рабочее окно;
- Сетки разметки;
- Консоль;
- Плеер.
Возможности консоли
- Конфигурация начальной системы тел;
- Изменение системы в процессе работы ("на лету");
- Запуск алгоритмов анализа системы;
- Распознавание и вывод ошибок в пользовательских запросах и в исходном коде;
- Распознавание и вывод предупреждений в пользовательских запросах и в исходном коде;
- Подключение/отключение сеток разметки, в том числе с пользовательскими размерами ячейки;
- Тетрис.
Команды консоли
Координаты пишутся в декартовой системе (х,у), единица измерения - пиксели, орт х направлен от левого края к правому, орт у от верхнего края к нижнему. Пример: (0,100) - координаты точки, лежащей на левом краю экрана на 100 пикселей ниже верхней границы.
Консоль выводит каждое сообщение как одну строку. Если доступной длины строки не хватает, сообщение обрезается. Для отображения полной версии сообщения необходимо кликнуть по нему мышью.
Основные запросы
Очистить поле консоли
- clrscr
Отобразить статистику элементов системы и чилто тиков:
- getInfo
Создать частицу(100,100) - текущие координаты
(0,10) - вектор скорости относительно начала координат
80 - радиус частицы в пикселях
5 - масса частицы в у. е.
Задать вектор скорости (относительно начала координат)
- setVelocity #0 (10,10), где
0 - id частицы[4]
Переместить частицу (относительно начала координат)3 - id частицы[4]
(100,100) - новые координаты
saveV - флаг сохранения скорости. Если указан, частица после перемещения сохранит вектор своей скорости.
Задать массу
- setProps #0 mass 10, где
0 - id частицы[4]
10 - новая масса
- setProps #0 invmass 0.1, где
0 - id частицы[4]
0.1 - обратное значение новой массы
Задать радиус
- setProps #0 radius 100, где
0 - id частицы[4]
100 - радиус частицы
Создать пружину между частицами0, 1 - id частиц[4]
50 - жёсткость пружины
Изменить жёсткость пружины
- changeSpring #5 10, где
5 - id пружины[4]
10 - новая жёсткость
Изменить жёсткость одинаковых пружин
- changeSprings 10->15, где
10 - текущая жёсткость
15 - новая жёсткость
Изменить жёсткость всех пружин
- changeSprings all->20, где
20 - новая жёсткость
Создать стержень между частицами
- addStick #2 #1[5], где
2, 1 - id частиц[4]
Отключение гравитации
- gravy disable
Задать вектор ускорения свободного падения (относительно начала координат)
- gravy (0,10)
Отобразить сетку
- showGrid type 0
- showGrid type 1
- showGrid type 2
Спрятать сетку
- hideGrid
Запустить симуляцию
- play
Остановить симуляцию
- stop
"Промотать" симуляцию
- step(100), где
100 - число пропускаемых отрисовкой тиков
Исполнить внутренний метод
- execute nameMethod(params), где
nameMethod - название метода
params - сообщаемые параметры
Посмотреть историю запросов
- getHistory [7]
Объединить команды в одном запросе
- commandOne---commandTwo, где
commandOne, commandTwo - команды консоли, могут также состоять из объединённых команд
Кнопки интерфейса
- Кнопка Get point's id
После нажатия на кнопку, а затем на частицу выводит id последней в консоль.
- Кнопка Move point[5]
Эквивалентна команде консоли movePoint без флага saveV.
После нажатия на кнопку, а затем на частицу закрепляет управление положением последним за курсором мыши.
Дальнейшие нажатия на свободные участки поля переносят эту частицу в точку нажатия, при этом её скорость считается нулевой.
Для прекращения управления следует вновь нажать кнопку.
- Клавиша клавиатуры ~ (также `, ё, Ё)
Эквивалентна командам консоли play/stop и крайней левой кнопке плеера.
- Кнопки плеера
Слева направо:
Воспроизведение/остановка симуляции (эквивалентно командам консоли play/step), переход на тик вперёд (эквивалентно команде консоли step(1))переход на 50 тиков вперёд (эквивалентно команде консоли step(50)), переход на 100 тиков вперёд (эквивалентно команде консоли step(100)).
Возможности плеера
- Воспроизведение/пауза симуляции с заданным ;
- Скачок вперёд на кратное время;
- "Замедление времени"[8].
Пример системы
Смоделируем молекулу из трёх атомов (подобную молекуле углекислого газа, рассматриваемой в курсовом проекте А. Смирнова).
Для создания такой системы необходимо последовательно выполнить следующие команды:
Создаём атомы кислорода, углерода и кислорода соответственно.
- addPoint (100,100) (0,0) 40 5
- addPoint (250,100) (0,0) 50 10
- addPoint (400,100) (0,0) 40 5
Соединяем атомы пружинами для моделирования связей. Первая пружина обеспечивает устойчивость молекулы, не создавая внутреннего напряжения.
- addSpring #0 #2 10
- addSpring #0 #1 20
- addSpring #1 #2 20
Дополнительно можем задать скорости у атомов кислорода для создания колебаний.
- setVelocity #0 (1,0)
- setVelocity #2 (-1,0)
Итоговый код (одна строка):
addPoint (100,100) (0,0) 40 5---addPoint (250,100) (0,0) 50 10---addPoint (400,100) (0,0) 40 5---addSpring #0 #2 10---addSpring #0 #1 20---addSpring #1 #2 20---setVelocity #0 (1,0)---setVelocity #2 (-1,0)
Выдержки кода решения
Файл "point.js"
1 function Point(mass, radius, coorXY, oldCoorXY, isExpToPot)
2 {
3 this.mass = mass;
4 this.invmass = 1 / mass;
5 this.radius = radius;
6 this.coor = coorXY;
7 this.oldCoor = oldCoorXY || coorXY;
8 this.id = getId();
9 this.type = 'point';
10 this.isExpToPot = isExpToPot || false;
11
12 // log
13 console.log('Create: '+this.toString());
14 }
15
16
17 Point.prototype.checkCoor = function(){
18 if (this.coor.isNaN())
19 {
20 console.error('Coor is NaN: ' + this);
21 player.stop();
22 }
23 else
24 {
25 if (this.oldCoor.isNaN())
26 {
27 console.warning('OldCoor is NaN: ' + this);
28 this.oldCoor = new Coor(this.coor);
29 }
30 else
31 {
32 return ;
33 }
34 }
35 }
36
37 Point.prototype.toString = function(type){
38 if (type === 'full')
39 {
40 return JSON.stringify(this, [
41 'type',
42 'id',
43 'coor',
44 'mass_',
45 'radius_',
46 'x',
47 'y',
48 'begin',
49 'end'
50 ], 4)
51 }
52 else
53 {
54 return JSON.stringify(this, [
55 'type',
56 'id',
57 'coor',
58 'x',
59 'y',
60 'begin',
61 'end'
62 ], 4)
63 }
64 }
65
66 Point.prototype.move = function(coorXY){
67 var delta = minusCoor(this.coor, this.oldCoor);
68 this.coor = coorXY;
69 this.oldCoor = minusCoor(coorXY, delta);
70 // log
71 console.log('Move: '+this.toString());
72 }
73
74 Point.prototype.moveEase = function(coorXY){
75 this.coor = coorXY;
76 this.oldCoor = coorXY;
77 // log
78 console.log('Move: '+this.toString());
79 }
Файл "player.js"
1 var step;
2
3 function Player(dt, phys, render, console)
4 {
5 this.tick = 0;
6 this.dt = dt;
7 this.tickCount = 0;
8 this.physics = new Physics(phys, this);
9 this.renderer = new Renderer(render, this);
10 this.parser = new Parser(this);
11 this.console = new Console(console, this);
12 var checker = checkerPl.bind(this);
13
14 this.stop = function(){
15 clearTimeout(this.tick);
16 this.tick = 0;
17
18 // log
19 this.console.logAns('Pause...');
20 }
21
22 this.play = function(){
23 if (!this.tick)
24 {
25 this.tick = setTimeout(function stepTm(pl){
26 step(pl, checker);
27 pl.tick = setTimeout(stepTm, pl.dt, pl);
28 }, this.dt, this);
29 // log
30 this.console.logAns('Play');
31 }
32 }
33
34 this.shifter = function(){
35 if (this.tick)
36 {
37 this.stop();
38
39 return 0;
40 }
41 else
42 {
43 this.play();
44
45 return 1;
46 }
47 }
48
49 this.nextStep = function(i){
50 if (!i)
51 {
52 step(this, checker);
53 // log
54 this.console.logAns('step');
55 }
56 else
57 {
58 this.console.logAns('step x ' + i);
59
60 while (i--)
61 {
62 step(this, checker);
63 }
64 }
65
66 }
67
68 this.getInfo = function(){
69 this.console.logAns('Tick count: '+this.tickCount);
70 this.console.logAns('Points count: '+this.physics.Points.length);
71 this.console.logAns('Walls count: '+this.physics.Walls.length);
72 this.console.logAns('Springs count: '+this.physics.Springs.length);
73 this.console.logAns('Sticks count: '+this.physics.Sticks.length);
74 }
75
76 this.find = function(id){
77 if (id in this.renderer.arr)
78 {
79 return this.renderer.arr[id].ptr;
80 }
81
82 return 0;
83 }
84
85 this.addPoint = function(mass, radius, coorXY, oldCoorXY, isExpToPot){
86 var tmp = this.physics.addPoint(mass, radius, coorXY, oldCoorXY, isExpToPot);
87 this.renderer.addPoint(tmp);
88 // log
89 this.console.logAns('Create point.');
90 }
91
92 this.addWall = function(vectM, vectO){
93 var tmp = this.physics.addWall(vectM, vectO);
94 this.renderer.addWall(tmp);
95 // log
96 this.console.logAns('Create wall.');
97 }
98
99 this.addStick = function(elem1, elem2){
100 var tmp = this.physics.addStick(elem1, elem2);
101 this.renderer.addStick(tmp);
102 // log
103 this.console.logAns('Create stick.');
104 }
105
106 this.addSpring = function(elem1, elem2, k){
107 var tmp = this.physics.addSpring(elem1, elem2, k);
108 this.renderer.addSpring(tmp);
109 // log
110 this.console.logAns('Create spring.');
111 }
112
113 this.parse = function(str){
114 return this.parser.parse(str);
115 }
116
117 this.log = function(str){
118 this.console.log(str);
119 }
120
121 this.warn = function(str){
122 this.console.warn(str);
123 }
124
125 this.error = function(str){
126 this.console.error(str);
127 }
128 }
129
130 step = function(pl, checker){
131 if (!checker())
132 {
133 pl.physics.step();
134 pl.renderer.draw();
135 pl.tickCount++;
136 }
137 else
138 {
139 pl.stop();
140 checker('delete');
141 }
142 }
143
144 function checkerPl(mode, vr, val)
145 {
146 var st = checkerPl.state;
147
148 if (arguments.length == 0) // check
149 {
150 for (var i in st)
151 {
152 if (player[st[i].vr] < st[i].val)
153 {
154 return 1;
155 }
156 }
157
158 return 0;
159 }
160
161 if (mode == 'delete') // delete
162 {
163 for (var i in st)
164 {
165 if (player[st[i].vr] < st[i].val)
166 {
167 delete st[i];
168 }
169 }
170
171 return 0;
172 }
173
174 if (mode == 'add') // add
175 {
176 st.push({'vr': vr, 'val': val});
177
178 return 0;
179 }
180 }
Файл "renderer.js"
1 function Renderer(param, pl)
2 {
3 this.scr = $('#'+param.idEl);
4 this.borderSize = param.borderSize;
5 this.arr = new Array();
6 this.player = pl;
7 }
8
9 Renderer.prototype.addPoint = function(point){
10 var el = $('<div data-id="'+point.id+'" class="point"></div>');
11 el.css('width', 2 * point.radius - 2 * this.borderSize);
12 el.css('height', 2 * point.radius - 2 * this.borderSize);
13 el.css('border-radius', 2 * point.radius - 2 * this.borderSize);
14 el.css('font-size', 10 * (point.radius - this.borderSize) + '%');
15 this.scr.append(el);
16
17 var tmp = {
18 'el': el,
19 'ptr': point,
20 'draw': function(){
21 var c = this.patch(this.ptr.coor);
22 this.el.css('top', c.y);
23 this.el.css('left', c.x);
24 },
25 'setCoor': function(c){
26 c = c || this.patch(this.ptr.coor);
27 this.el.css('top', c.y);
28 this.el.css('left', c.x);
29 },
30 'patch': function(c){
31 return new Coor(c.x - this.ptr.radius, c.y - this.ptr.radius);
32 },
33 'patchC': function(c){
34 return new Coor(c.x + this.ptr.radius, c.y + this.ptr.radius);
35 }
36 };
37 this.arr[point.id] = tmp;
38
39 tmp.setCoor();
40 }
41
42
43 Renderer.prototype.log = function(str){
44 this.player.log(str);
45 }
46
47
48 Renderer.prototype.warn = function(str){
49 this.player.warn(str);
50 }
51
52
53 Renderer.prototype.error = function(str){
54 this.player.error(str);
55 }
56
57
58 Renderer.prototype.addWall = function(wall){
59 var el = $('<div data-id="'+wall.id+'" class="wall"></div>');
60 el.css('left', wall.main_.midX());
61 el.css('top', wall.main_.midY())
62 this.scr.append(el);
63
64 var tmp = {
65 'el': el,
66 'ptr': wall
67 }
68
69 this.arr[wall.id] = tmp;
70 }
71
72 Renderer.prototype.addStick = function(stick){
73 var el = $('<div data-id="'+stick.id+'" class="stick"></div>');
74 this.scr.append(el);
75
76 var tmp = {
77 'el': el,
78 'ptr': stick,
79 'setCoor': function(b, e, l){
80 setCoorLink.call(this, b, e, l);
81 },
82 'draw': function(){
83 this.setCoor(this.ptr.coorB, this.ptr.coorE, this.ptr.length_);
84 }
85 };
86
87 this.arr[stick.id] = tmp;
88 }
89
90 Renderer.prototype.addSpring = function(spring){
91 var el = $('<div data-id="'+spring.id+'" class="spring"></div>');
92 this.scr.append(el);
93
94 var tmp = {
95 'el': el,
96 'ptr': spring,
97 'setCoor': function(b, e, l){
98 setCoorLink.call(this, b, e, l);
99 },
100 'draw': function(){
101 if (this.ptr.k == 0)
102 {
103 this.el.hide();
104 delete this.draw;
105 }
106 else
107 {
108 this.setCoor(this.ptr.coorB, this.ptr.coorE, length(this.ptr.coorB, this.ptr.coorE));
109 }
110 }
111 };
112
113 this.arr[spring.id] = tmp;
114 }
115
116 Renderer.prototype.draw = function(){
117 for (var key in this.arr)
118 {
119 var l = this.arr[key];
120
121 if (l.draw)
122 {
123 l.draw();
124 }
125
126 }
127 }
Файл "func.js"
1 function Coor(e, y)
2 {
3 if (e.pageX)
4 {
5 this.x = e.pageX;
6 this.y = e.pageY;
7
8 return ;
9 }
10 if (typeof e == 'number')
11 {
12 this.x = e;
13 this.y = y;
14
15 return ;
16 }
17 else
18 {
19 this.x = e.x;
20 this.y = e.y;
21
22 return ;
23 }
24 }
25
26 function createHTMLTable(id, numberOfCellsPerRow, numberOfRows, cellWidth, cellHeight)
27 {
28 var generatedHTMLTable = '<style> \
29 #table' + id + '.show{} \
30 #table' + id + '.hide{display:none;} \
31 #table' + id + '{display:table;position:fixed;width: 2000px;height:1000px;top:27px;border-collapse:collapse;left:0px;z-index:1;outline:2px solid gray;} \
32 #table' + id + ' > div{display:table-row;} \
33 #table' + id + ' > div > div{display:table-cell;width:' + cellWidth + 'px;height:' + cellHeight + 'px;outline:1px solid white;} \
34 </style>';
35 for(var ii = 0; ii < numberOfRows; ++ii)
36 {
37 generatedHTMLTable += "<div>";
38
39 for(var i = 0; i < numberOfCellsPerRow; ++i)
40 {
41 generatedHTMLTable += "<div></div>";
42 }
43
44 generatedHTMLTable += "</div>";
45 }
46
47 var tableDiv = $('<div id="table'+id+'" class="show" data-type="grid">'+generatedHTMLTable+'</div>')
48
49 $('body').append(tableDiv);
50 }
51
52 function colorHTMLTableCell(id, numberOfRows, numberOfCell)
53 {
54 var fix = 1;
55 var rowBefore = Math.floor((fix + numberOfCell) / numberOfRows);
56 var styleToColorDiv = $('<style>#table' + id + ' > div:nth-child(' + (rowBefore + 1 + fix) + ') > div:nth-child(' + (0 + Math.floor(fix + numberOfCell - rowBefore * numberOfRows)) +'){outline:3px dashed dodgerblue;}</style>');
57 $('#table' + id).append(styleToColorDiv);
58 }
59
60
61 Coor.prototype.toString = function(type){
62 return JSON.stringify(this);
63 }
64
65 Coor.prototype.plus = function(B){
66 this.x += B.x;
67 this.y += B.y;
68
69 return this;
70 }
71
72 Coor.prototype.isNaN = function(){
73 if (isNaN(this.x) || isNaN(this.y))
74 return 1;
75 return 0;
76 }
77
78 function dot(A, B)
79 {
80 return (A.x * B.x + A.y * B.y);
81 }
82
83 function length(A, B)
84 {
85 return Math.sqrt((A.x - B.x)*(A.x - B.x) + (A.y - B.y)*(A.y - B.y));
86 }
87
88 function sign(x)
89 {
90 return (x > 0 ? 1 : x < 0 ? -1 : 0);
91 }
92
93 function Vector(A, B)
94 {
95 this.begin = A;
96 this.end = B;
97 this.length = length(A, B);
98 }
99
100 Vector.prototype.midX = function(){
101 return (this.begin.x + this.end.x)/2;
102 }
103
104
105 Vector.prototype.midY = function(){
106 return (this.begin.y + this.end.y)/2;
107 }
108
109 Vector.prototype.toString = function(type){
110 if (type === 'full')
111 {
112 return JSON.stringify(this, null, 4)
113 }
114 else
115 {
116 return JSON.stringify(this, [
117 'begin',
118 'end',
119 'length',
120 'x',
121 'y',
122 ], 4)
123 }
124 }
125
126 Vector.prototype.resize = function(){
127 this.length = length(A, B);
128 }
129
130 Vector.prototype.plus = function(B){
131 this.begin.plus(B.begin);
132 this.end.plus(B.end);
133 this.resize();
134
135 return this;
136 }
137
138 function plusVector(A, B)
139 {
140 return new Vector(plusCoor(A.begin, B.begin), plusCoor(A.end, B.end));
141 }
142
143 function plusCoor(A, B)
144 {
145 return new Coor(A.x + B.x, A.y + B.y);
146 }
147
148 function minusCoor(A, B)
149 {
150 return new Coor(A.x - B.x, A.y - B.y);
151 }
152
153 function timesCoor(e, k)
154 {
155 return new Coor(k * e.x, k * e.y);
156 }
157 function deltaCoor(e)
158 {
159 return Math.sqrt(dot(e,e));
160 }
161
162 function setCoorLink(a, b, length)
163 {
164
165 this.el.css('top', a.y);
166 this.el.css('left', a.x);
167 this.el.css('height', length);
168 var angle = 0;
169 var deltaX = b.x - a.x;
170 var deltaY = b.y - a.y;
171
172 if (deltaX == 0)
173 angle = ((deltaY > 0) - (deltaY < 0)) * Math.PI / 2;
174 else
175 if(deltaX < 0)
176 angle = ((deltaY >= 0) - (deltaY < 0)) * Math.PI + Math.atan(deltaY / deltaX);
177 else angle = Math.atan(deltaY / deltaX);
178
179 angle -= Math.PI/2;
180 this.el.css('transform', 'rotate(' + angle + 'rad)');
181 }
Файл "console.js"
1 function Console(par, pl)
2 {
3 this.player = pl;
4 this.btn = $(par.btn);
5 this.input = $(par.inp);
6 this.txt = $(par.log);
7 this.history = new Array();
8 this.oHistory = new Array();
9 var exec = this.execute.bind(this);
10 var keydown = keydownCons.bind(this);
11
12 this.btn.click(function(){
13 exec();
14 });
15
16 this.input.keydown(keydown);
17 }
18
19 function keydownCons(e)
20 {
21 var e = e || window.event;
22 var key = e.which;
23
24 if (key == 38)
25 {
26 this.input.val(this.getHist());
27 }
28
29 if (key == 40)
30 {
31 this.input.val(this.getHistInv());
32 }
33
34 if (e.ctrlKey && key == 13)
35 {
36 this.input.val(this.input.val() + '---');
37
38 return ;
39 }
40
41 if (key == 13)
42 {
43 this.btn.trigger('click');
44 }
45 }
46
47 Console.prototype.log = function(str){
48 var el = $('<div></div>').addClass('log').html(str.toString().trim()).click(showAllLog);
49
50 this.txt.prepend(el);
51 };
52
53 function showAllLog()
54 {
55 $(this).addClass('all');
56 }
57
58 Console.prototype.warn = function(str){
59 var el = $('<div></div>').addClass('warn').html(str.toString().trim()).click(showAllLog);
60
61 this.txt.prepend(el);
62 };
63
64 Console.prototype.error = function(str){
65 var el = $('<div></div>').addClass('err').html(str.toString().trim()).click(showAllLog);
66
67 this.txt.prepend(el);
68 };
69
70 Console.prototype.logAns = function(str){
71 var el = $('<div></div>').addClass('log').html('> ' + str.toString().trim()).click(showAllLog);
72
73 this.txt.prepend(el);
74 };
75
76 Console.prototype.warnAns = function(str){
77 var el = $('<div></div>').addClass('warn').html('> ' + str.toString().trim()).click(showAllLog);
78
79 this.txt.prepend(el);
80 };
81
82 Console.prototype.errorAns = function(str){
83 var el = $('<div></div>').addClass('err').html('> ' + str.toString().trim()).click(showAllLog);
84
85 this.txt.prepend(el);
86 };
87
88 Console.prototype.saveHist = function(txt){
89 while(this.oHistory.length)
90 {
91 this.history.push(this.oHistory.shift());
92 }
93
94 this.history.push(txt);
95 };
96
97 Console.prototype.getHist = function(){
98 var tmp = this.history.pop();
99 if (!tmp || tmp.length < 3)
100 {
101 return '';
102 }
103 this.oHistory.unshift(tmp);
104
105 return tmp;
106 };
107
108 Console.prototype.getHistInv = function(){
109 var tmp = this.oHistory.shift();
110 if (!tmp || tmp.length < 3)
111 {
112 return '';
113 }
114 this.history.push(tmp);
115
116 return tmp;
117 };
118
119 Console.prototype.execute = function(){
120 var txt = this.input.val().trim();
121
122 if (txt === '')
123 {
124 return ;
125 }
126
127
128 if (txt === 'getHistory')
129 {
130 this.input.val('');
131
132 var string = '';
133
134 if(this.history.length > 0){
135 var historyLength = this.history.length;
136
137 for(var i = 0; i < historyLength; ++i){
138 console.log(this.history[i] + '---');
139 if(this.history[i] != undefined)
140 string += this.history[i] + '---';
141 }
142 string = string.slice(0,-3);
143 }
144
145 if(this.history.length == 0)
146 string = 'History is empty';
147
148 alert(string);
149
150 return 0;
151 }
152
153 this.log(txt);
154
155 if(!this.player.parse(txt))
156 {
157 this.input.val('');
158
159 this.saveHist(txt);
160 }
161 else
162 {
163 this.warnAns('Can not disassemble');
164 }
165 };
Файл "phen.css"
1 *{margin:0;padding:0;}
2 body{background: gainsboro;}
3 .point{
4 position: absolute;
5 z-index: 20;
6 counter-increment: num;
7 text-align: center;
8 border: 4px solid white;
9 background: dodgerblue;
10 }
11 .point:before{
12 content: counter(num);
13 color: white;
14 -webkit-user-select: none;
15 -khtml-user-select: none;
16 -moz-user-select: none;
17 -ms-user-select: none;
18 -o-user-select: none;
19 user-select: none;
20 }
21 #screen{
22 position: fixed;
23 z-index: 2;
24 top:27px;
25 counter-reset: num;
26 display: block;
27 overflow-x: hidden;
28 width: 100%;
29 height: auto;
30 bottom:0;
31 background: transparent;
32 }
33 /*#screen:after{
34 position:absolute;
35 content: '';
36 display: block;
37 height:596px;
38 width:1196px;
39 border: 2px solid crimson;
40 }*/
41 #btns input
42 {
43 font-size: 18px;
44 position: fixed;
45 z-index: 2;
46 overflow: hidden;
47 width: 25%;
48 white-space: nowrap;
49 text-overflow: ellipsis;
50 color: white;
51 border: 3px solid white;
52 border-right: none;
53 border-radius: none!important;
54 outline: none!important;
55 background: dodgerblue;
56 }
57 #btns input.hide{display:none;}
58 #btns input:nth-child(2){left:25%;}
59 #btns input:nth-child(3){left:50%;}
60 #btns input:nth-child(4){left:50%;}
61 #btns input:last-child
62 {
63 left: 75%;
64 border: 3px solid white;
65 }
66 #btns input.notActive{}
67 #btns input:active,
68 #btns input.active
69 {
70 background: crimson!important;
71 }
72
73 #btns input[disabled],
74 #btns input[disabled]:active
75 {
76 background: gray!important;
77 }
78 .wall{
79 left: 252.5px;
80 top: 227.5px;
81 height:0px;
82 width:500px;
83 z-index:1000;
84 margin-top:-2px;
85 position: absolute;
86 /* background: url('./wall.png') repeat-x;*/
87 -webkit-transform: rotate(180deg);
88 -khtml-transform: rotate(180deg);
89 -moz-transform: rotate(180deg);
90 -ms-transform: rotate(180deg);
91 -o-transform: rotate(180deg);
92 transform: rotate(180deg);
93 /*border-top: 2px solid crimson;*/
94 }
95 .stick,.spring{
96 background: white;
97 width: 2px;
98 position:absolute;
99 -webkit-transform-origin: 50% 0;
100 -khtml-transform-origin: 50% 0;
101 -moz-transform-origin: 50% 0;
102 -ms-transform-origin: 50% 0;
103 -o-transform-origin: 50% 0;
104 transform-origin: 50% 0;
105 }
106 .spring{
107 background: url('./spring.png') no-repeat;
108 background-size: 10px 100%;
109 width:10px;margin-left:-5px;
110 }
111 input[disabled],
112 input[disabled]:active{
113 background: gray!important;
114 }
115 #stack {
116 width: 130px;
117 height: 260px;
118 border: solid 1px black;
119 border-top: 0px;
120 }
121
122 #stack .brick {
123 width: 11px;
124 height: 11px;
125 border: solid 1px white;
126 background: white;
127 float: left;
128 }
129 #stack .brick.on {
130 background: black;
131 }
132 #stack .brick.now {
133 background: green;
134 }
135
136 #controls{
137 position: fixed;
138 z-index: 22;
139 bottom: 5px;
140 left: 5px;
141 width: 165px;
142 height: 30px;
143 padding: 15px;
144 -webkit-transition: all 400ms;
145 -khtml-transition: all 400ms;
146 -moz-transition: all 400ms;
147 -o-transition: all 400ms;
148 transition: all 400ms;
149 opacity: .5;
150 border: 3px solid #eee;
151 border-radius: 20px;
152 background-color: dodgerblue;
153 }
154 #controls.show{opacity: 1;}
155 #controls:before{
156 position: absolute;
157 top: -2px;
158 left: -2px;
159 display: block;
160 width: 334px;
161 height: 65px;
162 content: '';
163 }
164 #playerBtns{
165 position: relative;
166 left: 30px;
167 }
168 #playerBtns span{
169 position: absolute;
170 top: 4px;
171 }
172 #controls[type='0'] #playerBtns span:nth-of-type(1){
173 top: 0;
174 width: 0;
175 height: 0;
176 border-width: 15px 0 15px 30px;
177 border-style: solid;
178 border-color: transparent transparent transparent white;
179 }
180 #controls[type='1'] #playerBtns span:nth-of-type(1){
181 top: 0;
182 right: 169px!important;
183 width: 11px;
184 height: 30px;
185 border-right: 8px solid crimson;
186 border-left: 8px solid crimson;
187 }
188 #playerBtns span:nth-of-type(2){
189 width: 0;
190 height: 0;
191 border-width: 10px 0 10px 20px;
192 border-style: solid;
193 border-color: transparent transparent transparent white;
194 }
195 #playerBtns span:nth-of-type(3){
196 width: 0;
197 height: 0;
198 border-width: 10px 0 10px 20px;
199 border-style: solid;
200 border-color: transparent transparent transparent white;
201 }
202 #playerBtns span:nth-of-type(3):after{
203 position: absolute;
204 top: -10px;
205 width: 0;
206 height: 0;
207 content: '';
208 border-width: 10px 0 10px 20px;
209 border-style: solid;
210 border-color: transparent transparent transparent white;
211 }
212 #playerBtns span:nth-of-type(4){
213 width: 0;
214 height: 0;
215 border-width: 10px 0 10px 20px;
216 border-style: solid;
217 border-color: transparent transparent transparent white;
218 }
219 #playerBtns span:nth-of-type(4):after{
220 position: absolute;
221 top: -10px;
222 width: 0;
223 height: 0;
224 content: '';
225 border-width: 10px 2px;
226 border-style: solid;
227 border-color: white;
228 }
229 #playerBtns span:nth-of-type(1){right: 165px;}
230 #playerBtns span:nth-of-type(2){right: 130px;}
231 #playerBtns span:nth-of-type(3){right: 90px;}
232 #playerBtns span:nth-of-type(4){right: 35px;}
233 #consoleWindow{
234 position: fixed;
235 top: 30px;
236 z-index: 2;
237 right: 0;
238 width: 25%;
239 font-weight: bold;
240 font-size: 14px;
241 overflow: hidden;
242 max-height: 339px;
243 background: white;
244 }
245 #consoleWindow.hide{height:0;}
246 #consoleWindow.show{height: auto;}
247 #consoleInput{border-bottom: 3px solid gainsboro;}
248 #consoleInput:before{
249 content: '>';
250 font-size: 14px;
251 position: absolute;
252 top: 4px;
253 left: 4px;
254 }
255 #consoleInput [type="text"]{
256 border: none!important;
257 outline: none!important;
258 height: 20px;
259 font-size: 14px;
260 width: 100%;
261 box-sizing:border-box;
262 padding-left: 15px;
263 }
264 #consoleInput [type="button"]{
265 font-size: 18px;
266 overflow: hidden;
267 width: 100%;
268 right: 0;
269 white-space: nowrap;
270 text-overflow: ellipsis;
271 color: white;
272 border: 3px solid white;
273 border-radius: none!important;
274 outline: none!important;
275 background: dodgerblue;
276 }
277 #consoleInput [type="button"]:active{background: crimson!important;}
278 #consoleLog{
279 width: 100%;
280 max-height: 290px;
281 box-sizing: border-box;
282 height: auto;
283 word-wrap: break-word;
284 overflow-y: auto;
285 overflow-x: hidden;
286 background: white;
287 padding-left: 3px;
288 padding-bottom: 10px;
289 }
290
291 #consoleLog div:not(.all){
292 text-overflow: ellipsis;
293 overflow: hidden;
294 max-height: 37px;
295 width: 100%;
296 white-space: nowrap;
297 }
298
299 #consoleLog .warn{color: #DAA520;}
300 #consoleLog .err{color: crimson;}
301 #consoleLog .log{color: grey;}
Файл "index.php"
1 <!DOCTYPE html>
2 <html lang="ru">
3 <head>
4 <title>Phen v2.0 a</title>
5 <link rel="stylesheet" href="./phen.css">
6 <script type="text/javascript" src="./jquery-2.1.3.min.js"></script>
7 <script type="text/javascript" src="./jquery-css-transform.js"></script>
8 <script type="text/javascript" src="./point.js"></script>
9 <script type="text/javascript" src="./spring.js"></script>
10 <script type="text/javascript" src="./stick.js"></script>
11 <script type="text/javascript" src="./func.js"></script>
12 <script type="text/javascript" src="./renderer.js"></script>
13 <script type="text/javascript" src="./wall.js"></script>
14 <script type="text/javascript" src="./phen.js"></script>
15 <script type="text/javascript" src="./parser.js"></script>
16 <script type="text/javascript" src="./console.js"></script>
17 <script type="text/javascript" src="./player.js"></script>
18 <script type="text/javascript" src="./cycle.js"></script>
19 <script type="text/javascript" src="./clean.js"></script>
20 </head>
21 <body>
22 <div id="screen"></div>
23 <div id="btns">
24 <input type="button" value="Open/Save" disabled="disabled">
25 <input id="getPoint" type="button" value="Get point's id">
26 <input id="movePoint" type="button" value="Move point">
27 <input id="demovePoint" type="button" class="active hide" value="Move point">
28 <input id="consoleOpen" class="active" type="button" value="Console">
29 </div>
30 <div id="consoleWindow" class="show">
31 <div id="consoleInput">
32 <input id="console" type="text" autofocus="autofocus">
33 <input id="consoleBtn" type="button" value="Enter">
34 </div>
35 <div id="consoleLog">
36 </div>
37 </div>
38 <div id="controls" class="show" type="0">
39 <div id="playerBtns">
40 <span id="stop"></span>
41 <span id="stepMin"></span>
42 <span id="stepMid"></span>
43 <span id="stepMax"></span>
44 </div>
45 </div>
46 </body>
47 </html>
Обсуждение результатов и выводы
Скачать отчет:
Скачать презентацию:
Ссылки по теме
T, Jakobsen. "Advanced Character Physics", 2003. (перевод статьи )
Л. Ландау, Е. Лифшиц, "Теоретическая физика", том первый, "Механика", 2001.
А, Смирнов. "Курсовой проект: молекула углекислого газа", 2015. (страница проекта)
См. также
Примечания
- ↑ Стержни рассчитываются на расстяжение/сжатие методом коррекции координат.
Действие пружин учитывается как действие сил упругости. - ↑ 2,0 2,1 2,2 Без выделения жирным написаны необязательные параметры. При желании указать необязательный параметр все значения слева от него следует считать обязательными (во избежание путаницы при парсинге безразмерных величин в команде).
- ↑ Значения по умолчанию: вектор скорости нулевой, радиус равен 50 пикселям, масса равна 5 у. е.
- ↑ 4,0 4,1 4,2 4,3 4,4 4,5 4,6 4,7 Идентификационный номер элемента в системе. Генерируется последовательно, начиная с единицы, для стенок, стержней, пружин и частиц. Для частиц значение id можно найти нажатием сначала на кнопку "Get point's id", а затем на частицу: тогда Id отобразится в консоли.
- ↑ 5,0 5,1 5,2 5,3 Важно! Визуально действие этой команды применится только при перерисовке кадра.
- ↑ Жёсткость пружины по умолчанию равна 5 у. е.
- ↑ Запросы выводятся в всплывающем окне одной цельной командой, для воспроизведения цепочки запросов достаточно ввести эту команду в консоль.
Важно! Учитываются только запросы, успешно введённые в консоль. Воздействия на систему при помощи кнопки "Move point" будут потеряны. - ↑ При малой производительности клиента уменьшаем число отрисовок в единицу времени для сохранения гладкости анимации. Управлятся через консоль.