Принцип копирования

Материал из Department of Theoretical and Applied Mechanics
Версия от 18:55, 8 марта 2015; Wikiadmin (обсуждение | вклад) (Замена текста — «</source>» на «</syntaxhighligh>»)

Перейти к: навигация, поиск
Виртуальная лаборатория > Принцип копирования

Если какое либо сгенерированное изображение в программе повторяется много раз, то имеет смысл не рисовать его каждый раз заново, а копировать единожды нарисованное. Это позволит в несколько раз увеличить скорость работы программы. Далее будет продемонстрировано применение данного принципа при рисовании на HTML5 Canvas с помощью Javascript.

Для начала создадим новый элемент Canvas - "buffer". Он будет находиться во внутренней памяти программы, без вывода на экран. <syntaxhighlight lang="javascript" line start="1" enclose="div"> var buffer = document.createElement('canvas'); </syntaxhighligh>

Далее нарисуем в "buffer" изображение, которое хотим копировать: <syntaxhighlight lang="javascript" line start="1" enclose="div"> var buf_ctx = buffer.getContext('2d'); buf_ctx... // здесь рисуется нужное нам изображение </syntaxhighligh>

Чтобы скопировать полученное изображение из внутренней памяти на экран, воспользуемся функцией drawImage(): <syntaxhighlight lang="javascript" line start="1" enclose="div"> context.drawImage(buffer, x, y); </syntaxhighligh> где "x", "y" - координаты, в которые копируется изображение.


В программе ниже демонстрируется применение описанного принципа. В фукнции draw_star(color) генерируется изображение звезды с градиентом, color - цвет звезды. Функция generate_star_pics() с помощью draw_star(color) генерирует два различных типа звезд - фиолетовые (название буфера - stars1) и желтые (stars2).

После задается треугольная решетка, и запускается цикл setInterval(tick, 1000 / fps), который рассчитывает изменение положения узлов в решетке, и запускает функцию рисования системы - draw(). Данная функция копирует изображение звезды определенного цвета (определяемого параметром type) в нужные позиции.

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

   canvas.onselectstart = function () {return false;};     // запрет выделения canvas
   // Предварительные установки
   var context = canvas.getContext("2d");                  // на context происходит рисование
   var a0 = 1;                                             // масштаб расстояния
   // *** Задание вычислительных параметров ***
   var a = 2 * a0;                         // шаг решетки
   var N = 8.46 * a;                      // количество звезд по горизонтали
   // *** Выполнение программы ***
   var fps = 60;                           // количество кадров в секунду
   var scale = canvas.height / N;          // масштабный коэффициент для перехода от расчетных к экранным координатам
   var w = canvas.width / scale;           // ширина окна в расчетных координатах
   var h = canvas.height / scale;          // высота окна в расчетных координатах
   var points = [];
   var ax = a;                             // шаг по горизонтали
   var ay = a * Math.sqrt(3) / 2;          // шаг по вертикали
   function set_triangular_lattice() {     // треугольная решетка
       for (var i = 0; i < Math.floor(w / ax) + 5; i++) {
           points[i] = [];
           for (var j = 0; j < Math.floor(h / ay) + 5; j++) {
               points[i][j] = [];
               points[i][j].x = (i - 2) * ax * scale + ax / 2 * (j % 2) * scale;
               points[i][j].y = (j - 2) * ay * scale;
               points[i][j].type = j % 2;
           }
       }
   }
   // Предварительная генерация двух разных звезд
   var stars1, stars2;
   function generate_star_pics() {
       stars1 = draw_star("#8050cc");          // фиолетовые звезды
       stars2 = draw_star("#dd9922");          // желтые звезды
   }
   function draw_star(color) {
       var buffer = document.createElement('canvas');
       var ctx = buffer.getContext('2d');
       var n = 5;                          // углов у звезды
       var r = 0.35 * a * scale;           // радиус звезды
       var fi = 0;                         // поворот звезды
       // здесь звезда рисуется на невидимый канвас, с которого потом будет копироваться на видимый
       ctx.beginPath();
       for(var i = 0; i <= (n * 2) + 1; i++) {
           var r_actual = r * (i % 2 + 1) / 2;
           var omega = 2 * Math.PI / (n * 2) * i;
           ctx.lineTo(((r_actual * Math.sin(omega + fi * 3.14 / 180)) + r), (r_actual * Math.cos(omega + fi * 3.14 / 180)) + r);
       }
       ctx.closePath();
       var gradient = context.createRadialGradient(r, r, r, r, r, 0);
       gradient.addColorStop(0, color);
       gradient.addColorStop(1, color_light(color));
       ctx.fillStyle = gradient;
       ctx.fill();
       return buffer;
   }
   function color_light(col) {             // осветление цвета для градиента, можно еще делать через схему HSV -> RGB
       var col_light = "#";
       for (var i = 1; i < col.length; i++) {
           if (parseInt(col[i], 16) + 0x8 <= 0xf) col_light += (parseInt(col[i], 16) + 0x8).toString(16);
           else col_light += "f";
       }
       return col_light;
   }
   function tick() {
       move();
       draw();
   }
   var phase1, phase2, phase3;
   var phase1_0 = 60, phase2_0 = 60, phase3_0 = 60;
   function init() {
       set_triangular_lattice();
       phase1 = phase1_0;
       phase2 = phase2_0;
       phase3 = phase3_0;
   }
   function move() {                       // перемещение звезд
       var i, j, sign, p;
       if (phase1 > 0) {
           for (i = 0; i < points.length; i++)
           for (j = 0; j < points[i].length; j++) {
               if (j % 2 == 0) sign = 1;
               else sign = -1;
               points[i][j].x = points[i][j].x + sign * ax * scale / phase1_0;
           }
           phase1--;
       } else if (phase2 > 0) {
           p = 1;
           for (j = 0; j < points[0].length; j++) {
               for (i = 0; i < points.length; i++) {
                   if (p >= 2) {
                       if (i % 2 == 0) sign = 1;
                       else sign = -1;
                   } else {
                       if (i % 2 == 1) sign = 1;
                       else sign = -1;
                   }
                   points[i][j].x = points[i][j].x + sign * ax * scale / (phase2_0 * 2);
                   points[i][j].y = points[i][j].y - sign * ay * scale / phase2_0;
               }
               if (p == 3) p = 0;
               else p++;
           }
           phase2--;
       } else if (phase3 > 0) {
           p = 0;
           for (j = 0; j < points[0].length; j++) {
               for (i = 0; i < points.length; i++) {
                   if (p >= 2) {
                       if (i % 2 == 0) sign = 1;
                       else sign = -1;
                   } else {
                       if (i % 2 == 1) sign = 1;
                       else sign = -1;
                   }
                   points[i][j].x = points[i][j].x + sign * ax * scale / (phase3_0 * 2);
                   points[i][j].y = points[i][j].y + sign * ay * scale / phase3_0;
               }
               if (p == 3) p = 0;
               else p++;
           }
           phase3--;
       } else {init()}
   }
   // Рисование
   function draw() {
       // очистка экрана
       context.fillStyle = "#000000";
       context.fillRect(0, 0, w * scale, h * scale);
       // копирование звезд в нужные позиции
       for (var i = 0; i < points.length; i++)
       for (var j = 0; j < points[i].length; j++) {
           if (points[i][j].type == 0) context.drawImage(stars1, points[i][j].x, points[i][j].y);
           else context.drawImage(stars2, points[i][j].x, points[i][j].y);
       }
       // звезда не рисуется в цикле каждый раз, когда нужна, она просто копируется в нужную точку.
   }
   generate_star_pics();               // генерация изображений звезд происходит только один раз
   init();
   setInterval(tick, 1000 / fps);

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

   <meta charset="UTF-8" />
   <title>Copy pic</title>
   <script src="CopyPic_v1_release.js"></script>

</head> <body>

   <canvas id="Copy_pic_canvas" width="500" height="500" style="border:1px solid #000000;"></canvas>
   <script type="text/javascript">var app = new Main_Copy_pic(document.getElementById('Copy_pic_canvas'));</script>

</body> </html> </syntaxhighligh> </toggledisplay>