2D преобразования в контексте элемента canvas

Автор: | 05.02.2018

Простейший пример
Матрица аффинных преобразований
Контрольные задания

Простейший пример

Ниже приведен простейший код приложения, который обеспечивает 2D-преобразования и рисование прямоугольника в Web-браузере (рис.1). В файле index.html на языке HTML определен элемент canvas – растровый холст на Web странице (прямоугольная двумерная сетка). Измерения пространственной области элемента canvas по ширине и высоте задаются в пикселах в так называемой оконной системе координат (рис.2). Верхний левый угол области Canvas имеет координаты x=0, y=0, ось X направлена вправо, ось X вниз.

Файл index.html

<html> 
     <head> 
         <script src="draw.js" type="text/javascript"> </script>
     </head> 
     <body onload="draw_b();"> 
  <canvas  id="b"  width="100" height="100" >  </canvas>
     </body> 
 </html>

Файл draw.js

var fi = 45.0;
fi = fi * Math.PI / 180.0;
var a = Math.cos (fi);        // a  d  0 /
var b = Math.sin (fi);        // b  e  0 /
var d = -Math.sin (fi);       // c  f  1 /
var e = Math.cos (fi);
var c = 10.0;
var f = 25.0;
function draw_b() {
  var b_canvas = document.getElementById("b");
  var b_context = b_canvas.getContext("2d");
  b_context.fillStyle = "#0000FF";
  b_context.fillRect(0, 0, b_canvas.width, b_canvas.height);
  b_context.fillStyle = "#FFFF00";
  //b_context.translate(c,f);
  //b_context.rotate(fi);
  //b_context.setTransform(a, b, d, e, 0, 0);
  //b_context.setTransform(1, 0, 0, 1, c, f);
  //b_context.setTransform(a, b, d, e, c, f); 
  //b_context.setTransform(a, b, d, e, c*a+f*b, c*d+f*e);
  b_context.fillRect(0, 0, 25, 10);
}

В файле draw.js на языке JavaScript получаем доступ по атрибуту id к элементу canvas и затем к объекту context, где определены методы преобразования пространства и свойства рисования. Вызов метода fillRect() рисует прямоугольник и заполняет его текущим цветом заливки. Прямоугольник задается левым верхним углом (0, 50), шириной (25) и высотой (10) в направлении слева направо и сверху вниз. Функции, которые обеспечивают геометрические преобразования, в программе закомментированы.

Взаимосвязь между  JS-кодом и  HTML -элементами реализуется через идентификаторы. В приложении описан элемент  canvas с идентификатором id=»b» и свойствами width=»100″ height=»100″. В JS коде доступ к этому элементу получаем через обращение document.getElementById(«b»).  Объект document находится в верхушке иерархии объектной модели. Через него получаем доступ к входящим объектам, их свойствам и методам.

Для тестирования приложения, которое состоит из модулей кода на языках HTML и JavaScript(JS), удобно использовать online редактор CodePen. При запуске его в WEB-браузере открываются 3 окна для ввода кода на языках HTML, CSS и JS, а внизу размещается окно для отображения результата.

Окно для CSS кода уменьшено, т.к. его нет в рассматриваемом приложении.

 

Ниже приведены результаты тестовых запусков программы с различными сочетаниями функций преобразования.

Из рисунка 3 видим, что положительное направление вращения по часовой стрелке. Результаты запуска приложения с подключением функций rotate и translate (рис.3-6) показывают, что конечный результат элементарных преобразований зависит не только от значений параметров функций (они в обоих случаях одинаковы), но и последовательности их использования. На рис. 7-8 получены соответственно аналогичные результаты для функции setTransform. Она объединяет элементарные преобразования в матрице.

Судя из рисунков 3-6 после выполнения каждой из функций (rotate или translate) каждое последующее преобразование происходит относительно новой (текущей) системы координат.

Из рисунков 7-8 мы пока ничего не можем сказать, какая система координат будет активна после выполнения функции setTransform. Для определения этого был выполнен тестовый пример. После функции setTransform вызываем функцию translate (25 0). Результат (рис.9-10) показывает, что прямоугольник передвигается в направлении вдоль оси X текущей системы координат.

Матрица аффинных преобразований

Многие веб-разработчики игнорируют матрицу аффинных преобразований, полагая её слишком сложной для понимания и используя взамен простейшие функции для трансформации типа rotate и  translate. И совершенно зря, матрица преобразований обладает широкими возможностями, вдобавок, в том или ином виде поддерживаются всеми браузерами, а значит, её применение даёт кроссбраузерный код. Сама матрица имеет размер 3х3 и в общем виде записывается так:

Роль каждого коэффициента матрицы становится понятна из следующего. Рассмотрим схему и  уравнения для пересчета координат точки в новой СК [4].

В матричном представлении уравнения запишутся так:

Обобщим формулы преобразований движения к формулам аффинного преобразования плоскости:

Однородные координаты позволяют описать преобразования матрицей размером:


Обратите внимание, что около синуса угла изменился знак, поскольку вывод системы уравнений выполнялся для системы координат, ось Y которой направлена вверх, а положительный угол против часовой стрелки.

Преимущество такого подхода (матричных формул) заключается в том, что совмещение последовательных элементарных преобразований при этом значительно упрощается.  Например,  перемещение и поворот можно записать в виде одной матрицы:

Результирующая матрица, полученная произведением двух исходных матриц преобразования, представляет собой совмещение элементарных преобразований. Независимо от количества элементарных преобразований в последовательности, можно всегда произвести совмещение так, чтобы только одна матрица 3×3 представляла всю последовательность преобразований.
Еще больший эффект использования матрицы аффинных преобразований можно получить взамен  простейших функций для трансформации при создании 3D-графики на Web-странице. Переход из одной прямолинейной координатной системы к другой описывается в общем случае системой уравнений с соответствующим матричным представлением:

Контрольные задания

  1. Запустить приложение simpleCanvas (см. код ниже). Приложение обеспечивает появление квадратиков в месте, указанном курсором мышки (рис.18).
  2. Модифицировать приложение, обеспечив появление квадратиков под углом 45 градусов (рис.19), используя функции rotate и translate. В качестве подсказки, посмотрите как решается задача поворота объекта относительно произвольной точки.
  3. Модифицировать приложение, обеспечив появление  квадратиков под углом 45 градусов (см. рис.19), используя функцию setTransform.
  4. Модифицировать приложение, обеспечив появление  квадратиков с пошаговым изменением угла при каждом click (рис.20).
  5. Модифицировать приложение, обеспечив вращение квадратика по часовой стрелке при перемещении курсора мышки (при нажатой клавише мышки) вправо и вращение квадратика против часовой стрелки при перемещении курсора мышки влево (рис.21).
  6. Курсор должен быть привязан к центру квадратика. Квадратик заменить окружностью (рис.22).

 

Приложение simpleCanvas

Файл index.html

<html> 
     <head> 
         <script src="draw.js" type="text/javascript"> </script>
     </head> 
     <body onload="init();"> 
  <canvas  id="b"  width="500" height="500" >  </canvas>
     </body> 
 </html>

Файл draw.js

var fi = -45.0;
fi = fi * Math.PI / 180.0;
var canvas;
var context;
var mouseX;
var mouseY;
function init(){
canvas = document.getElementById("b");
context = canvas.getContext("2d");
canvas.addEventListener("click", setMousePosition, false);
//canvas.addEventListener("mousemove", setMousePosition, false);
mouseX = 0;
mouseY = 0;
update();
}
function update() {
//context.clearRect(0, 0, canvas.width, canvas.height);
//context.rect(mouseX, mouseY, 100, 100,true);
context.rect(0, 0, 100, 100,true);
context.fill();
//requestAnimationFrame(update);
}

function setMousePosition(e) {
mouseX = e.clientX;
mouseY = e.clientY;
context.beginPath();
context.fillStyle = '#'+Math.random().toString(16).slice(-6);
// rotate 45 degrees clockwise
//context.rotate(fi);
//context.save();//сохраняет текущее состояние на вершине стека
context.translate(mouseX,mouseY);
context.rotate(fi);
update();
context.rotate(-fi);
context.translate(-mouseX,-mouseY);
//context.restore();//устанавливает состояние с вершины стека
}

 

 

Автор: Николай Свирневский

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *