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

Материал из Department of Theoretical and Applied Mechanics
Перейти к: навигация, поиск
Виртуальная лаборатория > Принцип копирования

Если какое либо сгенерированное изображение в программе повторяется много раз, то имеет смысл не рисовать его каждый раз заново, а копировать единожды нарисованное. Это позволит в несколько раз увеличить скорость работы программы. Далее будет продемонстрировано применение данного принципа при рисовании на 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) в нужные позиции.

Скачать программу: 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>