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

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

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

Правка может быть отменена. Пожалуйста, просмотрите сравнение версий, чтобы убедиться, что это именно те изменения, которые вас интересуют, и нажмите «Записать страницу», чтобы изменения вступили в силу.
Текущая версия Ваш текст
Строка 6: Строка 6:
 
'''Исполнитель:''' [[Старобинский Егор]]
 
'''Исполнитель:''' [[Старобинский Егор]]
  
'''Группа:''' [[Группа 09|09]]
+
'''Группа:''' [[Группа 09|09]] (23604)
  
 
'''Семестр:''' весна 2015
 
'''Семестр:''' весна 2015
  
 
== Аннотация проекта ==
 
== Аннотация проекта ==
Рассматриваемой задачей этого проекта является определение поведения задаваемой механической двухмерной системы<ref>Строго говоря, мы рассматриваем проекцию трёхмерной системы на двухмерное пространство. Так, на изображении в заголовке страницы демонстрируется следующий эксперимент: есть тело в форме параллелепипеда (набор частиц со связями: пружинами), на тело сверху падает стержень. Проекцией этого тела является его слой: треугольная решётка, закреплённая на концах. Проекция стержня - частица (на изображении отсутствует).</ref> из частиц, сил и ограничений. Для этого был создан интернет-сайт с программой, позволяющей найти решение уравнения движения системы (физический движок). Также на базе этой программы был реализован пользовательский интерфейс с возможностями создания и редактирования исходной системы и визуализацией её поведения.
 
  
В процессе решения широко применялись знания из классической механики и программирования, благодаря чему была реализована возможность моделирования тел из большого числа частиц и ограничений (интерес с точки зрения механики) и консоль управления с упрощёнными интерпретируемыми командами (интерес с точки зрения программирования). Был внедрён ряд математических методов как для вычисления решения уравнения движения, так и для анализа разрешённой системы. Были проведены проверки получившегося движка на предварительно решённых задачах.
 
 
 
''' Научная новизна '''
 
 
Созданный движок позволяет человеку без специальных знаний в области программирования проводить моделирование собственных систем. Не требуется установки никакого дополнительного софта, программа запускается при помощи браузера как на компьютерах, так и на телефонах, планшетах, телевизорах класса Smart.
 
 
Визуализация не применяет технологию WebGL, благодаря чему многократно увеличивается диапазон устройств, способных запустить сайт с полноценной функциональностью.
 
 
Применён метод нахождения периода движения частицы по участку траектории путём последовательного разбиения поля на сетки с возрастающей плотностью ячеек (идея позаимствована из теории слов).
 
 
 
 
Движок представляет все тела как набор частиц со связями и применяет базовый двухшаговый метод численного интегрирования Верле и другие математические методы для разрешения уравнения движения в короткое время.
 
 
По возможности упрощено интегрирование в систему новых алгоритмов анализа (требует знаний в программировании на javascript как для реализации алгоритма, так и для его интегрирования), при этом все пользователи оперируют только актуальной версией программы.
 
 
 
Объединение физического движка и пользовательского интерфейса получило название "Многочастичный симулятор", имеет открытый исходный код и выполнено без использования готовых решений по теме проекта.
 
  
 
== Формулировка задачи ==
 
== Формулировка задачи ==
 
 
===== Цель работы =====
 
===== Цель работы =====
 
Создание интернет-сайта, позволяющего пользователю моделировать многоточечную систему онлайн.
 
Создание интернет-сайта, позволяющего пользователю моделировать многоточечную систему онлайн.
Строка 68: Строка 47:
  
 
Язык реализации: JavaScript.
 
Язык реализации: JavaScript.
 
  
 
===== Визуализация =====
 
===== Визуализация =====
Строка 74: Строка 52:
  
 
Обработка событий: JavaScript.
 
Обработка событий: JavaScript.
 
Манипуляции с DOM: jQuery (безболезненно заменяется на Zepto).
 
  
 
Отказ от WebGL продиктован выбором методов оптимизации для возможности работы с тысячами частиц.
 
Отказ от WebGL продиктован выбором методов оптимизации для возможности работы с тысячами частиц.
  
 
== Решение ==
 
== Решение ==
 
 
 
===== Результат =====
 
===== Результат =====
 
<center>
 
<center>
{{#widget:Iframe|url=https://ailurus.ru/stands/phen/|width=960|height:540|border=0}}
+
{{#widget:Iframe|url=http://cl49743.tmweb.ru/node/phen/|width=960|height:540|border=0}}
  
<big>[//ailurus.ru/stands/phen/ Страница решения]</big>
+
<big>[http://cl49743.tmweb.ru/node/phen/ Страница решения]</big>
 
</center>
 
</center>
  
Строка 94: Строка 68:
  
 
[[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: Строка 81:
  
 
[[File:iPhone_image_2015-4-13-1431495674173_2.jpg|thumb|Пример вывода консоли]]
 
[[File:iPhone_image_2015-4-13-1431495674173_2.jpg|thumb|Пример вывода консоли]]
 
 
 
===== Возможности консоли =====
 
===== Возможности консоли =====
 
* Конфигурация начальной системы тел;
 
* Конфигурация начальной системы тел;
Строка 118: Строка 89:
 
* Подключение/отключение сеток разметки, в том числе с пользовательскими размерами ячейки;
 
* Подключение/отключение сеток разметки, в том числе с пользовательскими размерами ячейки;
 
* Тетрис.
 
* Тетрис.
 
  
 
===== Команды консоли =====
 
===== Команды консоли =====
Строка 125: Строка 95:
 
Консоль выводит каждое сообщение как одну строку. Если доступной длины строки не хватает, сообщение обрезается. Для отображения полной версии сообщения необходимо кликнуть по нему мышью.
 
Консоль выводит каждое сообщение как одну строку. Если доступной длины строки не хватает, сообщение обрезается. Для отображения полной версии сообщения необходимо кликнуть по нему мышью.
  
 +
{{начало цитаты}}
 +
'''Основные запросы'''
  
<div class="mw-collapsible mw-collapsed" style="width:100%" >
+
<small>
  <big>'''Примеры основных запросов'''</big>
 
<div class="mw-collapsible-content">
 
{{начало цитаты}}
 
 
Очистить поле консоли
 
Очистить поле консоли
  
Строка 135: Строка 104:
  
  
Отобразить статистику элементов системы и число тиков:
+
Отобразить статистику элементов системы и чилто тиков:
  
 
* '''getInfo'''
 
* '''getInfo'''
Строка 157: Строка 126:
 
* '''setVelocity #0 (10,10)''', где
 
* '''setVelocity #0 (10,10)''', где
  
0 - id частицы<ref name="pointsId">Идентификационный номер элемента в системе. Генерируется последовательно, начиная с нуля, для стенок, стержней, пружин и частиц при их добавлении. Для частиц значение id можно найти нажатием сначала на кнопку "Get point's id", а затем на частицу: тогда Id отобразится в консоли.</ref>
+
0 - id частицы<ref name="pointsId">Идентификационный номер элемента в системе. Генерируется последовательно, начиная с единицы, для стенок, стержней, пружин и частиц. Для частиц значение id можно найти нажатием сначала на кнопку "Get point's id", а затем на частицу: тогда Id отобразится в консоли.</ref>
 
 
(10,10) - новый вектор скорости относительно начала координат
 
  
  
Строка 171: Строка 138:
  
 
saveV - флаг сохранения скорости. Если указан, частица после перемещения сохранит вектор своей скорости.
 
saveV - флаг сохранения скорости. Если указан, частица после перемещения сохранит вектор своей скорости.
 
  
 
Задать массу
 
Задать массу
  
* '''setProp #0 mass 10''', где
+
* '''setProps #0 mass 10''', где
  
 
0 - id частицы<ref name="pointsId" />
 
0 - id частицы<ref name="pointsId" />
  
10 - новая масса в у. е.
+
10 - новая масса
  
* '''setProp #0 invmass 0.1''', где
+
* '''setProps #0 invmass 0.1''', где
  
 
0 - id частицы<ref name="pointsId" />
 
0 - id частицы<ref name="pointsId" />
Строка 190: Строка 156:
 
Задать радиус
 
Задать радиус
  
* '''setProp #0 radius 100''', где
+
* '''setProps #0 radius 100''', где
  
 
0 - id частицы<ref name="pointsId" />
 
0 - id частицы<ref name="pointsId" />
  
100 - радиус частицы в пикселях
+
100 - радиус частицы
  
  
 
Создать пружину между частицами
 
Создать пружину между частицами
  
* '''addSpring #0 #1''' 50<ref name="optValues" /><ref name="workWithVisualisation" /><ref>Жёсткость пружины по умолчанию равна 5 у. е.</ref>, где
+
* '''addSpring #0 #1''' 50<ref name="optValues" /><ref>Жёсткость пружины по умолчанию равна 5 у. е.</ref><ref name="workWithVisualisation" />, где
  
 
0, 1 - id частиц<ref name="pointsId" />
 
0, 1 - id частиц<ref name="pointsId" />
  
50 - жёсткость пружины в у. е.
+
50 - жёсткость пружины
  
  
Строка 211: Строка 177:
 
5 - id пружины<ref name="pointsId" />
 
5 - id пружины<ref name="pointsId" />
  
10 - новая жёсткость в у. е.
+
10 - новая жёсткость
  
  
Строка 217: Строка 183:
 
* '''changeSprings 10->15''', где
 
* '''changeSprings 10->15''', где
  
10 - текущая жёсткость в у. е.
+
10 - текущая жёсткость
  
15 - новая жёсткость в у. е.
+
15 - новая жёсткость
  
  
Строка 225: Строка 191:
 
* '''changeSprings all->20''', где
 
* '''changeSprings all->20''', где
  
20 - новая жёсткость в у. е.
+
20 - новая жёсткость
  
  
Строка 237: Строка 203:
 
Отключение гравитации
 
Отключение гравитации
  
* '''gravity disable'''
+
* '''gravy disable'''
  
  
 
Задать вектор ускорения свободного падения (относительно начала координат)
 
Задать вектор ускорения свободного падения (относительно начала координат)
  
* '''gravity (0,10)'''
+
* '''gravy (0,10)'''
  
  
Отобразить сетку<ref>''' Важно! ''' Отображается максимум одна таблица за раз.</ref>
+
Отобразить сетку
  
 
* '''showGrid type 0'''
 
* '''showGrid type 0'''
 
Сетка 100х50 пикселей.
 
  
 
* '''showGrid type 1'''
 
* '''showGrid type 1'''
 
Сетка 50х25 пикселей.
 
  
 
* '''showGrid type 2'''
 
* '''showGrid type 2'''
 
Сетка 20х10 пикселей.
 
 
* '''showGrid 100x75''', где
 
 
100 - ширина ячеек в пикселях
 
 
50 - высота ячеек в пикселях
 
  
  
Строка 294: Строка 248:
  
 
Посмотреть историю запросов
 
Посмотреть историю запросов
* '''getHistory''' <ref>Запросы выводятся в всплывающем окне одной цельной командой, для воспроизведения цепочки запросов достаточно ввести эту команду в консоль.
+
* '''getHistory''' <ref>Запросы выводятся в всплывающем окне одной цельной командой, для воспроизведения цепочки запросов достаточно ввести выведенную команду в консоль.
 
<br>
 
<br>
'''Важно!''' Учитываются только запросы, успешно введённые в консоль. Воздействия на систему при помощи кнопки "Move point" будут потеряны.</ref>
+
'''Внимание!''' Учитываются только запросы, успешно введённые в консоль. Воздействия на систему при помощи кнопки "Move point" будут потеряны.</ref>
  
  
Строка 303: Строка 257:
  
 
commandOne, commandTwo - команды консоли, могут также состоять из объединённых команд
 
commandOne, commandTwo - команды консоли, могут также состоять из объединённых команд
 +
</small>
 
{{конец цитаты}}
 
{{конец цитаты}}
</div>
 
</div>
 
 
===== Возможности плеера =====
 
* Воспроизведение/пауза симуляции с заданным <math>\Delta t</math>;
 
* Скачок вперёд на кратное <math>\Delta t</math> время;
 
* "Замедление времени"<ref>При малой производительности клиента уменьшаем число отрисовок в единицу времени для сохранения гладкости анимации. Управляется через консоль.</ref>.
 
  
 
===== Кнопки интерфейса =====
 
===== Кнопки интерфейса =====
Строка 316: Строка 264:
  
 
После нажатия на кнопку, а затем на частицу выводит id последней в консоль.
 
После нажатия на кнопку, а затем на частицу выводит id последней в консоль.
 
  
 
* Кнопка '''Move point'''<ref name="workWithVisualisation" />
 
* Кнопка '''Move point'''<ref name="workWithVisualisation" />
Строка 322: Строка 269:
 
Эквивалентна команде консоли movePoint без флага saveV.
 
Эквивалентна команде консоли movePoint без флага saveV.
  
После нажатия на кнопку, а затем на частицу закрепляет управление положением последней за курсором мыши.
+
После нажатия на кнопку, а затем на частицу закрепляет управление положением последним за курсором мыши.
  
 
Дальнейшие нажатия на свободные участки поля переносят эту частицу в точку нажатия, при этом её скорость считается нулевой.
 
Дальнейшие нажатия на свободные участки поля переносят эту частицу в точку нажатия, при этом её скорость считается нулевой.
  
Для прекращения управления следует вновь нажать исходную кнопку.
+
Для прекращения управления следует вновь нажать кнопку.
 
 
  
 
* Клавиша клавиатуры '''~''' (также '''`''', '''ё''',  '''Ё''')
 
* Клавиша клавиатуры '''~''' (также '''`''', '''ё''',  '''Ё''')
  
Эквивалентна командам консоли play/stop и кнопкам <big>'''►'''</big> / <big>'''| |'''</big> плеера.
+
Эквивалентна командам консоли play/stop и крайней левой кнопке плеера.
 
 
  
 
* Кнопки плеера
 
* Кнопки плеера
  
<big>'''►'''</big> / <big>'''| |'''</big>  - воспроизведение/остановка симуляции (эквивалентна командам консоли play/stop).
+
Слева направо:
  
'''►''' - переход на тик вперёд (эквивалентна команде консоли step(1)).
+
Воспроизведение/остановка симуляции (эквивалентно командам консоли play/step), переход на тик вперёд (эквивалентно команде консоли step(1))переход на 50 тиков вперёд (эквивалентно команде консоли step(50)), переход на 100 тиков вперёд (эквивалентно команде консоли step(100)).
  
'''►►''' - переход на 50 тиков вперёд (эквивалентна команде консоли step(50)).
+
===== Возможности плеера =====
 
+
* Воспроизведение/пауза симуляции с заданным <math>\Delta t</math>;
'''►|''' - переход на 100 тиков вперёд (эквивалентна команде консоли step(100)).
+
* Скачок вперёд на кратное <math>\Delta t</math> время;
 +
* "Замедление времени"<ref>При малой производительности клиента уменьшаем число отрисовок в единицу времени для сохранения гладкости анимации. Управлятся через консоль.</ref>.
  
 
===== Пример системы =====
 
===== Пример системы =====
Строка 381: Строка 327:
 
{{конец цитаты}}
 
{{конец цитаты}}
  
== Выдержки кода решения ==
+
== Обсуждение результатов и выводы ==
<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());
 
}
 
  
 
+
<br>
Point.prototype.checkCoor = function(){
+
Скачать отчет:
    if (this.coor.isNaN())
+
<br>
    {
+
Скачать презентацию:
        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>])
+
T, Jakobsen. "Advanced Character Physics", 2003. ([http://dkhramov.dp.ua/uploads/Comp/Jakobsen/jakobsen.pdf перевод статьи <nowiki></nowiki>])
  
Л. Ландау, Е. Лифшиц. "Теоретическая физика", том первый, "Механика", 1988.
+
Л. Ландау, Е. Лифшиц, "Теоретическая физика", том первый, "Механика", 2001.
  
А. Смирнов. "Курсовой проект: молекула углекислого газа", 2015. ([[КП: Молекула углекислого газа|страница проекта]])
+
А, Смирнов. "Курсовой проект: молекула углекислого газа", 2015. ([[КП: Молекула углекислого газа|страница проекта]])
  
 
== См. также ==
 
== См. также ==
Вам запрещено изменять защиту статьи. 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:

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