Редактирование: КП: Многочастичный симулятор

Перейти к: навигация, поиск

Внимание! Вы не авторизовались на сайте. Ваш IP-адрес будет публично видимым, если вы будете вносить любые правки. Если вы войдёте или создадите учётную запись, правки вместо этого будут связаны с вашим именем пользователя, а также у вас появятся другие преимущества.

Правка может быть отменена. Пожалуйста, просмотрите сравнение версий, чтобы убедиться, что это именно те изменения, которые вас интересуют, и нажмите «Записать страницу», чтобы изменения вступили в силу.
Текущая версия Ваш текст
Строка 1: Строка 1:
 
[[А.М. Кривцов]] > [[Теоретическая механика: физико-механический факультет|Теоретическая механика]] > [[Курсовые проекты ТМ 2015]] > '''Многочастичный симулятор''' <HR>
 
[[А.М. Кривцов]] > [[Теоретическая механика: физико-механический факультет|Теоретическая механика]] > [[Курсовые проекты ТМ 2015]] > '''Многочастичный симулятор''' <HR>
  
[[File:iPhone_image_2015-4-13-1431495674163_1.jpg|800px|right]]
+
[[File:iPhone_image_2015-4-13-1431495674163_1.jpg|thumb|Ткань из пружин]]
 
'''''Курсовой проект по [[Теоретическая механика: физико-механический факультет|Теоретической механике]]'''''
 
'''''Курсовой проект по [[Теоретическая механика: физико-механический факультет|Теоретической механике]]'''''
  
 
'''Исполнитель:''' [[Старобинский Егор]]
 
'''Исполнитель:''' [[Старобинский Егор]]
  
'''Группа:''' [[Группа 09|09]]
+
'''Группа:''' [[Группа 09|09]] (23604)
  
 
'''Семестр:''' весна 2015
 
'''Семестр:''' весна 2015
  
 
== Аннотация проекта ==
 
== Аннотация проекта ==
Рассматриваемой задачей этого проекта является определение поведения задаваемой механической двухмерной системы<ref>Строго говоря, мы рассматриваем проекцию трёхмерной системы на двухмерное пространство. Так, на изображении в заголовке страницы демонстрируется следующий эксперимент: есть тело в форме параллелепипеда (набор частиц со связями: пружинами), на тело сверху падает стержень. Проекцией этого тела является его слой: треугольная решётка, закреплённая на концах. Проекция стержня - частица (на изображении отсутствует).</ref> из частиц, сил и ограничений. Для этого был создан интернет-сайт с программой, позволяющей найти решение уравнения движения системы (физический движок). Также на базе этой программы был реализован пользовательский интерфейс с возможностями создания и редактирования исходной системы и визуализацией её поведения.
 
  
В процессе решения широко применялись знания из классической механики и программирования, благодаря чему была реализована возможность моделирования тел из большого числа частиц и ограничений (интерес с точки зрения механики) и консоль управления с упрощёнными интерпретируемыми командами (интерес с точки зрения программирования). Был внедрён ряд математических методов как для вычисления решения уравнения движения, так и для анализа разрешённой системы. Были проведены проверки получившегося движка на предварительно решённых задачах.
 
 
 
''' Научная новизна '''
 
 
Созданный движок позволяет человеку без специальных знаний в области программирования проводить моделирование собственных систем. Не требуется установки никакого дополнительного софта, программа запускается при помощи браузера как на компьютерах, так и на телефонах, планшетах, телевизорах класса Smart.
 
 
Визуализация не применяет технологию WebGL, благодаря чему многократно увеличивается диапазон устройств, способных запустить сайт с полноценной функциональностью.
 
 
Применён метод нахождения периода движения частицы по участку траектории путём последовательного разбиения поля на сетки с возрастающей плотностью ячеек (идея позаимствована из теории слов).
 
 
 
 
Движок представляет все тела как набор частиц со связями и применяет базовый двухшаговый метод численного интегрирования Верле и другие математические методы для разрешения уравнения движения в короткое время.
 
 
По возможности упрощено интегрирование в систему новых алгоритмов анализа (требует знаний в программировании на javascript как для реализации алгоритма, так и для его интегрирования), при этом все пользователи оперируют только актуальной версией программы.
 
 
 
Объединение физического движка и пользовательского интерфейса получило название "Многочастичный симулятор", имеет открытый исходный код и выполнено без использования готовых решений по теме проекта.
 
  
 
== Формулировка задачи ==
 
== Формулировка задачи ==
 
 
===== Цель работы =====
 
===== Цель работы =====
 
Создание интернет-сайта, позволяющего пользователю моделировать многоточечную систему онлайн.
 
Создание интернет-сайта, позволяющего пользователю моделировать многоточечную систему онлайн.
Строка 49: Строка 28:
 
Хотим знать, где окажется тело через малое изменение времени - <math>\Delta t</math>.
 
Хотим знать, где окажется тело через малое изменение времени - <math>\Delta t</math>.
  
Рассмотрим базовый метод интегрирования Верле:
+
Рассмотрим [[ Интегрирование Верле | базовый метод Верле ]]:
  
<big><math>\vec{x}(t + \Delta t) = 2\vec{x}(t) - \vec{x}(t - \Delta t) +\frac{ \vec{R}(t) \Delta t^2 }m</math></big>, где
+
<big><math>\vec{x}(t + \Delta t) = 2\vec{x}(t) - \vec{x}(t - \Delta t) + \vec{R}(t) \Delta t^2 / m</math></big>, где
  
 
<math>\vec{x}</math> - позиция точки,
 
<math>\vec{x}</math> - позиция точки,
Строка 68: Строка 47:
  
 
Язык реализации: JavaScript.
 
Язык реализации: JavaScript.
 
  
 
===== Визуализация =====
 
===== Визуализация =====
Строка 74: Строка 52:
  
 
Обработка событий: JavaScript.
 
Обработка событий: JavaScript.
 
Манипуляции с DOM: jQuery (безболезненно заменяется на Zepto).
 
  
 
Отказ от WebGL продиктован выбором методов оптимизации для возможности работы с тысячами частиц.
 
Отказ от WebGL продиктован выбором методов оптимизации для возможности работы с тысячами частиц.
  
 
== Решение ==
 
== Решение ==
 
+
'''[http://cl49743.tmweb.ru/node/phen/pendulum.php Страница решения]'''
 
 
===== Результат =====
 
<center>
 
{{#widget:Iframe|url=https://ailurus.ru/stands/phen/|width=960|height:540|border=0}}
 
 
 
<big>[//ailurus.ru/stands/phen/ Страница решения]</big>
 
</center>
 
 
 
Вы можете попробовать возможности симулятора прямо здесь, либо перейдя на полноразмерную страницу решения.
 
 
 
В пункте '''4.7''' представлен пример готовой команды для создания трёхатомной молекулы.
 
 
 
 
[[File:iPhone_image_2015-4-13-1431495673979_0.jpg|thumb|Нахождение периода в простом движении]]
 
[[File:iPhone_image_2015-4-13-1431495673979_0.jpg|thumb|Нахождение периода в простом движении]]
 
 
===== Элементы системы =====
 
===== Элементы системы =====
 
* Частицы;
 
* Частицы;
 
* Стержни и пружины<ref>Стержни рассчитываются на расстяжение/сжатие методом коррекции координат.<br>
 
* Стержни и пружины<ref>Стержни рассчитываются на расстяжение/сжатие методом коррекции координат.<br>
Действие пружин учитывается как действие сил упругости.</ref>;
+
Действие пружин учитывается как действие силы упругости.</ref>;
 
* Стенки;
 
* Стенки;
 
* Поле сил;
 
* Поле сил;
Строка 108: Строка 71:
  
 
[[File:iPhone_image_2015-4-13-1431495674173_2.jpg|thumb|Пример вывода консоли]]
 
[[File:iPhone_image_2015-4-13-1431495674173_2.jpg|thumb|Пример вывода консоли]]
 
 
 
===== Возможности консоли =====
 
===== Возможности консоли =====
 
* Конфигурация начальной системы тел;
 
* Конфигурация начальной системы тел;
Строка 115: Строка 76:
 
* Запуск алгоритмов анализа системы;
 
* Запуск алгоритмов анализа системы;
 
* Распознавание и вывод ошибок в пользовательских запросах и в исходном коде;
 
* Распознавание и вывод ошибок в пользовательских запросах и в исходном коде;
* Распознавание и вывод предупреждений в пользовательских запросах и в исходном коде;
+
* Распознавани и вывод предупреждений в пользовательских запросах и в исходном коде;
 
* Подключение/отключение сеток разметки, в том числе с пользовательскими размерами ячейки;
 
* Подключение/отключение сеток разметки, в том числе с пользовательскими размерами ячейки;
 
* Тетрис.
 
* Тетрис.
 
  
 
===== Команды консоли =====
 
===== Команды консоли =====
Координаты пишутся в декартовой системе (х,у), единица измерения - пиксели, орт х направлен от левого края к правому, орт у от верхнего края к нижнему. Пример: (0,100) - координаты точки, лежащей на левом краю экрана на 100 пикселей ниже верхней границы.
+
Координаты пишутся в декартовой системе (х,у), единица измерения - пиксели, орт х направлен от левого края к правосу, орт у от верхнего края к нижнему.Пример: (0,100) - координаты точки, лежащей на левом краю экрана на 100 пикселей ниже верхней границы.
  
Консоль выводит каждое сообщение как одну строку. Если доступной длины строки не хватает, сообщение обрезается. Для отображения полной версии сообщения необходимо кликнуть по нему мышью.
 
 
 
<div class="mw-collapsible mw-collapsed" style="width:100%" >
 
  <big>'''Примеры основных запросов'''</big>
 
<div class="mw-collapsible-content">
 
 
{{начало цитаты}}
 
{{начало цитаты}}
 +
<small>
 
Очистить поле консоли
 
Очистить поле консоли
  
* '''clrscr'''
+
*'''clrscr'''
 
 
  
Отобразить статистику элементов системы и число тиков:
+
Отобразить статистику элементов системы и чисто тиков:
 
 
* '''getInfo'''
 
  
 +
*'''getInfo'''
  
 
Создать частицу
 
Создать частицу
  
* '''addPoint (100,100)''' (0,10) 80 5<ref name="optValues">Без выделения жирным написаны необязательные параметры. При желании указать необязательный параметр все значения слева от него следует считать обязательными (во избежание путаницы при парсинге безразмерных величин в команде).</ref><ref>Значения по умолчанию: вектор скорости нулевой, радиус равен 50 пикселям, масса равна 5 у. е.</ref>, где
+
*'''addPoint (100,100)''' (100,90) 80 5<ref>без выделения жирным написаны необязательные параметры. Значения по умолчанию: предыдущие координаты совпадают с текущими, радиус равен 50 пикселям, масса равна 5 у. е. При желании указать необязательный параметр все значения становятся обязательными (во избежание путаницы при парсинге команы)</ref>, где
  
 
(100,100) - текущие координаты
 
(100,100) - текущие координаты
  
(0,10) - вектор скорости относительно начала координат
+
(100,90) - предыдущие координаты
  
 
80 -  радиус частицы в пикселях
 
80 -  радиус частицы в пикселях
  
 
5 - масса частицы в у. е.  
 
5 - масса частицы в у. е.  
 
  
 
Задать вектор скорости (относительно начала координат)
 
Задать вектор скорости (относительно начала координат)
  
* '''setVelocity #0 (10,10)''', где
+
*'''setVelocity 0 (10,10)''', где
 
 
0 - id частицы<ref name="pointsId">Идентификационный номер элемента в системе. Генерируется последовательно, начиная с нуля, для стенок, стержней, пружин и частиц при их добавлении. Для частиц значение id можно найти нажатием сначала на кнопку "Get point's id", а затем на частицу: тогда Id отобразится в консоли.</ref>
 
 
 
(10,10) - новый вектор скорости относительно начала координат
 
 
 
 
 
Переместить частицу (относительно начала координат)
 
 
 
* '''movePoint #3 (100,100)''' saveV<ref name="optValues" /><ref name="workWithVisualisation">'''Важно!''' Визуально действие этой команды применится только при перерисовке кадра.</ref>, где
 
 
 
3 - id частицы<ref name="pointsId" />
 
 
 
(100,100) - новые координаты
 
 
 
saveV - флаг сохранения скорости. Если указан, частица после перемещения сохранит вектор своей скорости.
 
 
 
 
 
Задать массу
 
 
 
* '''setProp #0 mass 10''', где
 
 
 
0 - id частицы<ref name="pointsId" />
 
 
 
10 - новая масса в у. е.
 
 
 
* '''setProp #0 invmass 0.1''', где
 
 
 
0 - id частицы<ref name="pointsId" />
 
 
 
0.1 - обратное значение новой массы
 
 
 
 
 
Задать радиус
 
 
 
* '''setProp #0 radius 100''', где
 
 
 
0 - id частицы<ref name="pointsId" />
 
 
 
100 - радиус частицы в пикселях
 
  
 +
0 - id частицы
  
 
Создать пружину между частицами
 
Создать пружину между частицами
  
* '''addSpring #0 #1''' 50<ref name="optValues" /><ref name="workWithVisualisation" /><ref>Жёсткость пружины по умолчанию равна 5 у. е.</ref>, где
+
*'''addSpring -0 d -1 d''', где
 
 
0, 1 - id частиц<ref name="pointsId" />
 
 
 
50 - жёсткость пружины в у. е.
 
 
 
 
 
Изменить жёсткость пружины
 
* '''changeSpring #5 10''', где
 
 
 
5 - id пружины<ref name="pointsId" />
 
 
 
10 - новая жёсткость в у. е.
 
 
 
 
 
Изменить жёсткость одинаковых пружин
 
* '''changeSprings 10->15''', где
 
 
 
10 - текущая жёсткость в у. е.
 
 
 
15 - новая жёсткость в у. е.
 
 
 
 
 
Изменить жёсткость всех пружин
 
* '''changeSprings all->20''', где
 
 
 
20 - новая жёсткость в у. е.
 
  
 +
0, 1 - id частиц
  
 
Создать стержень между частицами
 
Создать стержень между частицами
  
* '''addStick #2 #1'''<ref name="workWithVisualisation" />, где
+
*'''addStick -2 d -1 d''', где
 
 
2, 1 - id частиц<ref name="pointsId" />
 
  
 +
2, 1 - id частиц
  
 
Отключение гравитации
 
Отключение гравитации
  
* '''gravity disable'''
+
*'''gravy disable'''
 
 
  
 
Задать вектор ускорения свободного падения (относительно начала координат)
 
Задать вектор ускорения свободного падения (относительно начала координат)
  
* '''gravity (0,10)'''
+
*'''gravy (0,10)'''
 
 
 
 
Отобразить сетку<ref>''' Важно! ''' Отображается максимум одна таблица за раз.</ref>
 
 
 
* '''showGrid type 0'''
 
 
 
Сетка 100х50 пикселей.
 
 
 
* '''showGrid type 1'''
 
 
 
Сетка 50х25 пикселей.
 
 
 
* '''showGrid type 2'''
 
 
 
Сетка 20х10 пикселей.
 
 
 
* '''showGrid 100x75''', где
 
  
100 - ширина ячеек в пикселях
+
Отобразить сетку
  
50 - высота ячеек в пикселях
+
*'''showTable type 0'''
  
 +
*'''showTable type 1'''
  
 
Спрятать сетку
 
Спрятать сетку
  
* '''hideGrid'''
+
*'''hideTable'''
 
+
</small>
 
 
Запустить симуляцию
 
* '''play'''
 
 
 
 
 
Остановить симуляцию
 
* '''stop'''
 
 
 
 
 
"Промотать" симуляцию
 
* '''step(100)''', где
 
 
 
100 - число пропускаемых отрисовкой тиков
 
 
 
 
 
Исполнить внутренний метод
 
* '''execute nameMethod(params)''', где
 
 
 
nameMethod - название метода
 
 
 
params - сообщаемые параметры
 
 
 
 
 
Посмотреть историю запросов
 
* '''getHistory''' <ref>Запросы выводятся в всплывающем окне одной цельной командой, для воспроизведения цепочки запросов достаточно ввести эту команду в консоль.
 
<br>
 
'''Важно!''' Учитываются только запросы, успешно введённые в консоль. Воздействия на систему при помощи кнопки "Move point" будут потеряны.</ref>
 
 
 
 
 
Объединить команды в одном запросе
 
* '''commandOne---commandTwo''', где
 
 
 
commandOne, commandTwo - команды консоли, могут также состоять из объединённых команд
 
 
{{конец цитаты}}
 
{{конец цитаты}}
</div>
 
</div>
 
  
 
===== Возможности плеера =====
 
===== Возможности плеера =====
 
* Воспроизведение/пауза симуляции с заданным <math>\Delta t</math>;  
 
* Воспроизведение/пауза симуляции с заданным <math>\Delta t</math>;  
 
* Скачок вперёд на кратное <math>\Delta t</math> время;
 
* Скачок вперёд на кратное <math>\Delta t</math> время;
* "Замедление времени"<ref>При малой производительности клиента уменьшаем число отрисовок в единицу времени для сохранения гладкости анимации. Управляется через консоль.</ref>.
+
* "Замедление времени"<ref>При малой производительности клиента уменьшаем число отрисовок в единицу времени для сохранения гладкости анимации. Управлятся через консоль.</ref>.
  
===== Кнопки интерфейса =====
+
== Обсуждение результатов и выводы ==
* Кнопка '''Get point's id'''
 
  
После нажатия на кнопку, а затем на частицу выводит id последней в консоль.
+
<br>
 
+
Скачать отчет:
 
+
<br>
* Кнопка '''Move point'''<ref name="workWithVisualisation" />
+
Скачать презентацию:
 
 
Эквивалентна команде консоли movePoint без флага saveV.
 
 
 
После нажатия на кнопку, а затем на частицу закрепляет управление положением последней за курсором мыши.
 
 
 
Дальнейшие нажатия на свободные участки поля переносят эту частицу в точку нажатия, при этом её скорость считается нулевой.
 
 
 
Для прекращения управления следует вновь нажать исходную кнопку.
 
 
 
 
 
* Клавиша клавиатуры '''~''' (также '''`''', '''ё''',  '''Ё''')
 
 
 
Эквивалентна командам консоли play/stop и кнопкам <big>'''►'''</big> / <big>'''| |'''</big> плеера.
 
 
 
 
 
* Кнопки плеера
 
 
 
<big>'''►'''</big> / <big>'''| |'''</big>  - воспроизведение/остановка симуляции (эквивалентна командам консоли play/stop).
 
 
 
'''►''' - переход на тик вперёд (эквивалентна команде консоли step(1)).
 
 
 
'''►►''' - переход на 50 тиков вперёд (эквивалентна команде консоли step(50)).
 
 
 
'''►|''' - переход на 100 тиков вперёд (эквивалентна команде консоли step(100)).
 
 
 
===== Пример системы =====
 
Смоделируем молекулу из трёх атомов (подобную молекуле углекислого газа, рассматриваемой в [[КП: Молекула углекислого газа|курсовом проекте]] [[Смирнов Александр|А. Смирнова]]).
 
 
 
[[File:phenCO2Example.png|center]]
 
 
 
Для создания такой системы необходимо последовательно выполнить следующие команды:
 
 
 
{{начало цитаты}}
 
Создаём атомы кислорода, углерода и кислорода соответственно.
 
 
 
* 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)
 
{{конец цитаты}}
 
 
 
== Выдержки кода решения ==
 
<div class="mw-collapsible mw-collapsed" style="width:100%" >
 
'''Исходный код некоторых файлов [HTML, JS, CSS]'''
 
<div class="mw-collapsible-content">
 
Файл '''"point.js"'''
 
<syntaxhighlight lang="javascript" line start="1" enclose="div">
 
function Point(mass, radius, coorXY, oldCoorXY, isExpToPot)
 
{
 
    this.mass = mass;
 
    this.invmass = 1 / mass;
 
    this.radius = radius;
 
    this.coor = coorXY;
 
    this.oldCoor = oldCoorXY || coorXY;
 
    this.id = getId();
 
    this.type = 'point';
 
    this.isExpToPot = isExpToPot || false;
 
   
 
// log
 
    console.log('Create: '+this.toString());
 
}
 
 
 
 
 
Point.prototype.checkCoor = function(){
 
    if (this.coor.isNaN())
 
    {
 
        console.error('Coor is NaN: ' + this);
 
        player.stop();
 
    }
 
    else
 
    {
 
        if (this.oldCoor.isNaN())
 
        {
 
            console.warning('OldCoor is NaN: ' + this);
 
            this.oldCoor = new Coor(this.coor);
 
        }
 
        else
 
        {
 
            return ;
 
        }
 
    }
 
}
 
 
 
Point.prototype.toString = function(type){
 
    if (type === 'full')
 
    {
 
        return JSON.stringify(this, [ 
 
                                        'type',
 
                                        'id',
 
                                        'coor',
 
                                        'mass_',
 
                                        'radius_',
 
                                        'x',
 
                                        'y',
 
                                        'begin',
 
                                        'end'
 
                                ], 4)
 
    }
 
    else
 
    {
 
        return JSON.stringify(this, [ 
 
                                        'type',
 
                                        'id',
 
                                        'coor',
 
                                        'x',
 
                                        'y',
 
                                        'begin',
 
                                        'end'
 
                                ], 4)
 
    }
 
}
 
 
 
Point.prototype.move = function(coorXY){
 
var delta = minusCoor(this.coor, this.oldCoor);
 
    this.coor = coorXY;
 
    this.oldCoor = minusCoor(coorXY, delta);
 
// log
 
    console.log('Move: '+this.toString());
 
}
 
 
 
Point.prototype.moveEase = function(coorXY){
 
    this.coor = coorXY;
 
    this.oldCoor = coorXY;
 
// log
 
    console.log('Move: '+this.toString());
 
}
 
</syntaxhighlight>
 
 
 
Файл '''"player.js"'''
 
<syntaxhighlight lang="javascript" line start="1" enclose="div">
 
var step;
 
 
 
function Player(dt, phys, render, console)
 
{
 
    this.tick = 0;
 
    this.dt = dt;
 
    this.tickCount = 0;
 
    this.physics = new Physics(phys, this);
 
    this.renderer = new Renderer(render, this);
 
    this.parser = new Parser(this);
 
    this.console = new Console(console, this);
 
    var checker = checkerPl.bind(this);
 
   
 
    this.stop = function(){
 
        clearTimeout(this.tick);
 
        this.tick = 0;
 
       
 
// log
 
        this.console.logAns('Pause...');
 
    }
 
   
 
    this.play = function(){
 
        if (!this.tick)
 
        {
 
            this.tick = setTimeout(function stepTm(pl){
 
                step(pl, checker);
 
                pl.tick = setTimeout(stepTm, pl.dt, pl);
 
            }, this.dt, this);
 
// log
 
            this.console.logAns('Play');
 
        }
 
    }
 
   
 
    this.shifter = function(){
 
        if (this.tick)
 
        {
 
            this.stop();
 
           
 
            return 0;
 
        }
 
        else
 
        {
 
            this.play();
 
         
 
            return 1;
 
        }
 
    }
 
   
 
    this.nextStep = function(i){
 
        if (!i)
 
        {
 
            step(this, checker);
 
// log
 
            this.console.logAns('step');
 
        }
 
        else
 
        {
 
            this.console.logAns('step x ' + i);
 
           
 
            while (i--)
 
            {
 
                step(this, checker);
 
            }
 
        }
 
       
 
    }
 
   
 
    this.getInfo = function(){
 
        this.console.logAns('Tick count: '+this.tickCount);
 
        this.console.logAns('Points count: '+this.physics.Points.length);
 
        this.console.logAns('Walls count: '+this.physics.Walls.length);
 
        this.console.logAns('Springs count: '+this.physics.Springs.length);
 
        this.console.logAns('Sticks count: '+this.physics.Sticks.length);
 
    }
 
   
 
    this.find = function(id){
 
        if (id in this.renderer.arr)
 
        {
 
            return this.renderer.arr[id].ptr;
 
        }
 
       
 
        return 0;
 
    }
 
   
 
    this.addPoint = function(mass, radius, coorXY, oldCoorXY, isExpToPot){
 
        var tmp = this.physics.addPoint(mass, radius, coorXY, oldCoorXY, isExpToPot);
 
        this.renderer.addPoint(tmp);
 
// log
 
        this.console.logAns('Create point.');
 
    }
 
   
 
    this.addWall = function(vectM, vectO){
 
        var tmp = this.physics.addWall(vectM, vectO);
 
        this.renderer.addWall(tmp);
 
// log
 
        this.console.logAns('Create wall.');
 
    }
 
   
 
    this.addStick = function(elem1, elem2){
 
        var tmp = this.physics.addStick(elem1, elem2);
 
        this.renderer.addStick(tmp);
 
// log
 
        this.console.logAns('Create stick.');
 
    }
 
   
 
    this.addSpring = function(elem1, elem2, k){
 
        var tmp = this.physics.addSpring(elem1, elem2, k);
 
        this.renderer.addSpring(tmp);
 
// log
 
        this.console.logAns('Create spring.');
 
    }
 
   
 
    this.parse = function(str){
 
        return this.parser.parse(str);
 
    }
 
   
 
    this.log = function(str){
 
        this.console.log(str);
 
    }
 
   
 
    this.warn = function(str){
 
        this.console.warn(str);
 
    }
 
   
 
    this.error = function(str){
 
        this.console.error(str);
 
    }
 
}     
 
 
 
step = function(pl, checker){
 
    if (!checker())
 
    {
 
        pl.physics.step();
 
        pl.renderer.draw();
 
        pl.tickCount++;
 
    }
 
    else
 
    {
 
        pl.stop();
 
        checker('delete');
 
    } 
 
 
 
 
function checkerPl(mode, vr, val)
 
{
 
    var st = checkerPl.state;
 
   
 
    if (arguments.length == 0) // check
 
    {
 
        for (var i in st)
 
        {
 
            if (player[st[i].vr] < st[i].val)
 
            {
 
                return 1;
 
            }
 
        }
 
       
 
        return 0;
 
    }
 
   
 
    if (mode == 'delete') // delete
 
    {
 
        for (var i in st)
 
        {
 
            if (player[st[i].vr] < st[i].val)
 
            {
 
                delete st[i];
 
            }
 
        }
 
       
 
        return 0;
 
    }
 
   
 
    if (mode == 'add') // add
 
    {
 
        st.push({'vr': vr, 'val': val});
 
       
 
        return 0;
 
    }
 
}
 
</syntaxhighlight>
 
 
 
Файл '''"renderer.js"'''
 
<syntaxhighlight lang="javascript" line start="1" enclose="div">
 
function Renderer(param, pl)
 
{
 
    this.scr = $('#'+param.idEl);
 
    this.borderSize = param.borderSize;
 
    this.arr = new Array();
 
    this.player = pl;
 
}
 
 
 
Renderer.prototype.addPoint = function(point){ 
 
    var el = $('<div data-id="'+point.id+'" class="point"></div>');
 
    el.css('width', 2 * point.radius - 2 * this.borderSize);
 
    el.css('height', 2 * point.radius - 2 * this.borderSize);
 
    el.css('border-radius', 2 * point.radius - 2 * this.borderSize);
 
  el.css('font-size', 10 * (point.radius - this.borderSize) + '%');
 
    this.scr.append(el);
 
   
 
    var tmp = {
 
        'el': el,
 
        'ptr': point,
 
        'draw': function(){
 
                var c = this.patch(this.ptr.coor);
 
                this.el.css('top', c.y);
 
                this.el.css('left', c.x);
 
        },
 
        'setCoor': function(c){
 
                c = c || this.patch(this.ptr.coor);
 
                this.el.css('top', c.y);
 
                this.el.css('left', c.x);
 
        },
 
        'patch': function(c){
 
            return new Coor(c.x - this.ptr.radius, c.y - this.ptr.radius);
 
        },
 
        'patchC': function(c){
 
            return new Coor(c.x + this.ptr.radius, c.y + this.ptr.radius);
 
        }
 
    };
 
    this.arr[point.id] = tmp;
 
   
 
    tmp.setCoor();
 
}
 
 
 
 
 
Renderer.prototype.log = function(str){
 
    this.player.log(str);
 
}
 
 
 
 
 
Renderer.prototype.warn = function(str){
 
    this.player.warn(str);
 
}
 
 
 
 
 
Renderer.prototype.error = function(str){
 
    this.player.error(str);
 
}
 
 
 
 
 
Renderer.prototype.addWall = function(wall){
 
    var el = $('<div data-id="'+wall.id+'" class="wall"></div>');
 
    el.css('left', wall.main_.midX());
 
    el.css('top', wall.main_.midY())
 
    this.scr.append(el);
 
   
 
    var tmp = {
 
        'el': el,
 
        'ptr': wall
 
    }
 
   
 
    this.arr[wall.id] = tmp;
 
}
 
 
 
Renderer.prototype.addStick = function(stick){
 
    var el = $('<div data-id="'+stick.id+'" class="stick"></div>');
 
    this.scr.append(el);
 
   
 
    var tmp = {
 
        'el': el,
 
        'ptr': stick,
 
        'setCoor': function(b, e, l){
 
            setCoorLink.call(this, b, e, l);
 
        },
 
        'draw': function(){
 
            this.setCoor(this.ptr.coorB, this.ptr.coorE, this.ptr.length_);
 
        }
 
    };
 
 
 
    this.arr[stick.id] = tmp;
 
}
 
 
 
Renderer.prototype.addSpring = function(spring){
 
    var el = $('<div data-id="'+spring.id+'" class="spring"></div>');
 
    this.scr.append(el);
 
   
 
    var tmp = {
 
        'el': el,
 
        'ptr': spring,
 
        'setCoor': function(b, e, l){
 
            setCoorLink.call(this, b, e, l);
 
        },
 
        'draw': function(){
 
            if (this.ptr.k == 0)
 
            {
 
                this.el.hide();
 
                delete this.draw;
 
            }
 
            else
 
            {
 
                this.setCoor(this.ptr.coorB, this.ptr.coorE, length(this.ptr.coorB, this.ptr.coorE));
 
            }
 
        }
 
    };
 
 
 
    this.arr[spring.id] = tmp;
 
}
 
 
 
Renderer.prototype.draw = function(){
 
    for (var key in this.arr)
 
    {
 
        var l = this.arr[key];
 
       
 
        if (l.draw)
 
        {
 
            l.draw();           
 
        }
 
 
 
    }
 
}
 
</syntaxhighlight>
 
 
 
Файл '''"func.js"'''
 
<syntaxhighlight lang="javascript" line start="1" enclose="div">
 
function Coor(e, y)
 
{
 
    if (e.pageX)
 
    {
 
        this.x = e.pageX;
 
        this.y = e.pageY;
 
       
 
        return ;
 
    }
 
    if (typeof e == 'number')
 
    {
 
        this.x = e;
 
        this.y = y;
 
       
 
        return ;
 
    }
 
    else
 
    {
 
        this.x = e.x;
 
        this.y = e.y;
 
       
 
        return ;
 
    }
 
}
 
 
 
function createHTMLTable(id, numberOfCellsPerRow, numberOfRows, cellWidth, cellHeight)
 
{
 
var generatedHTMLTable = '<style>    \
 
#table' + id + '.show{}        \
 
#table' + id + '.hide{display:none;}    \
 
#table' + id + '{display:table;position:fixed;width: 2000px;height:1000px;top:27px;border-collapse:collapse;left:0px;z-index:1;outline:2px solid gray;}  \
 
#table' + id + ' > div{display:table-row;}                                                \
 
#table' + id + ' > div > div{display:table-cell;width:' + cellWidth + 'px;height:' + cellHeight + 'px;outline:1px solid white;}    \
 
</style>';
 
for(var ii = 0; ii < numberOfRows; ++ii)
 
{
 
generatedHTMLTable += "<div>";
 
 
for(var i = 0; i < numberOfCellsPerRow; ++i)
 
{
 
generatedHTMLTable += "<div></div>";
 
}
 
 
generatedHTMLTable += "</div>";
 
}
 
                                             
 
var tableDiv = $('<div id="table'+id+'" class="show" data-type="grid">'+generatedHTMLTable+'</div>')
 
 
$('body').append(tableDiv);
 
}
 
 
 
function colorHTMLTableCell(id, numberOfRows, numberOfCell)
 
{
 
  var fix = 1;
 
  var rowBefore = Math.floor((fix + numberOfCell) / numberOfRows);
 
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>');
 
$('#table' + id).append(styleToColorDiv);
 
}
 
 
 
 
 
Coor.prototype.toString = function(type){
 
    return JSON.stringify(this);
 
}
 
 
 
Coor.prototype.plus = function(B){
 
    this.x += B.x;
 
    this.y += B.y;
 
   
 
    return this;
 
}
 
 
 
Coor.prototype.isNaN = function(){
 
    if (isNaN(this.x) || isNaN(this.y))
 
        return 1;
 
    return 0;
 
}
 
 
 
function dot(A, B)
 
{
 
    return (A.x * B.x + A.y * B.y);
 
}
 
 
 
function length(A, B)
 
{
 
    return Math.sqrt((A.x - B.x)*(A.x - B.x) + (A.y - B.y)*(A.y - B.y));
 
}
 
 
 
function sign(x)
 
{
 
    return (x > 0 ? 1 : x < 0 ? -1 : 0);
 
}
 
 
function Vector(A, B)
 
{
 
    this.begin = A;
 
    this.end = B;
 
    this.length = length(A, B);
 
}
 
 
 
Vector.prototype.midX = function(){
 
    return (this.begin.x + this.end.x)/2;
 
}
 
 
 
 
 
Vector.prototype.midY = function(){
 
    return (this.begin.y + this.end.y)/2;
 
}
 
 
 
Vector.prototype.toString = function(type){
 
    if (type === 'full')
 
    {
 
        return JSON.stringify(this, null, 4)
 
    }
 
    else
 
    {
 
        return JSON.stringify(this, [ 
 
                                        'begin',
 
                                        'end',
 
                                        'length',
 
                                        'x',
 
                                        'y',
 
                                ], 4)
 
    }
 
}
 
 
 
Vector.prototype.resize = function(){
 
    this.length = length(A, B);
 
}
 
 
 
Vector.prototype.plus = function(B){
 
    this.begin.plus(B.begin);
 
    this.end.plus(B.end);
 
    this.resize();
 
   
 
    return this;
 
}
 
 
 
function plusVector(A, B)
 
{
 
    return new Vector(plusCoor(A.begin, B.begin), plusCoor(A.end, B.end));
 
}
 
 
 
function plusCoor(A, B)
 
{
 
    return new Coor(A.x + B.x, A.y + B.y);
 
}
 
         
 
function minusCoor(A, B)
 
{
 
    return new Coor(A.x - B.x, A.y - B.y);
 
}
 
 
 
function timesCoor(e, k)
 
{
 
    return new Coor(k * e.x, k * e.y);
 
}   
 
function deltaCoor(e)
 
{
 
    return Math.sqrt(dot(e,e));
 
}
 
 
 
function setCoorLink(a, b, length)
 
{
 
 
 
    this.el.css('top', a.y);
 
    this.el.css('left', a.x);
 
this.el.css('height', length);
 
var angle = 0;
 
  var deltaX = b.x - a.x;
 
  var deltaY = b.y - a.y;
 
 
 
  if (deltaX == 0)
 
angle = ((deltaY > 0) - (deltaY < 0)) * Math.PI / 2;
 
else
 
        if(deltaX < 0)
 
        angle = ((deltaY >= 0) - (deltaY < 0)) * Math.PI + Math.atan(deltaY / deltaX);
 
        else angle = Math.atan(deltaY / deltaX);
 
 
 
  angle -= Math.PI/2;
 
  this.el.css('transform', 'rotate(' + angle + 'rad)');
 
}
 
</syntaxhighlight>
 
 
 
Файл '''"console.js"'''
 
<syntaxhighlight lang="javascript" line start="1" enclose="div">
 
function Console(par, pl)
 
{
 
    this.player = pl;
 
    this.btn = $(par.btn);
 
    this.input = $(par.inp);
 
    this.txt = $(par.log);
 
    this.history = new Array();
 
    this.oHistory = new Array();
 
    var exec = this.execute.bind(this);
 
    var keydown = keydownCons.bind(this);
 
   
 
    this.btn.click(function(){
 
        exec();
 
    });
 
   
 
    this.input.keydown(keydown);
 
}
 
 
 
function keydownCons(e)
 
{
 
    var e = e || window.event;
 
    var key = e.which;
 
       
 
    if (key == 38)
 
    {
 
        this.input.val(this.getHist());
 
    }
 
   
 
    if (key == 40)
 
    {
 
        this.input.val(this.getHistInv());
 
    }
 
   
 
    if (e.ctrlKey && key == 13)
 
    {
 
        this.input.val(this.input.val() + '---');
 
       
 
        return ;
 
    }
 
       
 
    if (key == 13)
 
    {
 
        this.btn.trigger('click');
 
    }
 
}
 
 
 
Console.prototype.log = function(str){
 
    var el = $('<div></div>').addClass('log').html(str.toString().trim()).click(showAllLog);
 
   
 
    this.txt.prepend(el);
 
};
 
 
 
function showAllLog()
 
{
 
    $(this).addClass('all');
 
}
 
 
 
Console.prototype.warn = function(str){
 
    var el = $('<div></div>').addClass('warn').html(str.toString().trim()).click(showAllLog);
 
   
 
    this.txt.prepend(el);
 
};
 
 
 
Console.prototype.error = function(str){
 
    var el = $('<div></div>').addClass('err').html(str.toString().trim()).click(showAllLog);
 
   
 
    this.txt.prepend(el);
 
};
 
 
 
Console.prototype.logAns = function(str){
 
    var el = $('<div></div>').addClass('log').html('> ' + str.toString().trim()).click(showAllLog);
 
   
 
    this.txt.prepend(el);
 
};
 
 
 
Console.prototype.warnAns = function(str){
 
    var el = $('<div></div>').addClass('warn').html('> ' + str.toString().trim()).click(showAllLog);
 
   
 
    this.txt.prepend(el);
 
};
 
 
 
Console.prototype.errorAns = function(str){
 
    var el = $('<div></div>').addClass('err').html('> ' + str.toString().trim()).click(showAllLog);
 
   
 
    this.txt.prepend(el);
 
};
 
 
 
Console.prototype.saveHist = function(txt){
 
    while(this.oHistory.length)
 
    {
 
        this.history.push(this.oHistory.shift());
 
    }
 
   
 
    this.history.push(txt);
 
};
 
 
 
Console.prototype.getHist = function(){
 
    var tmp = this.history.pop();
 
    if (!tmp || tmp.length < 3)
 
    {
 
        return '';
 
    }
 
    this.oHistory.unshift(tmp);
 
   
 
    return tmp; 
 
};
 
 
 
Console.prototype.getHistInv = function(){
 
    var tmp = this.oHistory.shift();
 
    if (!tmp || tmp.length < 3)
 
    {
 
        return '';
 
    }
 
    this.history.push(tmp);
 
   
 
    return tmp; 
 
};
 
 
 
Console.prototype.execute = function(){
 
    var txt = this.input.val().trim();
 
   
 
    if (txt === '')
 
    {
 
        return ;
 
    }
 
   
 
   
 
    if (txt === 'getHistory')
 
    {
 
this.input.val('');
 
 
    var string = '';
 
   
 
    if(this.history.length > 0){
 
    var historyLength = this.history.length;
 
   
 
    for(var i = 0; i < historyLength; ++i){
 
    console.log(this.history[i] + '---');
 
    if(this.history[i] != undefined)
 
    string += this.history[i] + '---';
 
    }
 
string = string.slice(0,-3);
 
    }
 
   
 
    if(this.history.length == 0)
 
    string = 'History is empty';
 
   
 
    alert(string);
 
 
 
    return 0;
 
    }
 
   
 
    this.log(txt);
 
   
 
    if(!this.player.parse(txt))
 
    {
 
        this.input.val('');
 
       
 
        this.saveHist(txt);
 
    }
 
    else
 
    {
 
        this.warnAns('Can not disassemble');
 
    }
 
};
 
</syntaxhighlight>
 
 
 
Файл '''"phen.css"'''
 
<syntaxhighlight lang="css" line start="1" enclose="div">
 
*{margin:0;padding:0;}
 
body{background: gainsboro;}
 
.point{
 
    position: absolute;
 
    z-index: 20;
 
    counter-increment: num;
 
    text-align: center;
 
    border: 4px solid white;
 
    background: dodgerblue;
 
}
 
.point:before{
 
    content: counter(num);
 
    color: white;
 
    -webkit-user-select: none;
 
    -khtml-user-select: none;
 
    -moz-user-select: none;
 
    -ms-user-select: none;
 
    -o-user-select: none;
 
    user-select: none;
 
}
 
#screen{
 
    position: fixed;
 
    z-index: 2;
 
    top:27px;
 
    counter-reset: num;
 
    display: block;
 
    overflow-x: hidden;
 
    width: 100%;
 
    height: auto;
 
    bottom:0;
 
    background: transparent;
 
}
 
#btns input{
 
    font-size: 18px;
 
    position: fixed;
 
    z-index: 2;
 
    overflow: hidden;
 
    width: 25%;
 
    white-space: nowrap;
 
    text-overflow: ellipsis;
 
    color: white;
 
    border: 3px solid white;
 
    border-right: none;
 
    border-radius: none!important;
 
    outline: none!important;
 
    background: dodgerblue;
 
}
 
#btns input.hide{display:none;}
 
#btns input:nth-child(2){left:25%;}
 
#btns input:nth-child(3){left:50%;}
 
#btns input:nth-child(4){left:50%;}
 
#btns input:last-child{
 
    left: 75%;
 
    border: 3px solid white;
 
}
 
#btns input.notActive{}
 
#btns input:active,
 
#btns input.active{
 
    background: crimson!important;
 
}
 
 
 
#btns input[disabled],
 
#btns input[disabled]:active{
 
    background: gray!important;
 
}
 
.wall{
 
    left: 252.5px;
 
    top: 227.5px;
 
    height:0px;
 
    width:500px;
 
    z-index:1000;
 
    margin-top:-2px;
 
    position: absolute;
 
    -webkit-transform: rotate(180deg);
 
    -khtml-transform: rotate(180deg);
 
      -moz-transform: rotate(180deg);
 
      -ms-transform: rotate(180deg);
 
        -o-transform: rotate(180deg);
 
          transform: rotate(180deg);
 
}
 
.stick,
 
.spring{
 
background: white;
 
width: 2px;
 
position:absolute;
 
-webkit-transform-origin:      50% 0;
 
-khtml-transform-origin: 50% 0;
 
  -moz-transform-origin: 50% 0;
 
    -ms-transform-origin: 50% 0;
 
    -o-transform-origin: 50% 0;
 
        transform-origin: 50% 0;
 
}
 
.spring{
 
background: url('./spring.png') no-repeat;
 
background-size: 10px 100%;
 
  width:10px;margin-left:-5px;
 
}
 
input[disabled],
 
input[disabled]:active{
 
    background: gray!important;
 
}
 
#stack{
 
    width: 130px;
 
    height: 260px;
 
    border: solid 1px black;
 
    border-top: 0px;
 
}
 
 
 
#stack .brick{
 
    width: 11px;
 
    height: 11px;
 
    border: solid 1px white;
 
    background: white;
 
    float: left;
 
}
 
#stack .brick.on{
 
    background: black;
 
}
 
#stack .brick.now{
 
    background: green;
 
}
 
 
 
#controls{
 
    position: fixed;
 
    z-index: 22;
 
    bottom: 5px;
 
    left: 5px;
 
    width: 165px;
 
    height: 30px;
 
    padding: 15px;
 
    -webkit-transition: all 400ms;
 
    -khtml-transition:  all 400ms;
 
      -moz-transition: all 400ms;
 
        -o-transition: all 400ms;
 
            transition: all 400ms;
 
    opacity: .5;
 
    border: 3px solid #eee;
 
    border-radius: 20px;
 
    background-color: dodgerblue;
 
}
 
#controls.show{opacity: 1;}
 
#controls:before{
 
    position: absolute;
 
    top: -2px;
 
    left: -2px;
 
    display: block;
 
    width: 334px;
 
    height: 65px;
 
    content: '';
 
}
 
#playerBtns{
 
    position: relative;
 
    left: 30px;
 
}
 
#playerBtns span{
 
    position: absolute;
 
    top: 4px;
 
}
 
#controls[type='0'] #playerBtns span:nth-of-type(1){
 
    top: 0;
 
    width: 0;
 
    height: 0;
 
    border-width: 15px 0 15px 30px;
 
    border-style: solid;
 
    border-color: transparent transparent transparent white;
 
}
 
#controls[type='1'] #playerBtns span:nth-of-type(1){
 
    top: 0;
 
    right: 169px!important;
 
    width: 11px;
 
    height: 30px;
 
    border-right: 8px solid crimson;
 
    border-left: 8px solid crimson;
 
}
 
#playerBtns span:nth-of-type(2){
 
    width: 0;
 
    height: 0;
 
    border-width: 10px 0 10px 20px;
 
    border-style: solid;
 
    border-color: transparent transparent transparent white;
 
}
 
#playerBtns span:nth-of-type(3){
 
    width: 0;
 
    height: 0;
 
    border-width: 10px 0 10px 20px;
 
    border-style: solid;
 
    border-color: transparent transparent transparent white;
 
}
 
#playerBtns span:nth-of-type(3):after{
 
    position: absolute;
 
    top: -10px;
 
    width: 0;
 
    height: 0;
 
    content: '';
 
    border-width: 10px 0 10px 20px;
 
    border-style: solid;
 
    border-color: transparent transparent transparent white;
 
}
 
#playerBtns span:nth-of-type(4){
 
    width: 0;
 
    height: 0;
 
    border-width: 10px 0 10px 20px;
 
    border-style: solid;
 
    border-color: transparent transparent transparent white;
 
}
 
#playerBtns span:nth-of-type(4):after{
 
    position: absolute;
 
    top: -10px;
 
    width: 0;
 
    height: 0;
 
    content: '';
 
    border-width: 10px 2px;
 
    border-style: solid;
 
    border-color: white;
 
}
 
#playerBtns span:nth-of-type(1){right: 165px;}
 
#playerBtns span:nth-of-type(2){right: 130px;}
 
#playerBtns span:nth-of-type(3){right: 90px;}
 
#playerBtns span:nth-of-type(4){right: 35px;}
 
#consoleWindow{
 
position: fixed;
 
top: 30px;
 
z-index: 2;
 
right: 0;
 
width: 25%;
 
font-weight: bold;
 
font-size: 14px;
 
overflow: hidden;
 
max-height: 339px;
 
background: white;
 
}
 
#consoleWindow.hide{height:0;}
 
#consoleWindow.show{height: auto;}
 
#consoleInput{border-bottom: 3px solid gainsboro;}
 
#consoleInput:before{
 
content: '>';
 
font-size: 14px;
 
position: absolute;
 
top: 4px;
 
left: 4px;
 
}
 
#consoleInput [type="text"]{
 
border: none!important;
 
outline: none!important;
 
height: 20px;
 
font-size: 14px;
 
width: 100%;
 
box-sizing:border-box;
 
padding-left: 15px;
 
}
 
#consoleInput [type="button"]{
 
font-size: 18px;
 
    overflow: hidden;
 
    width: 100%;
 
    right: 0;
 
    white-space: nowrap;
 
    text-overflow: ellipsis;
 
    color: white;
 
    border: 3px solid white;
 
    border-radius: none!important;
 
    outline: none!important;
 
    background: dodgerblue;
 
}
 
#consoleInput [type="button"]:active{background: crimson!important;}
 
#consoleLog{
 
width: 100%;
 
max-height: 290px;
 
box-sizing: border-box;
 
height: auto;
 
word-wrap: break-word;
 
overflow-y: auto;
 
overflow-x: hidden;
 
background: white;
 
padding-left: 3px;
 
padding-bottom: 10px;
 
}
 
 
 
#consoleLog div:not(.all){
 
text-overflow: ellipsis;
 
overflow: hidden;
 
max-height: 37px;
 
width: 100%;
 
white-space: nowrap;
 
}
 
 
 
#consoleLog .warn{color: #DAA520;}
 
#consoleLog .err{color: crimson;}
 
#consoleLog .log{color: grey;}
 
</syntaxhighlight>
 
 
 
Файл '''"index.php"'''
 
<syntaxhighlight lang="html5" line start="1" enclose="div">
 
<!DOCTYPE html>
 
<html lang="ru">
 
<head>
 
  <title>Phen v2.0 a</title>
 
  <link rel="stylesheet" href="./phen.css">
 
  <script type="text/javascript" src="./jquery-2.1.3.min.js"></script>
 
        <script type="text/javascript" src="./jquery-css-transform.js"></script>
 
        <script type="text/javascript" src="./point.js"></script>
 
        <script type="text/javascript" src="./spring.js"></script>
 
        <script type="text/javascript" src="./stick.js"></script>
 
        <script type="text/javascript" src="./func.js"></script>
 
<script type="text/javascript" src="./renderer.js"></script>
 
  <script type="text/javascript" src="./wall.js"></script>
 
  <script type="text/javascript" src="./phen.js"></script>
 
        <script type="text/javascript" src="./parser.js"></script>
 
<script type="text/javascript" src="./console.js"></script>
 
  <script type="text/javascript" src="./player.js"></script>
 
  <script type="text/javascript" src="./cycle.js"></script>
 
        <script type="text/javascript" src="./clean.js"></script>
 
</head>
 
<body>
 
    <div id="screen"></div>
 
    <div id="btns">
 
        <input type="button" value="Open/Save" disabled="disabled">
 
        <input id="getPoint" type="button" value="Get point's id">
 
      <input id="movePoint" type="button" value="Move point">
 
      <input id="demovePoint" type="button" class="active hide" value="Move point">
 
      <input id="consoleOpen" class="active" type="button" value="Console">
 
    </div>
 
    <div id="consoleWindow" class="show">
 
        <div id="consoleInput">
 
            <input id="console" type="text" autofocus="autofocus">
 
            <input id="consoleBtn" type="button" value="Enter">
 
        </div>
 
        <div id="consoleLog">
 
        </div>
 
    </div>
 
    <div id="controls" class="show" type="0">
 
        <div id="playerBtns">
 
            <span id="stop"></span>
 
            <span id="stepMin"></span>
 
            <span id="stepMid"></span>
 
            <span id="stepMax"></span>
 
        </div>
 
    </div>
 
</body>
 
</html>
 
</syntaxhighlight>
 
</div>
 
 
 
[[ Медиа : MultiparticleSimulator.zip|Исходный код [php js css].zip]]
 
 
 
Суммарно код текущей версии ('''v2.0 b''' от 2 июня 2015 года) движка занимает 2 500 строк без учёта библиотеки.
 
 
 
Отличие от семейства версий '''1.*''' в полностью переписанной логике проекта для оптимизации вычислений и соблюдения принципов OOP JS.
 
 
 
<div class="mw-collapsible mw-collapsed" style="width:100%" >
 
'''Разбор кода файла point.js'''
 
<div class="mw-collapsible-content">
 
Файл '''"point.js"'''
 
<syntaxhighlight lang="javascript" line start="1" enclose="div">
 
function Point(mass, radius, coorXY, oldCoorXY, isExpToPot)
 
{
 
    this.mass = mass;
 
    this.invmass = 1 / mass;
 
    this.radius = radius;
 
    this.coor = coorXY;
 
    this.oldCoor = oldCoorXY || coorXY;
 
    this.id = getId();
 
    this.type = 'point';
 
    this.isExpToPot = isExpToPot || false;
 
   
 
// log
 
    console.log('Create: '+this.toString());
 
}
 
 
 
 
 
Point.prototype.checkCoor = function(){
 
    if (this.coor.isNaN())
 
    {
 
        console.error('Coor is NaN: ' + this);
 
        player.stop();
 
    }
 
    else
 
    {
 
        if (this.oldCoor.isNaN())
 
        {
 
            console.warning('OldCoor is NaN: ' + this);
 
            this.oldCoor = new Coor(this.coor);
 
        }
 
        else
 
        {
 
            return ;
 
        }
 
    }
 
}
 
 
 
Point.prototype.toString = function(type){
 
    if (type === 'full')
 
    {
 
        return JSON.stringify(this, [ 
 
                                        'type',
 
                                        'id',
 
                                        'coor',
 
                                        'mass_',
 
                                        'radius_',
 
                                        'x',
 
                                        'y',
 
                                        'begin',
 
                                        'end'
 
                                ], 4)
 
    }
 
    else
 
    {
 
        return JSON.stringify(this, [ 
 
                                        'type',
 
                                        'id',
 
                                        'coor',
 
                                        'x',
 
                                        'y',
 
                                        'begin',
 
                                        'end'
 
                                ], 4)
 
    }
 
}
 
 
 
Point.prototype.move = function(coorXY){
 
var delta = minusCoor(this.coor, this.oldCoor);
 
    this.coor = coorXY;
 
    this.oldCoor = minusCoor(coorXY, delta);
 
// log
 
    console.log('Move: '+this.toString());
 
}
 
 
 
Point.prototype.moveEase = function(coorXY){
 
    this.coor = coorXY;
 
    this.oldCoor = coorXY;
 
// log
 
    console.log('Move: '+this.toString());
 
}
 
</syntaxhighlight>
 
 
 
 
 
<syntaxhighlight lang="javascript" line start="3" enclose="div">this.mass = mass;</syntaxhighlight>
 
 
 
Сообщаем нашей частице массу, равную mass.
 
 
 
 
 
<syntaxhighlight lang="javascript" line start="4" enclose="div">this.invmass = 1 / mass;</syntaxhighlight>
 
 
 
Вводим обратную массу, равную 1/mass. В дальнейшем работаем именно с обратным значением массы, так как это позволит вводить  “бесконечно тяжёлые” частицы (значение invmass можно задать отдельно впоследствие).
 
 
 
 
 
<syntaxhighlight lang="javascript" line start="5" enclose="div">this.radius = radius;</syntaxhighlight>
 
 
 
Задаём радиус, равный radius. Измеряется в пикселях.
 
 
 
 
 
<syntaxhighlight lang="javascript" line start="6" enclose="div">this.coor = coorXY;</syntaxhighlight>
 
 
 
Задаём текущие координаты значениями coorXY.x и coorXY.y.
 
 
 
 
 
<syntaxhighlight lang="javascript" line start="7" enclose="div">this.oldCoor = oldCoorXY || coorXY;</syntaxhighlight>
 
 
 
Если были заданы предыдущие координаты (oldCoor.x и oldCoor.y), то записываем их. Иначе записываем текущие координаты, тогда частица будет обладать нулевым вектором скорости
 
 
 
 
 
<syntaxhighlight lang="javascript" line start="8" enclose="div">this.id = getId();</syntaxhighlight>
 
 
 
Генерируем уникальный номер для частицы.
 
 
 
 
 
<syntaxhighlight lang="javascript" line start="9" enclose="div">this.type = 'point';</syntaxhighlight>
 
 
 
Указываем тип элемента как “point”, чтобы обработчик отличал частицу от других объектов.
 
 
 
 
 
<syntaxhighlight lang="javascript" line start="10" enclose="div">this.isExpToPot = isExpToPot || false;</syntaxhighlight>
 
 
 
Сообщаем, должна ли участвовать в потенциальном парном взаимодействии. Да - если isExpToPot равняется true, нет - во всех прочих случаях.
 
 
 
 
 
<syntaxhighlight lang="javascript" line start="13" enclose="div">console.log('Create: ‘+this.toString());</syntaxhighlight>
 
 
 
Выводим в консоль подробную информацию о созданной частице.
 
 
 
 
 
<syntaxhighlight lang="javascript" line start="17" enclose="div">Point.prototype.checkCoor</syntaxhighlight>
 
 
 
Метод проверки значений координат положений частицы. Если текущее положение частицы не может быть определено (не верный запрос, статически неопределимая система и пр.), останавливаем симуляцию и выводим в консоль ошибку. Если не удаётся определить значения координат предыдущего положения (симуляция запущена пользователем после получения ошибки в текущих координатах частицы и пр.), выдаём предупреждение, используем координаты текущего положения также и как координаты предыдущего.
 
 
 
 
 
<syntaxhighlight lang="javascript" line start="37" enclose="div">Point.prototype.toString</syntaxhighlight>
 
 
 
Метод, собирающий информацию о частице и выдающий её в удобном виде.
 
 
 
 
 
<syntaxhighlight lang="javascript" line start="66" enclose="div">Point.prototype.move</syntaxhighlight>
 
 
 
Метод перемещения частицы в указанные координаты. Вектор скорости сохраняется, в консоль записывается информация о перемещении.
 
 
 
 
 
<syntaxhighlight lang="javascript" line start="74" enclose="div">Point.prototype.moveEase</syntaxhighlight>
 
 
 
Другой метод перемещения частицы в указанные координаты. Вектор скорости зануляется, в консоль записывается информация о перемещении.
 
</div>
 
 
 
== Сопроводительная информация ==
 
{| class="wikitable" style="text-align:center; width: 100%;"
 
|-
 
! Общая презентация <br>симулятора
 
! Подробная презентация <br> физического движка <br>в составе симулятора
 
! Отчёт
 
|-
 
| [[File:MultiparticleSimulator.pdf]]
 
| [[File:PhysicsEngine.pdf]]
 
| [[File:MultiparticleSimulatorReport.pdf]]
 
|-
 
| 12 слайдов
 
| 55 слайдов
 
| 21 страница
 
|}
 
  
 
== Ссылки по теме ==
 
== Ссылки по теме ==
T. Jakobsen. "Advanced Character Physics", 2003. ([http://dkhramov.dp.ua/uploads/Comp/Jakobsen/jakobsen.pdf перевод статьи <nowiki></nowiki>])
+
Опорный материал:
 
 
Л. Ландау, Е. Лифшиц. "Теоретическая физика", том первый, "Механика", 1988.
 
  
А. Смирнов. "Курсовой проект: молекула углекислого газа", 2015. ([[КП: Молекула углекислого газа|страница проекта]])
+
Thomas Jakobsen. "Advanced Character Physics", January 21, 2003. ([http://dkhramov.dp.ua/uploads/Comp/Jakobsen/jakobsen.pdf перевод статьи <nowiki></nowiki>])
  
 
== См. также ==
 
== См. также ==
Вам запрещено изменять защиту статьи. Edit Создать редактором

Обратите внимание, что все добавления и изменения текста статьи рассматриваются как выпущенные на условиях лицензии Public Domain (см. Department of Theoretical and Applied Mechanics:Авторские права). Если вы не хотите, чтобы ваши тексты свободно распространялись и редактировались любым желающим, не помещайте их сюда.
Вы также подтверждаете, что являетесь автором вносимых дополнений или скопировали их из источника, допускающего свободное распространение и изменение своего содержимого.
НЕ РАЗМЕЩАЙТЕ БЕЗ РАЗРЕШЕНИЯ МАТЕРИАЛЫ, ОХРАНЯЕМЫЕ АВТОРСКИМ ПРАВОМ!

To protect the wiki against automated edit spam, we kindly ask you to solve the following CAPTCHA:

Отменить | Справка по редактированию  (в новом окне)