Принцип копирования — различия между версиями
Денис (обсуждение | вклад) (Новая страница: «Виртуальная лаборатория > Принцип копирования <HR> Category: Виртуальная лаборатория») |
Денис (обсуждение | вклад) |
||
Строка 1: | Строка 1: | ||
[[Виртуальная лаборатория]] > [[Принцип копирования]] <HR> | [[Виртуальная лаборатория]] > [[Принцип копирования]] <HR> | ||
+ | Если какое либо сгенерированное изображение в программе повторяется много раз, то имеет смысл не рисовать его каждый раз заново, а копировать единожды нарисованное. Это позволит в несколько раз увеличить скорость работы программы. Далее будет продемонстрировано применение данного принципа при рисовании на HTML5 Canvas с помощью Javascript. | ||
+ | |||
+ | Для начала создадим новый элемент Canvas - "buffer". Он будет находиться во внутренней памяти программы, без вывода на экран. | ||
+ | <source lang="javascript" first-line="1"> | ||
+ | var buffer = document.createElement('canvas'); | ||
+ | </source> | ||
+ | |||
+ | Далее нарисуем в "buffer" изображение, которое хотим копировать: | ||
+ | <source lang="javascript" first-line="1"> | ||
+ | var buf_ctx = buffer.getContext('2d'); | ||
+ | buf_ctx... // здесь рисуется нужное нам изображение | ||
+ | </source> | ||
+ | |||
+ | Чтобы скопировать полученное изображение из внутренней памяти на экран, воспользуемся функцией drawImage(): | ||
+ | <source lang="javascript" first-line="1"> | ||
+ | context.drawImage(buffer, x, y); | ||
+ | </source> | ||
+ | где "x", "y" - координаты, в которые копируется изображение. | ||
+ | |||
+ | |||
+ | В программе ниже демонстрируется применение описанного принципа. В фукнции '''draw_star('''''color''''')''' генерируется изображение звезды с градиентом, ''color'' - цвет звезды. Функция '''generate_star_pics()''' с помощью '''draw_star('''''color''''')''' генерирует два различных типа звезд - фиолетовые (название буфера - '''stars1''') и желтые ('''stars2'''). | ||
+ | |||
+ | После задается треугольная решетка, и запускается цикл '''setInterval('''''tick, 1000 / fps''''')''', который рассчитывает изменение положения узлов в решетке, и запускает функцию рисования системы - '''draw()'''. Данная функция копирует изображение звезды определенного цвета (определяемого параметром ''type'') в нужные позиции. | ||
+ | |||
+ | <addscript src=CopyPic_v1_release/> | ||
+ | <htmlet nocache="yes">CopyPic_v1_release_TM</htmlet> | ||
+ | |||
+ | Скачать программу: [[Медиа:CopyPic_v1_release.zip|CopyPic_v1_release.zip]] | ||
+ | Текст программы на языке JavaScript (разработчик [[Цветков Денис]]): <toggledisplay status=hide showtext="Показать↓" hidetext="Скрыть↑" linkstyle="font-size:default"> | ||
+ | Файл '''"CopyPic_v1_release.js"''' | ||
+ | <source lang="javascript" first-line="1"> | ||
+ | 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); | ||
+ | |||
+ | } | ||
+ | </source> | ||
+ | Файл '''"CopyPic_v1_release.html"''' | ||
+ | <source lang="html" first-line="1"> | ||
+ | <!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> | ||
+ | </source> | ||
+ | </toggledisplay> | ||
[[Category: Виртуальная лаборатория]] | [[Category: Виртуальная лаборатория]] |
Версия 14:26, 14 июля 2014
Виртуальная лаборатория > Принцип копированияЕсли какое либо сгенерированное изображение в программе повторяется много раз, то имеет смысл не рисовать его каждый раз заново, а копировать единожды нарисованное. Это позволит в несколько раз увеличить скорость работы программы. Далее будет продемонстрировано применение данного принципа при рисовании на HTML5 Canvas с помощью Javascript.
Для начала создадим новый элемент Canvas - "buffer". Он будет находиться во внутренней памяти программы, без вывода на экран.
var buffer = document.createElement('canvas');
Далее нарисуем в "buffer" изображение, которое хотим копировать:
var buf_ctx = buffer.getContext('2d');
buf_ctx... // здесь рисуется нужное нам изображение
Чтобы скопировать полученное изображение из внутренней памяти на экран, воспользуемся функцией drawImage():
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) в нужные позиции.
<addscript src=CopyPic_v1_release/>
Скачать программу: CopyPic_v1_release.zip Текст программы на языке JavaScript (разработчик Цветков Денис): <toggledisplay status=hide showtext="Показать↓" hidetext="Скрыть↑" linkstyle="font-size:default"> Файл "CopyPic_v1_release.js"
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);
}
Файл "CopyPic_v1_release.html"
<!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>
</toggledisplay>