Использование массивов при построении изображений

Понятие массива

При алгоритмическом построении изображений обязательно необходим навык работы с массивами. Массивы в графике используются повсеместно. Точки, ребра, грани фигур, цвета, текстуры и даже само поле для рисования - это все массивы.
Массив — структура данных, хранящая набор значений, идентифицируемых по индексу, принимающих однотипные значения из некоторого заданного непрерывного диапазона. Живая иллюстрация массива приведена на рисунке 1.


Рис.1. Пример массива, где каждый элемент массива (кот) имеет свой индекс (номер коробки).

Обращаться к элементам этого массива можно по номерам. Например, накормить кота из третьей коробки и отнести к ветеринару кота из пятой коробки. Аналогично и в программировании графики: узнать координаты пятой точки многоугольника или сдвинуть 7 вершину.

Итак, чтобы использовать массив необходимо его объявить и наполнить некоторыми значениями. В JavaScript это можно сделать одной командой:

var cats = ['Васька', 'Черныш', 'Мурзик', 'Барсик', 'Рыжик'];

Или это же действие можно выполнить по-другому:

var cats = [];
cats[0]='Васька';
cats[1]='Черныш';
cats[2]='Мурзик';
cats[3]='Барсик';
cats[4]='Рыжик';

Методы работы с массивом

Однако в JavaScript массив - это не просто набор проиндексированных значений, это объект, который имеет целый набор свойств и методов, позволяющих выполнять ряд действий над хранящимися в нем элементами.

Длина массива:
Общее число элементов массива содержится в его свойстве length.

var cats = ['Васька', 'Черныш', 'Мурзик', 'Барсик', 'Рыжик'];
console.log (cats.length); // В консоли браузера отобразится 5

Оперируя свойством length можно сокращать количество элементов массива или даже обнулить его:

var cats = ['Васька', 'Черныш', 'Мурзик', 'Барсик', 'Рыжик'];
cats.length = 2; // Остался массив из 2-х элементов ['Васька', 'Черныш']
cats.length = 0; // Массив очищен

Добавление элемента в конец массива:
Метод push() добавляет элемент в конец массива:

var cats = ['Васька', 'Черныш', 'Мурзик', 'Барсик', 'Рыжик'];
cats.push('Малыш'); // ['Васька', 'Черныш', 'Мурзик', 'Барсик', 'Рыжик', 'Малыш']

Удаление последнего элемента из массива:
Метод pop() удаляет последний элемент из массива:

var cats = ['Васька', 'Черныш', 'Мурзик', 'Барсик', 'Рыжик'];
var myCat=cats.pop(); // Переменная myCat='Рыжик'. Массив уменьшен ['Васька', 'Черныш', 'Мурзик', 'Барсик']

Удаление первого элемента массива:
Метод shift() извлекает и удаляет первый элемент из массива:

var cats = ['Васька', 'Черныш', 'Мурзик', 'Барсик', 'Рыжик'];
var myCat=cats.shift(); // Переменная myCat='Васька'. Массив уменьшен ['Черныш', 'Мурзик', 'Барсик', 'Рыжик']

Добавление первого элемента массива:
Метод unshift() добавляет новый элемент в начало массива:

var cats = ['Васька', 'Черныш', 'Мурзик', 'Барсик', 'Рыжик'];
cats.unshift('Малыш'); // ['Малыш', 'Васька', 'Черныш', 'Мурзик', 'Барсик', 'Рыжик']

Копирование массива:
Метод slice() позволяет скопировать один массив в другой, а также его часть:

var cats = ['Васька', 'Черныш', 'Мурзик', 'Барсик', 'Рыжик'];
var darkCats = cats.slice(0,3); // ['Васька', 'Черныш', 'Мурзик', 'Барсик']

Поиск индекса элемента:
Методы indexOf() и lastIndexOf() возвращают индекс первого и последнего включения элемента в массиве.

var cats = ['Васька', 'Черныш', 'Мурзик', 'Барсик', 'Рыжик', 'Мурзик'];
console.log(cats.indexOf('Мурзик')); // В консоли браузера отобразится 2
console.log(cats.lastIndexOf('Мурзик')); // В консоли браузера отобразится 5

Посмотреть все возможности работы с массивом на JavaScript можно на странице документации MDN.

Примечание: Массивы в JavaScript очень гибкие и позволяют нестандартно работать с ними. Например, выполнить вот такое действие:

var cats = ['Васька', 'Черныш', 'Мурзик', 'Барсик', 'Рыжик', 'Мурзик'];
cats[9999]=10;

Т.е. сделать пропуск большого числа индексов и присвоить целое число вместо строки. Это возможно, потому что в основе массива лежит объект. Можем присвоить ему любые свойства. Однако, в этом случае движок будет работать с массивом, как с обычным объектом и все способы оптимизации, используемые для массивов, будут отключены, что приведет к потере скорости выполнения алгоритма.

Перебор элементов массива

Классический способ перебора элементов массива - это пройти циклом for по цифровым индексам и получить значения:

var cats = ['Васька', 'Черныш', 'Мурзик', 'Барсик', 'Рыжик'];
 
for (var i = 0; i < cats.length; i++) {
  console.log( cats[i]+' сидит в ' + i + ' коробке' );
}

Но есть и более современные и оптимизированные способы. Например, цикл for..of

var cats = ['Васька', 'Черныш', 'Мурзик', 'Барсик', 'Рыжик'];
 
for (var cat of cats) {
  console.log( cat + ' это кот.');
}

Цикл for..of не предоставляет доступа к номеру текущего элемента, только к его значению, но в большинстве случаев этого достаточно. А также это короче.

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

var cats = ['Васька', 'Черныш', 'Мурзик', 'Барсик', 'Рыжик'];
 
cats.forEach(cat => console.log( cat + ' - это кот.' ));
 

Метод forEach() в качестве параметра принимает стрелочную функцию, которая имеет один параметр - текущий перебираемый элемент массива. А в теле функции над этим элементом можно выполнить различные операции.

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

var cats = ['Васька', 'Черныш', 'Мурзик'];
 
var nameCats = cats.map(cat => 'Кот ' + cat);  // Новый массив ['Кот Васька', 'Кот Черныш', 'Кот Мурзик']

Многомерные массивы

JavaScript не поддерживает «настоящие» многомерные массивы, но позволяет неплохо имитировать их при помощи массивов из массивов. Для доступа к элементу данных в массиве массивов достаточно дважды использовать оператор [].

Задать матрицу на JS можно следующим образом:

var matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
];
 
console.log( matrix[1][1] ); // 5, центральный элемент

Массивы объектов

При работе с графикой хранить в массиве только строки или целые числа недостаточно, ведь многие графические элементы представляют собой объекты, описываемые целым рядом свойств. Самые простые примеры - это точка, цвет, вектор, треугольник. Для создания объектов используется специальный синтаксис:

var nameobj = {key1: value1, key2: value2, ...};

Пример определения и использования объектов при построении прямоугольника:

var obj = document.getElementById('drawing'); 
if (obj.getContext){ 
  var ctx = obj.getContext('2d');
  // Определение точек
  var point1 = {x: 10, y: 10, color: "#FF0000"}; 
  var point2 = {x: 100, y: 50, color: "#FF0000"};
  // Обращение к свойствам объекта
  ctx.fillStyle = point1.color;
  ctx.fillRect (point1.x, point1.y, point2.x, point2.y);
}

Сложные графические модели представляют массивы таких объектов. Например, растровая картинка состоит из точек, многоугольник состоит из линий (или векторов), 3d модель - из треугольников и т.п. Соответственно, при построении изображений в графике приходится большей частью работать с массивами объектов.

Все что было сказано выше про массивы полностью соответствует и массивам объектов с учетом того, что сам объект это выражение в фигурных скобках. Таким образом определить массив объектов мы можем несколькими способами:

var points = [
   {x:10, y:10, color: "#FF0000"},
   {x:20, y:10, color: "#00FF00"},
   {x:20, y:20, color: "#0000FF"} ];

или

  var points = [];
  points[0] = {x:10, y:10, color: "#FF0000"};
  points[1] = {x:20, y:10, color: "#00FF00"};
  points[2] = {x:20, y:20, color: "#0000FF"};

или

  var points = [];
  points.push({x:10, y:10, color: "#FF0000"});
  points.push({x:20, y:10, color: "#00FF00"});
  points.push({x:20, y:20, color: "#0000FF"});

Обращаться к конкретным свойствам объекта можно так:

 points[0].x = 100;
 points[0].y = 100;

Пример построения многоугольника, заданного массивом точек:

var points = [
   {x:10, y:10},
   {x:200, y:100},
   {x:200, y:200} ];
 
var obj = document.getElementById('drawing'); 
if (obj.getContext){ 
  var ctx = obj.getContext('2d');  
  ctx.beginPath();
  ctx.moveTo(points[0].x, points[0].y);
  for (var point of points) ctx.lineTo(point.x, point.y);
  ctx.closePath();
  ctx.stroke();
}