Balls v4

Материал из Department of Theoretical and Applied Mechanics
Версия от 18:53, 8 марта 2015; Wikiadmin (обсуждение | вклад) (Замена текста — «<source lang="(.*)" first-line="(.*)">» на «<syntaxhighlight lang="$1" line start="$2" enclose="div">»)

Перейти к: навигация, поиск
Виртуальная лаборатория > Динамика взаимодействующих частиц > Balls - версии > Balls v4


Скачать программу: Balls_v4_release.zip Текст программы на языке JavaScript (разработчики Кривцов Антон, Цветков Денис): <toggledisplay status=hide showtext="Показать↓" hidetext="Скрыть↑" linkstyle="font-size:default"> Файл "Balls_v4_release.js" <syntaxhighlight lang="javascript" line start="1" enclose="div"> window.addEventListener("load", MainBalls, true); function MainBalls() {

   // Предварительные установки
   var canvas = canvasBalls;
   var context = canvas.getContext("2d");                  // на context происходит рисование
   canvas.oncontextmenu = function (e) {return false;};    // блокировка контекстного меню
   var Pi = 3.1415926;                   // число "пи"
   var m0 = 1;                           // масштаб массы
   var T0 = 1;                           // масштаб времени (период колебаний исходной системы)
   var a0 = 1;                           // масштаб расстояния (диаметр шара)
   var g0 = a0 / T0 / T0;                // масштаб ускорения (ускорение, при котором за T0 будет пройдено расстояние a0)
   var k0 = 2 * Pi / T0;                 // масштаб частоты
   var C0 = m0 * k0 * k0;                // масштаб жесткости
   var B0 = 2 * m0 * k0;                 // масштаб вязкости
   // *** Задание физических параметров ***
   var Ny = 5;                           // число шаров, помещающихся по вертикали в окно (задает размер шара относительно размера окна)
   var m = 1 * m0;                       // масса
   var Cwall = 10 * C0;                  // жесткость стен
   var Cball = 0.1 * Cwall;              // жесткость между частицами
   var B = 0.008 * B0;                   // вязкость среды
   var Bwall = 0.03 * B0;                // вязкость на стенках
   var mg = 0.25 * m * g0;               // сила тяжести
   var r = 0.5 * a0;                     // радиус частицы в расчетных координатах
   var K = 0.85;                         // сила взаимодействия ограничивается значением, реализующимся при r/a = K
   var a = 2 * r;                        // равновесное расстояние между частицами
   var aCut = 2 * a;                     // радиус обрезания
   // *** Задание вычислительных параметров ***
   var fps = 50;                         // frames per second - число кадров в секунду (качечтво отображения)
   var spf = 100;                        // steps per frame   - число шагов интегрирования между кадрами (скорость расчета)
   var dt  = 0.045 * T0 / fps;           // шаг интегрирования (качество расчета)
   // Выполнение программы
   var scale = canvas.height / Ny / a0;  // масштабный коэффициент для перехода от расчетных к экранным координатам
   var r2 = r * r;                       // ___в целях оптимизации___
   var aCut2 = aCut * aCut;              // ___в целях оптимизации___
   var a2 = a * a;                       // ___в целях оптимизации___
   var D = a2 * Cball / 72;              // энергия связи между частицами
   var LJCoeff = 12 * D / a2;            // коэффициент для расчета потенциала Л-Дж
   var Ka = K * a;                       // ___в целях оптимизации___
   var K2a2 = K * K * a2;                // ___в целях оптимизации___
   var w = canvas.width / scale;           // ширина окна в расчетных координатах
   var h = canvas.height / scale;          // высота окна в расчетных координатах
   var dNd = null;                         // ссылка на захваченный курсором шар (drag & drop)
   // Работа с мышью
   var mx_, my_;                           // буфер позиции мыши (для расчета скорости при отпускании шара)
   canvas.onmousedown = function(e) {      // функция при нажатии клавиши мыши
       var m = mouseCoords(e);             // получаем расчетные координаты курсора мыши
       // цикл в обратную сторону, чтобы захватывать шар, нарисованный "сверху"
       // (т.к. цикл рисования идет в обычном порядке)
       for (var i = balls.length - 1; i >= 0; i--) {
           var b = balls[i];
           var rx = b.x - m.x;
           var ry = b.y - m.y;
           var rLen2 = rx * rx + ry * ry;      // квадрат расстояния между курсором и центром шара
           if (rLen2 <= r2) {                  // курсор нажал на шар
               if (e.which == 1) {             // нажата левая клавиша мыши
                   dNd = b;
                   dNd.xPlus = dNd.x - m.x;    // сдвиг курсора относительно центра шара по x
                   dNd.yPlus = dNd.y - m.y;    // сдвиг курсора относительно центра шара по y
                   mx_ = m.x;    my_ = m.y;
                   canvas.onmousemove = mouseMove;     // пока клавиша нажата - работает функция перемещения
               } else if (e.which == 3) {              // нажата правая клавиша мыши
                   balls.splice(i, 1);                 // удалить шар
               }
               return;
           }
       }
       // если не вышли по return из цикла - нажатие было вне шара, добавляем новый
       if (e.which == 1) {
           dNd = addNewBall(m.x, m.y);         // добавляем шар и сразу захватываем его курсором
           if (dNd == null) return;            // если шар не добавился (из за стен или других шаров) - возвращаемся
           dNd.xPlus = 0;  dNd.yPlus = 0;      // держим шар по центру
           mx_ = m.x;    my_ = m.y;
           canvas.onmousemove = mouseMove;     // пока клавиша нажата - работает функция перемещения
       }
   };
   document.onmouseup = function(e) {          // функция при отпускании клавиши мыши
       canvas.onmousemove = null;              // когда клавиша отпущена - функции перемещения нету
       dNd = null;                             // когда клавиша отпущена - захваченного курсором шара нету
   };
   function mouseMove(e) {                     // функция при перемещении мыши, работает только с зажатой ЛКМ
       var m = mouseCoords(e);                 // получаем расчетные координаты курсора мыши
       dNd.x = m.x + dNd.xPlus;
       dNd.y = m.y + dNd.yPlus;
       dNd.vx = 0.6 * (m.x - mx_) / dt / fps;   dNd.vy = 0.6 * (m.y - my_) / dt / fps;
       mx_ = m.x;    my_ = m.y;
   }
   function mouseCoords(e) {                   // функция возвращает расчетные координаты курсора мыши
       var m = [];
       var rect = canvas.getBoundingClientRect();
       m.x = (e.clientX - rect.left) / scale;
       m.y = (e.clientY - rect.top) / scale;
       return m;
   }
   // Работа с массивом
   var balls = [];                             // массив шаров
   var addNewBall =  function(x, y) {
       // проверка - не пересекается ли новый шар со стенами или уже существующими шарами
       if (x - r < 0 || x + r > w || y - r < 0 || y + r > h) return null;
       for (var i = 0; i < balls.length; i++) {
           var rx = balls[i].x - x;
           var ry = balls[i].y - y;
           var rLen2 = rx * rx + ry * ry;
           if (rLen2 < 4 * r2) return null;
       }
       var b = [];
       b.x = x;                b.y = y;        // расчетные координаты шара
       b.fx = 0;               b.fy = mg;      // сила, действующая на шар
       b.vx = 0;               b.vy = 0;       // скорость
       balls[balls.length] = b;                // добавить элемент в конец массива
       return b;
   };
   // Основной цикл программы
   function control() {
       physics();
       draw();
   }
   // Расчетная часть программы
   function physics() {                        // то, что происходит каждый шаг времени
       for (var s = 1; s <= spf; s++) {
           // пересчет сил идет отдельным массивом, т.к. далее будут добавляться силы взаимодействия между шарами
           for (var i0 = 0; i0 < balls.length; i0++) {
               balls[i0].fx = - B * balls[i0].vx;
               balls[i0].fy = mg - B * balls[i0].vy;
           }
           for (var i = 0; i < balls.length; i++) {
               // расчет взаимодействия производится со всеми следующими шарами в массиве,
               // чтобы не считать каждое взаимодействие дважды
               var b = balls[i];
               for (var j = i + 1; j < balls.length; j++) {
                   var b2 = balls[j];
                   var rx = b.x - b2.x;   var ry = b.y - b2.y;         // вектор смотрит на первый шар (b)
                   var r2 = rx * rx + ry * ry;                         // квадрат расстояния между шарами
                   if (r2 > aCut2) continue;                           // проверка на радиус обрезания
                   var rLen = (Math.sqrt(r2));
                   // если расстояние между частицами мало, силы будут посчитаны для K * a
                   if (r2 < K2a2) {
                       if (rLen > 0.00001) {                           // проверка, чтобы избежать деления на 0
                           rx = rx / rLen * Ka;
                           ry = ry / rLen * Ka;
                       }
                       r2 = K2a2;
                       rLen = Ka;                                      // корень K2a2
                   }
                   // сила взаимодействия
                   var s2 = a2 / r2;         var s4 = s2 * s2;         // ___в целях оптимизации___
                   var F = LJCoeff * s4 * s4 * (s4 * s2 - 1);          // сила взаимодействия Леннарда-Джонса
                   var Fx = F * rx;        var Fy = F * ry;
                   b.fx += Fx;             b.fy += Fy;
                   b2.fx -= Fx;            b2.fy -= Fy;
               }
               if (b == dNd) continue;  // если шар схвачен курсором - его вз. со стенами и перемещение не считаем
               if (b.y + r > h) { b.fy += -Cwall * (b.y + r - h) - Bwall * b.vy; }
               if (b.y - r < 0) { b.fy += -Cwall * (b.y - r) - Bwall * b.vy;}
               if (b.x + r > w) { b.fx += -Cwall * (b.x + r - w) - Bwall * b.vx; }
               if (b.x - r < 0) { b.fx += -Cwall * (b.x - r) - Bwall * b.vx; }
               b.vx += b.fx / m * dt;        b.vy += b.fy / m * dt;
               b.x += b.vx * dt;             b.y += b.vy * dt;
           }
       }
   }
   // Рисование
   var rScale13 = r * scale * 1.3;         // ___в целях оптимизации___
   var rScaleShift = r * scale / 5;        // ___в целях оптимизации___
   function draw() {
       context.clearRect(0, 0, w * scale, h * scale);      // очистить экран
       for (var i = 0; i < balls.length; i++){
           var xS = balls[i].x * scale;           var yS = balls[i].y * scale;
           // расчет градиента нужно проводить для каждого шара
           var gradient = context.createRadialGradient(xS, yS, rScale13, xS - rScaleShift, yS + rScaleShift, 0);
           gradient.addColorStop(0, "#0000bb");
           gradient.addColorStop(1, "#44ddff");
           context.fillStyle = gradient;
           context.beginPath();
           context.arc(xS, yS, r * scale, 0, 2 * Math.PI, false);
           context.closePath();
           context.fill();
       }
   }
   // Запуск системы
   for (var i = 0; i < 1000; i++)
       addNewBall(Math.random() * w, Math.random() * h);
   setInterval(control, 1000 / fps);

} </source> Файл "Balls_v4_release.html" <syntaxhighlight lang="html" line start="1" enclose="div"> <!DOCTYPE html> <html> <head>

   <title>Balls</title>
   <script src="Balls_v4_release.js"></script>

</head> <body>

   <canvas id="canvasBalls" width="800" height="600" style="border:1px solid #000000;"></canvas>

</body> </html> </source> </toggledisplay>