JavaScript. Подробное руководство, 6-е издание, стр. 358

<b>polygon(c, 5, 255, 55, 50);                  // Пятиугольник</b>

<b>polygon(c, 6, 365, 53, 50, Math.PI/6);       // Шестиугольник</b>

<b>polygon(c, 4, 365, 53, 20, Math.PI/4, true); // Квадрат в шестиугольнике</b>

<b>// Установить некоторые свойства, определяющие внешний вид рисунка </b>

<b>c.fillStyle = &quot;ftссс&quot;;  // Светло-серый фон внутренних областей</b>

<b>с.strokeStyle = &quot;#008&quot;; // темно-синие контуры</b>

<b>с.lineWidth = 5;        // толщиной пять пикселов.</b>

<b>// Нарисовать все многоугольники (каждый создается в виде отдельного фрагмента контура)</b>

<b>// следующими вызовами</b>

<b>с.fill();   // Залить фигуры</b>

<b>c.stroke(); // И нарисовать контур</b>

Обратите

внимание
, что в этом примере внутри шестиугольника рисуется квадрат. Квадрат и шестиугольник являются отдельными фрагментами контура, но они перекрываются. Когда это происходит (или когда один фрагмент контура пересекается с самим собой), элементу
<b>&lt;canvas&gt;</b>
приходится выяснять, какая область является внутренней для фрагмента контура, а какая - внешней. Для этого элемент
<b>&lt;canvas&gt;</b>
использует алгоритм, известный как «правило ненулевого числа оборотов» («nonzero winding rule»). В данном случае внутренняя область квадрата не заливается светло-серым цветом, потому что квадрат и шестиугольник рисовались в противоположных направлениях: вершины шестиугольника соединялись линиями в направлении по часовой стрелке, а вершины квадрата - против часовой стрелки. Если бы рисование квадрата также выполнялось в направлении по часовой стрелке, метод
<b>fill()</b>
залил бы внутреннюю область квадрата.

Правило ненулевого числа оборотов

Чтобы определить, находится ли точка Р внутри контура, используя правило правило ненулевого числа оборотов, представьте луч, исходящий из точки Р в любом направлении и уходящий в бесконечность (или, если говорить практическим языком, до некоторой точки за пределами фигуры, образуемой рассматриваемым контуром). Теперь инициализируйте счетчик нулевым значением и проследите за всеми пересечениями контура с лучом. Каждый раз, когда луч пересекает контур, рисуемый в направлении по часовой стрелке, увеличивайте счетчик на единицу. Каждый раз, когда луч пересекает контур, рисуемый в направлении против часовой стрелки, уменьшайте счетчик на единицу. Если после учета всех пересечений в счетчике получается значение, не равное нулю, следовательно, точка Р находится внутри контура. Если счетчик оказывается равным нулю, следовательно, точка Р находится снаружи.

***********************************************

21.4.2. Графические атрибуты

В примере 21.4 устанавливаются свойства

<b>fillStyle, strokeStyle</b>
и
<b>lineWidth</b>
объек; та контекста элемента
<b>&lt;canvas&gt;.</b>
Эти свойства являются графическими атрибутами, определяющими цвет, используемый методом
<b>fill();</b>
цвет, используемый методом
<b>stroke();</b>
и толщину линий, рисуемых методом
<b>stroke().</b>
Обратите внимание, что эти параметры не передаются методам
<b>fill()</b>
и
<b>stroke(),</b>
а являются общими графическими свойствами элемента
<b>&lt;canvas&gt;.</b>
Если определяется метод, рисующий некоторую фигуру, который не устанавливает эти свойства, программа, использующая его, сможет сама определять цвет фигуры, устанавливая свойства
<b>strokeStyle</b>
и
<b>fillStyle</b>
перед вызовом этого метода. Такое отделение графических свойств от команд рисования является фундаментальной особенностью прикладного интерфейса объекта
<b>Canvas</b>
и сродни отделению представления от содержимого, достигаемого за счет применения таблиц стилей CSS к HTML-документам.

Прикладной интерфейс объекта

<b>Canvas</b>
содержит 15 свойств графических атрибутов в объекте
<b>CanvasRenderingContext2D</b>
. Эти свойства перечислены в табл. 21.1 и подробно описываются в соответствующих разделах ниже.

JavaScript. Подробное руководство, 6-е издание - i_058.jpg

Так как прикладной интерфейс объекта Canvas определяет графические атрибуты в объекте контекста, может появиться идея вызвать метод

<b>getContext()</b>
несколько раз, чтобы получить несколько объектов контекста. Если бы это удалось, можно было бы определить для каждого из них различные атрибуты и использовать их как различные кисти разного цвета и разной толщины. К сожалению, элемент
<b>&lt;canvas&gt;</b>
нельзя использовать таким способом. Каждый элемент
<b>&lt;canvas&gt;</b>
имеет только один объект контекста, и каждый вызов метода
<b>getContext()</b>
возвращает один и тот же объект
<b>CanvasRenderingContext2D</b>
.

Тем не менее, несмотря на то что прикладной интерфейс объекта

<b>Canvas</b>
позволяет определить только один набор графических атрибутов, он предусматривает возможность сохранять текущие графические свойства, чтобы их можно было изменить и позднее легко восстановить прежние значения. Метод
<b>save()</b>
помещает текущие значения графических свойств в стек. Метод restore() выталкивает их со стека и восстанавливает самые последние сохраненные значения. В множество сохраняемых свойств входят все свойства, перечисленные в табл. 21.1, а также текущее преобразование системы координат и область отсечения (обе особенности рассматриваются ниже). Важно отметить, что текущее определение контура и координаты текущей точки не входят в множество сохраняемых графических свойств и не могут сохраняться и восстанавливаться.

Если вам потребуется больше гибкости, чем может обеспечит простой стек графических свойств, вы можете определить вспомогательные методы, такие как в примере 21.5.

Пример 21.5. Утилиты управления графическими свойствами

<b>// Восстанавливает последние сохраненные значения графических свойств,</b>