Balls v4 — различия между версиями

Материал из Department of Theoretical and Applied Mechanics
Перейти к: навигация, поиск
(Новая страница: «Кафедра ТМ > Программирование > [[Программирование и моделирование в Интернет|Интер...»)
 
Строка 16: Строка 16:
 
     document.oncontextmenu=function(e){return false};      // блокировка контекстного меню
 
     document.oncontextmenu=function(e){return false};      // блокировка контекстного меню
  
     const Pi = 3.1415926;           // число "пи"
+
     const Pi = 3.1415926;                   // число "пи"
  
     const m0 = 1;                   // масштаб массы
+
     const m0 = 1;                           // масштаб массы
     const T0 = 1;                   // масштаб времени (период колебаний исходной системы)
+
     const T0 = 1;                           // масштаб времени (период колебаний исходной системы)
     const a0 = 1;                   // масштаб расстояния (диаметр шара)
+
     const a0 = 1;                           // масштаб расстояния (диаметр шара)
  
     const g0 = 0.25 * a0 / T0 / T0;   // масштаб ускорения (ускорение, при котором за T0 будет пройдено расстояние a0)
+
     const g0 = a0 / T0 / T0;               // масштаб ускорения (ускорение, при котором за T0 будет пройдено расстояние a0)
  
     const k0 = 2 * Pi / T0;           // масштаб частоты
+
     const k0 = 2 * Pi / T0;               // масштаб частоты
     const C0 = m0 * k0 * k0;         // масштаб жесткости
+
     const C0 = m0 * k0 * k0;             // масштаб жесткости
     const B0 = 2 * m0 * k0;       // масштаб вязкости
+
     const B0 = 2 * m0 * k0;           // масштаб вязкости
  
 
     // *** Задание физических параметров ***
 
     // *** Задание физических параметров ***
  
     const Ny = 5;     // Число шаров, помещающихся по вертикали в окно (задает размер шара относительно размера окна)
+
     const Ny = 5;     // число шаров, помещающихся по вертикали в окно (задает размер шара относительно размера окна)
    const scale = canvas.height / Ny / a0;  // масштабный коэффициент для перехода от расчетных к экранным координатам
 
 
     const m = 1 * m0;                    // масса
 
     const m = 1 * m0;                    // масса
 
     const Cwall = 10 * C0;                // жесткость стен
 
     const Cwall = 10 * C0;                // жесткость стен
     const Cball = 12 * Cwall;               // жесткость между частицами
+
     const Cball = 0.1 * Cwall;             // жесткость между частицами
 
     const B = 0.008 * B0;                  // вязкость среды
 
     const B = 0.008 * B0;                  // вязкость среды
 
     const B1 = 0.03 * B0;                  // вязкость на стенках
 
     const B1 = 0.03 * B0;                  // вязкость на стенках
     const mg = 1 * m * g0;              // сила тяжести
+
     const mg = 0.25 * m * g0;              // сила тяжести
     const r = a0 / 2;                  // радиус частицы в расчетных координатах
+
     const r = 0.5 * a0;                  // радиус частицы в расчетных координатах
     const rScale = r * scale;               // радиус частицы в экранных координатах
+
     const arCoeff = 0.75;                   // если r < arCoeff*a, то потенциал будет считаться для arCoeff*a
    const rScale2 = rScale*rScale;          // ___в целях оптимизации___
+
     const a = 2 * r;                       // равновесное расстояние между частицами
 
 
     const a = 2*rScale;                     // равновесное расстояние между частицами
 
 
     const aCut = 2 * a;                    // радиус обрезания
 
     const aCut = 2 * a;                    // радиус обрезания
    const aCut2 = aCut * aCut;              // ___в целях оптимизации___
 
    const a2 = a*a;                        // ___в целях оптимизации___
 
    const arCoeff = 0.75;                    // если r < arCoeff*a, то потенциал будет считаться для arCoeff*a
 
    const D = a2*Cball/72.0 /(scale*scale); // энергия связи между частицами
 
    const LJCoeff = 12*D/a2;                // коэффициент для расчета потенциала Л-Дж
 
  
 
     // *** Задание вычислительных параметров ***
 
     // *** Задание вычислительных параметров ***
  
     const fps = 50;                 // frames per second - число кадров в секунду (качечтво отображения)
+
     const fps = 50;                     // frames per second - число кадров в секунду (качечтво отображения)
     const spf = 100;             // steps per frame  - число шагов интегрирования между кадрами (скорость расчета)
+
     const spf = 100;                 // steps per frame  - число шагов интегрирования между кадрами (скорость расчета)
     const dt  = 0.045 * T0 / fps;     // шаг интегрирования (качество расчета)
+
     const dt  = 0.045 * T0 / fps;         // шаг интегрирования (качество расчета)
  
 
     // Выполнение программы
 
     // Выполнение программы
  
     var w = canvas.width / scale;         // ширина окна в расчетных координатах
+
    const scale = canvas.height / Ny / a0;  // масштабный коэффициент для перехода от расчетных к экранным координатам
     var h = canvas.height / scale;         // высота окна в расчетных координатах
+
    const r2 = r*r;                        // ___в целях оптимизации___
 +
    const aCut2 = aCut * aCut;              // ___в целях оптимизации___
 +
    const a2 = a*a;                        // ___в целях оптимизации___
 +
    const D = a2*Cball/72.0;                // энергия связи между частицами
 +
    const LJCoeff = 12*D/a2;                // коэффициент для расчета потенциала Л-Дж
 +
 
 +
     var w = canvas.width / scale;     // ширина окна в расчетных координатах
 +
     var h = canvas.height / scale;     // высота окна в расчетных координатах
 
     var xStart = 4 * r; var yStart = h - r-3; // начальное положение шара
 
     var xStart = 4 * r; var yStart = h - r-3; // начальное положение шара
  
     var dragAndDropBall = null;                 // ссылка на захваченный курсором шар
+
     var dragAndDropBall = null;             // ссылка на захваченный курсором шар
  
 
     // Работа с мышью
 
     // Работа с мышью
  
     var mouseX;    var mouseY;                 // координаты курсора мыши
+
     var mouseX;    var mouseY;             // координаты курсора мыши
     var mx, my;                                 // буфер позиции мыши (для расчета скорости при отпускании шара)
+
     var mx, my;                           // буфер позиции мыши (для расчета скорости при отпускании шара)
  
     canvas.onmousedown = function(e){           // функция при нажатии клавиши мыши
+
     canvas.onmousedown = function(e){       // функция при нажатии клавиши мыши
         refreshMouseCoords(e);                 // обновить координаты в переменных mouseX, mouseY
+
         refreshMouseCoords(e);             // обновить координаты в переменных mouseX, mouseY
 
         // цикл в обратную сторону, чтобы захватывать шар, нарисованный "сверху"
 
         // цикл в обратную сторону, чтобы захватывать шар, нарисованный "сверху"
 
         // (т.к. цикл рисования идет в обычном порядке)
 
         // (т.к. цикл рисования идет в обычном порядке)
 +
        var mouseXX = mouseX/scale;    var mouseYY = mouseY/scale;
 
         for (var i = balls.length-1; i >= 0; i--){
 
         for (var i = balls.length-1; i >= 0; i--){
 
             var b = balls[i];
 
             var b = balls[i];
             var rx = b.x - mouseX;
+
             var rx = b.xx - mouseXX;
             var ry = b.y - mouseY;
+
             var ry = b.yy - mouseYY;
             b.rLen2 = rx*rx + ry*ry;                 // квадрат расстояния между курсором и центром шара
+
             b.rLen2 = rx*rx + ry*ry;       // квадрат расстояния между курсором и центром шара
             if (b.rLen2 <= rScale2) {                     // курсор нажал на шар
+
             if (b.rLen2 <= r2) {           // курсор нажал на шар
                 if (e.which == 1) {                     // нажата левая клавиша мыши
+
                 if (e.which == 1) {         // нажата левая клавиша мыши
 
                     dragAndDropBall = b;
 
                     dragAndDropBall = b;
                     dragAndDropBall.xPlus = dragAndDropBall.x - mouseX; // сдвиг курсора относительно центра шара по x
+
                     dragAndDropBall.xPlus = dragAndDropBall.xx - mouseXX; // сдвиг курсора относительно центра шара по x
                     dragAndDropBall.yPlus = dragAndDropBall.y - mouseY; // сдвиг курсора относительно центра шара по y
+
                     dragAndDropBall.yPlus = dragAndDropBall.yy - mouseYY; // сдвиг курсора относительно центра шара по y
                     mx = mouseX;    my = mouseY;
+
                     mx = mouseXX;    my = mouseYY;
 
                     canvas.onmousemove = mouseMove;    // пока клавиша нажата - работает функция перемещения
 
                     canvas.onmousemove = mouseMove;    // пока клавиша нажата - работает функция перемещения
 
                 } else if (e.which == 3) {              // нажата правая клавиша мыши
 
                 } else if (e.which == 3) {              // нажата правая клавиша мыши
Строка 94: Строка 94:
 
         // если не вышли по return из цикла - нажатие было вне шара, проверяем, можно ли добавить новый
 
         // если не вышли по return из цикла - нажатие было вне шара, проверяем, можно ли добавить новый
 
         // расстояние между курсором и шаром должно быть 2r, между курсором и стеной - r;
 
         // расстояние между курсором и шаром должно быть 2r, между курсором и стеной - r;
         if (mouseX-rScale < 0 || mouseX+rScale > w*scale || mouseY-rScale < 0 || mouseY+rScale > h*scale) return;
+
         if (mouseXX-r < 0 || mouseXX+r > w || mouseYY-r < 0 || mouseYY+r > h) return;
 
         for (var i0=0; i0<balls.length; i0++)
 
         for (var i0=0; i0<balls.length; i0++)
             if (balls[i0].rLen2 < 4*rScale2) return;   // все rLen2 были посчитаны, т.к. мы не вышли по return
+
             if (balls[i0].rLen2 < 4*r2) return; // все rLen2 были посчитаны, т.к. мы не вышли по return
         if (e.which == 1) addNewBall(mouseX, mouseY);
+
         if (e.which == 1) {
 +
            dragAndDropBall = addNewBall(mouseX, mouseY);           // добавляем шар и сразу захватываем его курсором
 +
            dragAndDropBall.xPlus = 0;  dragAndDropBall.yPlus = 0;  // держим шар по центру
 +
            mx = mouseXX;    my = mouseYY;
 +
            canvas.onmousemove = mouseMove;    // пока клавиша нажата - работает функция перемещения
 +
        }
 
     };
 
     };
  
Строка 107: Строка 112:
 
     function mouseMove(e){                      // функция при перемещении мыши, работает только с зажатой ЛКМ
 
     function mouseMove(e){                      // функция при перемещении мыши, работает только с зажатой ЛКМ
 
         refreshMouseCoords(e);                  // обновить координаты в переменных mouseX, mouseY
 
         refreshMouseCoords(e);                  // обновить координаты в переменных mouseX, mouseY
         dragAndDropBall.xx = (mouseX + dragAndDropBall.xPlus)/scale;
+
        var mouseXX = mouseX/scale;    var mouseYY = mouseY/scale;
         dragAndDropBall.yy = (mouseY + dragAndDropBall.yPlus)/scale;
+
         dragAndDropBall.xx = mouseXX + dragAndDropBall.xPlus;
 +
         dragAndDropBall.yy = mouseYY + dragAndDropBall.yPlus;
 
         dragAndDropBall.x = dragAndDropBall.xx * scale;    dragAndDropBall.y = dragAndDropBall.yy * scale;
 
         dragAndDropBall.x = dragAndDropBall.xx * scale;    dragAndDropBall.y = dragAndDropBall.yy * scale;
         dragAndDropBall.vx = .005*(mouseX-mx)/dt/fps;  dragAndDropBall.vy = .005*(mouseY-my)/dt/fps;
+
         dragAndDropBall.vx = .6*(mouseXX-mx)/dt/fps;  dragAndDropBall.vy = .6*(mouseYY-my)/dt/fps;
         mx = mouseX;    my = mouseY;
+
         mx = mouseXX;    my = mouseYY;
 
     }
 
     }
  
Строка 122: Строка 128:
 
     // Работа с массивом
 
     // Работа с массивом
  
     var balls = [];                             // массив шаров
+
     var balls = [];                                 // массив шаров
 
     var addNewBall =  function(x, y){
 
     var addNewBall =  function(x, y){
 
         var b = [];
 
         var b = [];
  
         b.x = x;              b.y = y;                     // экранные координаты шара
+
         b.x = x;              b.y = y;             // экранные координаты шара
         b.xx = x / scale;      b.yy = y / scale;           // расчетные координаты шара
+
         b.xx = x / scale;      b.yy = y / scale;   // расчетные координаты шара
         b.fx = 0;              b.fy = mg;                   // сила, действующая на шар
+
         b.fx = 0;              b.fy = mg;           // сила, действующая на шар
         b.vx = 0;              b.vy = 0;                   // скорость
+
         b.vx = 0;              b.vy = 0;           // скорость
  
         balls[balls.length] = b;                           // добавить элемент в конец массива
+
         balls[balls.length] = b;                   // добавить элемент в конец массива
 +
        return b;
 
     };
 
     };
  
Строка 157: Строка 164:
  
 
                     var b1 = balls[i];      var b2 = balls[ii];            // ссылки на шары
 
                     var b1 = balls[i];      var b2 = balls[ii];            // ссылки на шары
                     var rx = b1.x - b2.x;  var ry = b1.y - b2.y;           // вектор смотрит на первый шар (b1)
+
                     var rx = b1.xx - b2.xx;  var ry = b1.yy - b2.yy;       // вектор смотрит на первый шар (b1)
 
                     var r2 = rx*rx + ry*ry;                                // квадрат расстояния между шарами
 
                     var r2 = rx*rx + ry*ry;                                // квадрат расстояния между шарами
 
                     if (r2 > aCut2) continue;                              // проверка на радиус обрезания
 
                     if (r2 > aCut2) continue;                              // проверка на радиус обрезания
Строка 188: Строка 195:
 
     // Рисование
 
     // Рисование
  
     var rScale13 = rScale*1.3;         // ___в целях оптимизации___
+
     var rScale13 = r * scale*1.3;           // ___в целях оптимизации___
     var rScaleShift = rScale/5.0;       // ___в целях оптимизации___
+
     var rScaleShift = r * scale/5.0;       // ___в целях оптимизации___
 
     function draw(){
 
     function draw(){
 
         context.clearRect(0, 0, w*scale, h*scale);          // очистить экран
 
         context.clearRect(0, 0, w*scale, h*scale);          // очистить экран
Строка 212: Строка 219:
 
     addNewBall(xStart * scale, yStart * scale);
 
     addNewBall(xStart * scale, yStart * scale);
 
     setInterval(control, 1000/fps);
 
     setInterval(control, 1000/fps);
   
+
 
 
}
 
}
 
</source>
 
</source>

Версия 22:23, 21 апреля 2014

Кафедра ТМ > Программирование > Интернет > JavaScript > Balls > Balls v4

<addscript src=Balls_v4_release/>

Не удается найти HTML-файл Balls_v4_TM.html


Скачать программу: Balls_v4_release.zip Текст программы на языке JavaScript (разработчики Кривцов Антон, Цветков Денис): <toggledisplay status=hide showtext="Показать↓" hidetext="Скрыть↑" linkstyle="font-size:default"> Файл "Balls_v4_release.js"

function MainMech(canvas) {

    // Предварительные установки

    var context = canvas.getContext("2d");                  // на context происходит рисование
    document.oncontextmenu=function(e){return false};       // блокировка контекстного меню

    const Pi = 3.1415926;                   // число "пи"

    const m0 = 1;                           // масштаб массы
    const T0 = 1;                           // масштаб времени (период колебаний исходной системы)
    const a0 = 1;                           // масштаб расстояния (диаметр шара)

    const g0 = a0 / T0 / T0;                // масштаб ускорения (ускорение, при котором за T0 будет пройдено расстояние a0)

    const k0 = 2 * Pi / T0;           	    // масштаб частоты
    const C0 = m0 * k0 * k0;          	    // масштаб жесткости
    const B0 = 2 * m0 * k0;  	      	    // масштаб вязкости

    // *** Задание физических параметров ***

    const Ny = 5;						    // число шаров, помещающихся по вертикали в окно (задает размер шара относительно размера окна)
    const m = 1 * m0;                 	    // масса
    const Cwall = 10 * C0;                 	// жесткость стен
    const Cball = 0.1 * Cwall;              // жесткость между частицами
    const B = 0.008 * B0;              	    // вязкость среды
    const B1 = 0.03 * B0;              	    // вязкость на стенках
    const mg = 0.25 * m * g0;          	    // сила тяжести
    const r = 0.5 * a0;               	    // радиус частицы в расчетных координатах
    const arCoeff = 0.75;                   // если r < arCoeff*a, то потенциал будет считаться для arCoeff*a
    const a = 2 * r;                        // равновесное расстояние между частицами
    const aCut = 2 * a;                     // радиус обрезания

    // *** Задание вычислительных параметров ***

    const fps = 50;             	        // frames per second - число кадров в секунду (качечтво отображения)
    const spf = 100;              		    // steps per frame   - число шагов интегрирования между кадрами (скорость расчета)
    const dt  = 0.045 * T0 / fps;      	    // шаг интегрирования (качество расчета)

    // Выполнение программы

    const scale = canvas.height / Ny / a0;  // масштабный коэффициент для перехода от расчетных к экранным координатам
    const r2 = r*r;                         // ___в целях оптимизации___
    const aCut2 = aCut * aCut;              // ___в целях оптимизации___
    const a2 = a*a;                         // ___в целях оптимизации___
    const D = a2*Cball/72.0;                // энергия связи между частицами
    const LJCoeff = 12*D/a2;                // коэффициент для расчета потенциала Л-Дж

    var w = canvas.width / scale;		    // ширина окна в расчетных координатах
    var h = canvas.height / scale;		    // высота окна в расчетных координатах
    var xStart = 4 * r;	var yStart = h - r-3;	// начальное положение шара

    var dragAndDropBall = null;             // ссылка на захваченный курсором шар

    // Работа с мышью

    var mouseX;     var mouseY;             // координаты курсора мыши
    var mx, my;                           // буфер позиции мыши (для расчета скорости при отпускании шара)

    canvas.onmousedown = function(e){       // функция при нажатии клавиши мыши
        refreshMouseCoords(e);              // обновить координаты в переменных mouseX, mouseY
        // цикл в обратную сторону, чтобы захватывать шар, нарисованный "сверху"
        // (т.к. цикл рисования идет в обычном порядке)
        var mouseXX = mouseX/scale;     var mouseYY = mouseY/scale;
        for (var i = balls.length-1; i >= 0; i--){
            var b = balls[i];
            var rx = b.xx - mouseXX;
            var ry = b.yy - mouseYY;
            b.rLen2 = rx*rx + ry*ry;        // квадрат расстояния между курсором и центром шара
            if (b.rLen2 <= r2) {            // курсор нажал на шар
                if (e.which == 1) {         // нажата левая клавиша мыши
                    dragAndDropBall = b;
                    dragAndDropBall.xPlus = dragAndDropBall.xx - mouseXX; // сдвиг курсора относительно центра шара по x
                    dragAndDropBall.yPlus = dragAndDropBall.yy - mouseYY; // сдвиг курсора относительно центра шара по y
                    mx = mouseXX;    my = mouseYY;
                    canvas.onmousemove = mouseMove;     // пока клавиша нажата - работает функция перемещения
                } else if (e.which == 3) {              // нажата правая клавиша мыши
                    balls.splice(i,1);                  // удалить шар
                }
                return;
            }
        }

        // если не вышли по return из цикла - нажатие было вне шара, проверяем, можно ли добавить новый
        // расстояние между курсором и шаром должно быть 2r, между курсором и стеной - r;
        if (mouseXX-r < 0 || mouseXX+r > w || mouseYY-r < 0 || mouseYY+r > h) return;
        for (var i0=0; i0<balls.length; i0++)
            if (balls[i0].rLen2 < 4*r2) return; // все rLen2 были посчитаны, т.к. мы не вышли по return
        if (e.which == 1) {
            dragAndDropBall = addNewBall(mouseX, mouseY);           // добавляем шар и сразу захватываем его курсором
            dragAndDropBall.xPlus = 0;  dragAndDropBall.yPlus = 0;  // держим шар по центру
            mx = mouseXX;    my = mouseYY;
            canvas.onmousemove = mouseMove;     // пока клавиша нажата - работает функция перемещения
        }
    };

    document.onmouseup = function(e){           // функция при отпускании клавиши мыши
        canvas.onmousemove = null;              // когда клавиша отпущена - функции перемещения нету
        dragAndDropBall = null;                 // когда клавиша отпущена - захваченного курсором шара нету
    };

    function mouseMove(e){                      // функция при перемещении мыши, работает только с зажатой ЛКМ
        refreshMouseCoords(e);                  // обновить координаты в переменных mouseX, mouseY
        var mouseXX = mouseX/scale;     var mouseYY = mouseY/scale;
        dragAndDropBall.xx = mouseXX + dragAndDropBall.xPlus;
        dragAndDropBall.yy = mouseYY + dragAndDropBall.yPlus;
        dragAndDropBall.x = dragAndDropBall.xx * scale;    		dragAndDropBall.y = dragAndDropBall.yy * scale;
        dragAndDropBall.vx = .6*(mouseXX-mx)/dt/fps;   dragAndDropBall.vy = .6*(mouseYY-my)/dt/fps;
        mx = mouseXX;    my = mouseYY;
    }

    function refreshMouseCoords(e){             // процедура обновляет координаты в переменных mouseX и mouseY
        var rect = canvas.getBoundingClientRect();
        mouseX = e.clientX - rect.left;
        mouseY = e.clientY - rect.top;
    }

    // Работа с массивом

    var balls = [];                                 // массив шаров
    var addNewBall =  function(x, y){
        var b = [];

        b.x = x;               b.y = y;             // экранные координаты шара
        b.xx = x / scale;      b.yy = y / scale;    // расчетные координаты шара
        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++){
                // расчет взаимодействия производится со всеми следующими шарами в массиве,
                // чтобы не считать каждое взаимодействие дважды
                for (var ii=i+1; ii < balls.length; ii++) {

                    var b1 = balls[i];      var b2 = balls[ii];             // ссылки на шары
                    var rx = b1.xx - b2.xx;   var ry = b1.yy - b2.yy;       // вектор смотрит на первый шар (b1)
                    var r2 = rx*rx + ry*ry;                                 // квадрат расстояния между шарами
                    if (r2 > aCut2) continue;                               // проверка на радиус обрезания
                    if (r2 < arCoeff*a2) r2 = arCoeff*a2;                   // проверка на минимальный радиус

                    var s2 = a2/r2;         var s4 = s2*s2;                 // ___в целях оптимизации___

                    var F = LJCoeff * s4 * s4 * (s4 * s2 - 1);              // сила взаимодействия Леннарда-Джонса
                    var Fx = F*rx;          var Fy = F*ry;

                    b1.fx += Fx;            b1.fy += Fy;
                    b2.fx -= Fx;            b2.fy -= Fy;
                }

                if (balls[i] == dragAndDropBall) continue;  // если шар схвачен курсором - его вз. со стенами и перемещение не считаем

                if (balls[i].yy + r > h) { balls[i].fy += -Cwall * (balls[i].yy + r - h) - B1 * balls[i].vy; }
                if (balls[i].yy - r < 0) { balls[i].fy += -Cwall * (balls[i].yy - r) - B1 * balls[i].vy;}
                if (balls[i].xx + r > w) { balls[i].fx += -Cwall * (balls[i].xx + r - w) - B1 * balls[i].vx; }
                if (balls[i].xx - r < 0) { balls[i].fx += -Cwall * (balls[i].xx - r) - B1 * balls[i].vx; }

                balls[i].vx += balls[i].fx / m * dt;        balls[i].vy += balls[i].fy / m * dt;
                balls[i].xx += balls[i].vx * dt;    	    balls[i].yy += balls[i].vy * dt;

                balls[i].x = balls[i].xx * scale;    		balls[i].y = balls[i].yy * scale;
            }
        }
    }

    // Рисование

    var rScale13 = r * scale*1.3;           // ___в целях оптимизации___
    var rScaleShift = r * scale/5.0;        // ___в целях оптимизации___
    function draw(){
        context.clearRect(0, 0, w*scale, h*scale);          // очистить экран
        for (var i = 0; i < balls.length; i++){
            var b = balls[i];

            // расчет градиента нужно проводить для каждого шара
            var gradient=context.createRadialGradient(b.x, b.y, rScale13, b.x-rScaleShift, b.y+rScaleShift,0);
            gradient.addColorStop(0,"#0000bb");
            gradient.addColorStop(1,"#44ddff");
            context.fillStyle=gradient;

            context.beginPath();
            context.arc(b.x, b.y, r*scale,0,2*Math.PI);
            context.closePath();
            context.fill();
        }
    }

    // Запуск системы

    addNewBall(xStart * scale, yStart * scale);
    setInterval(control, 1000/fps);

}

Файл "Balls_v4_release.html"

<!DOCTYPE html>
<html>
<head>
    <title>Simple Mechanics</title>
    <script src="Balls_v4_release.js"></script>
</head>
<body>
    <canvas id="canvasMech" width="800" height="600" style="border:1px solid #000000;"></canvas>
    <script type="text/javascript">MainMech(document.getElementById('canvasMech'));</script>
</body>
</html>

</toggledisplay>