Принцип копирования — различия между версиями

Материал из Department of Theoretical and Applied Mechanics
Перейти к: навигация, поиск
 
 
(не показано 6 промежуточных версий 2 участников)
Строка 1: Строка 1:
 
[[Виртуальная лаборатория]] > [[Принцип копирования]] <HR>
 
[[Виртуальная лаборатория]] > [[Принцип копирования]] <HR>
  
 +
Если какое либо сгенерированное изображение в программе повторяется много раз, то имеет смысл не рисовать его каждый раз заново, а копировать единожды нарисованное. Это позволит в несколько раз увеличить скорость работы программы. Далее будет продемонстрировано применение данного принципа при рисовании на HTML5 Canvas с помощью Javascript.
 +
 +
Для начала создадим новый элемент Canvas - "buffer". Он будет находиться во внутренней памяти программы, без вывода на экран.
 +
<syntaxhighlight lang="javascript" line start="1" enclose="div">
 +
var buffer = document.createElement('canvas');
 +
</syntaxhighlight>
 +
 +
Далее нарисуем в "buffer" изображение, которое хотим копировать:
 +
<syntaxhighlight lang="javascript" line start="1" enclose="div">
 +
var buf_ctx = buffer.getContext('2d');
 +
buf_ctx...        // здесь рисуется нужное нам изображение
 +
</syntaxhighlight>
 +
 +
Чтобы скопировать полученное изображение из внутренней памяти на экран, воспользуемся функцией drawImage():
 +
<syntaxhighlight lang="javascript" line start="1" enclose="div">
 +
context.drawImage(buffer, x, y);
 +
</syntaxhighlight>
 +
где "x", "y" - координаты, в которые копируется изображение.
 +
 +
 +
В программе ниже демонстрируется применение описанного принципа. В фукнции '''draw_star('''''color''''')''' генерируется изображение звезды с градиентом, ''color'' - цвет звезды. Функция '''generate_star_pics()''' с помощью '''draw_star('''''color''''')''' генерирует два различных типа звезд - фиолетовые (название буфера - '''stars1''') и желтые ('''stars2''').
 +
 +
После задается треугольная решетка, и запускается цикл '''setInterval('''''tick, 1000 / fps''''')''', который рассчитывает изменение положения узлов в решетке, и запускает функцию рисования системы - '''draw()'''. Данная функция копирует изображение звезды определенного цвета (определяемого параметром ''type'') в нужные позиции.
 +
 +
<htmlet nocache="yes">Tcvetkov/CopyPic/CopyPic_v1_release_TM</htmlet>
 +
 +
Скачать программу: [[Медиа:CopyPic_v1_release.zip|CopyPic_v1_release.zip]]
 +
<div class="mw-collapsible mw-collapsed" style="width:100%" >
 +
'''Текст программы на языке JavaScript (разработчик [[Цветков Денис]]):''' <div class="mw-collapsible-content">
 +
Файл '''"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);
 +
 +
}
 +
</syntaxhighlight>
 +
Файл '''"CopyPic_v1_release.html"'''
 +
<syntaxhighlight lang="html5" 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>
 +
</syntaxhighlight>
 +
</div>
 +
</div>
  
  
  
 
[[Category: Виртуальная лаборатория]]
 
[[Category: Виртуальная лаборатория]]

Текущая версия на 09:24, 11 марта 2015

Виртуальная лаборатория > Принцип копирования

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

Для начала создадим новый элемент Canvas - "buffer". Он будет находиться во внутренней памяти программы, без вывода на экран.

1 var buffer = document.createElement('canvas');

Далее нарисуем в "buffer" изображение, которое хотим копировать:

1 var buf_ctx = buffer.getContext('2d');
2 buf_ctx...        // здесь рисуется нужное нам изображение

Чтобы скопировать полученное изображение из внутренней памяти на экран, воспользуемся функцией drawImage():

1 context.drawImage(buffer, x, y);

где "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 (разработчик Цветков Денис):

Файл "CopyPic_v1_release.js"

  1 function Main_Copy_pic(canvas) {
  2 
  3     canvas.onselectstart = function () {return false;};     // запрет выделения canvas
  4 
  5     // Предварительные установки
  6 
  7     var context = canvas.getContext("2d");                  // на context происходит рисование
  8 
  9     var a0 = 1;                                             // масштаб расстояния
 10 
 11     // *** Задание вычислительных параметров ***
 12 
 13     var a = 2 * a0;                         // шаг решетки
 14     var N = 8.46 * a;                      // количество звезд по горизонтали
 15 
 16     // *** Выполнение программы ***
 17 
 18     var fps = 60;                           // количество кадров в секунду
 19     var scale = canvas.height / N;          // масштабный коэффициент для перехода от расчетных к экранным координатам
 20     var w = canvas.width / scale;           // ширина окна в расчетных координатах
 21     var h = canvas.height / scale;          // высота окна в расчетных координатах
 22 
 23     var points = [];
 24     var ax = a;                             // шаг по горизонтали
 25     var ay = a * Math.sqrt(3) / 2;          // шаг по вертикали
 26 
 27     function set_triangular_lattice() {     // треугольная решетка
 28         for (var i = 0; i < Math.floor(w / ax) + 5; i++) {
 29             points[i] = [];
 30             for (var j = 0; j < Math.floor(h / ay) + 5; j++) {
 31                 points[i][j] = [];
 32                 points[i][j].x = (i - 2) * ax * scale + ax / 2 * (j % 2) * scale;
 33                 points[i][j].y = (j - 2) * ay * scale;
 34                 points[i][j].type = j % 2;
 35             }
 36         }
 37     }
 38 
 39     // Предварительная генерация двух разных звезд
 40     var stars1, stars2;
 41     function generate_star_pics() {
 42         stars1 = draw_star("#8050cc");          // фиолетовые звезды
 43         stars2 = draw_star("#dd9922");          // желтые звезды
 44     }
 45     function draw_star(color) {
 46         var buffer = document.createElement('canvas');
 47         var ctx = buffer.getContext('2d');
 48 
 49         var n = 5;                          // углов у звезды
 50         var r = 0.35 * a * scale;           // радиус звезды
 51         var fi = 0;                         // поворот звезды
 52 
 53         // здесь звезда рисуется на невидимый канвас, с которого потом будет копироваться на видимый
 54         ctx.beginPath();
 55         for(var i = 0; i <= (n * 2) + 1; i++) {
 56             var r_actual = r * (i % 2 + 1) / 2;
 57             var omega = 2 * Math.PI / (n * 2) * i;
 58             ctx.lineTo(((r_actual * Math.sin(omega + fi * 3.14 / 180)) + r), (r_actual * Math.cos(omega + fi * 3.14 / 180)) + r);
 59         }
 60         ctx.closePath();
 61         var gradient = context.createRadialGradient(r, r, r, r, r, 0);
 62         gradient.addColorStop(0, color);
 63         gradient.addColorStop(1, color_light(color));
 64         ctx.fillStyle = gradient;
 65         ctx.fill();
 66 
 67         return buffer;
 68     }
 69     function color_light(col) {             // осветление цвета для градиента, можно еще делать через схему HSV -> RGB
 70         var col_light = "#";
 71         for (var i = 1; i < col.length; i++) {
 72             if (parseInt(col[i], 16) + 0x8 <= 0xf) col_light += (parseInt(col[i], 16) + 0x8).toString(16);
 73             else col_light += "f";
 74         }
 75         return col_light;
 76     }
 77 
 78     function tick() {
 79         move();
 80         draw();
 81     }
 82 
 83     var phase1, phase2, phase3;
 84     var phase1_0 = 60, phase2_0 = 60, phase3_0 = 60;
 85     function init() {
 86         set_triangular_lattice();
 87         phase1 = phase1_0;
 88         phase2 = phase2_0;
 89         phase3 = phase3_0;
 90     }
 91     function move() {                       // перемещение звезд
 92         var i, j, sign, p;
 93         if (phase1 > 0) {
 94             for (i = 0; i < points.length; i++)
 95             for (j = 0; j < points[i].length; j++) {
 96                 if (j % 2 == 0) sign = 1;
 97                 else sign = -1;
 98                 points[i][j].x = points[i][j].x + sign * ax * scale / phase1_0;
 99             }
100             phase1--;
101         } else if (phase2 > 0) {
102             p = 1;
103             for (j = 0; j < points[0].length; j++) {
104                 for (i = 0; i < points.length; i++) {
105                     if (p >= 2) {
106                         if (i % 2 == 0) sign = 1;
107                         else sign = -1;
108                     } else {
109                         if (i % 2 == 1) sign = 1;
110                         else sign = -1;
111                     }
112                     points[i][j].x = points[i][j].x + sign * ax * scale / (phase2_0 * 2);
113                     points[i][j].y = points[i][j].y - sign * ay * scale / phase2_0;
114                 }
115                 if (p == 3) p = 0;
116                 else p++;
117             }
118             phase2--;
119         } else if (phase3 > 0) {
120             p = 0;
121             for (j = 0; j < points[0].length; j++) {
122                 for (i = 0; i < points.length; i++) {
123                     if (p >= 2) {
124                         if (i % 2 == 0) sign = 1;
125                         else sign = -1;
126                     } else {
127                         if (i % 2 == 1) sign = 1;
128                         else sign = -1;
129                     }
130                     points[i][j].x = points[i][j].x + sign * ax * scale / (phase3_0 * 2);
131                     points[i][j].y = points[i][j].y + sign * ay * scale / phase3_0;
132                 }
133                 if (p == 3) p = 0;
134                 else p++;
135             }
136             phase3--;
137         } else {init()}
138     }
139 
140     // Рисование
141     function draw() {
142         // очистка экрана
143         context.fillStyle = "#000000";
144         context.fillRect(0, 0, w * scale, h * scale);
145 
146         // копирование звезд в нужные позиции
147         for (var i = 0; i < points.length; i++)
148         for (var j = 0; j < points[i].length; j++) {
149             if (points[i][j].type == 0) context.drawImage(stars1, points[i][j].x, points[i][j].y);
150             else context.drawImage(stars2, points[i][j].x, points[i][j].y);
151         }
152         // звезда не рисуется в цикле каждый раз, когда нужна, она просто копируется в нужную точку.
153     }
154 
155     generate_star_pics();               // генерация изображений звезд происходит только один раз
156     init();
157     setInterval(tick, 1000 / fps);
158 
159 }

Файл "CopyPic_v1_release.html"

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="UTF-8" />
 5     <title>Copy pic</title>
 6     <script src="CopyPic_v1_release.js"></script>
 7 </head>
 8 <body>
 9     <canvas id="Copy_pic_canvas" width="500" height="500" style="border:1px solid #000000;"></canvas>
10 
11     <script type="text/javascript">var app = new Main_Copy_pic(document.getElementById('Copy_pic_canvas'));</script>
12 </body>
13 </html>