15. ГРАФИКА. УНИВЕРСАЛЬНЫЙ ВАРИАНТ

Команда #g позволяет реализовать графику на экране компьютера, которая соответствовала первым графическим пакетам, использовавшимся на старых компьютерах с плохим разрешением экрана. По этой причине у нее мало возможностей для создания полноценного рисунка типографского качества. Линии не имеют толщины и структуры. Масштабирование и трансляция в языке Java отсутствуют и сделано в ACL в ограниченном объеме. Совсем нет вращений и других преобразований. В данном разделе представлена другая команда #eg (enhanced graphics), которая базируется на пакете языка Java (graphics2D). Этот пакет, в свою очередь, разработан в русле современных идей, развитых главным образом в языке постскрипт. Он сложнее для понимания, но имеет очень широкие возможности для создания рисунка высокого качества.

Прежде, чем перейти к описанию операций данной команды, необходимо обсудить основные концепции создания рисунка в этой технике. Любой объект на рисунке представляет собой область. Эта область имеет контур (границу) и внутренность. Соответственно все операции разделяются на две части, а именно, описание границы (контура) и способа заполнения области. Кроме того, существует большой набор универсальных преобразований заданного контура, аналогичных преобразованию пространства точек. Эти преобразования называются афинными преобразованиями. Трансляция, масштабирование, вращение являются частыми случаями афинных преобразований. В языке ACL техника афинных преобразований реализована лишь частично, а именно указанными частными случаями, хотя над готовыми рисунками можно выполнить и полный набор.

Для удобства программирования процедура задания контура объекта выделена в отдельную команду #path или короче #pat. Эта команда задает контур и запоминает его в массиве контуров под соответствующим номером. А команда #eg просто использует уже готовые контуры по номеру. Поэтому сначала познакомимся с командой #path. Она позволяет определить форму графического объекта как сложную линию для последующего рисования. Для этой цели команда запоминает сформированную линию как объект path в памяти компьютера. Каждый такой объект имеет номер в массиве path. Емкость массива равна 100. Path может быть сформирован одной или несколькими операциями. В названии операции достаточно оставить только первые 3 буквы.

Есть еще одно важное отличие универсального пакета. Он использует не целые координаты (пикселы экрана), а вещественные координаты. При этом существует возможность автоматически масштабировать рисунок при показе на экран или печать на принтере под разрешение устройства. Утверждается, что при выводе рисунка на экран координаты соответствуют пикселам для экрана с разрешением 800*600. Однако при создании рисунка, можно для простоты считать, что координаты соответствуют пикселам. Если это не подойдет, то рисунок можно без труда промасштабировать как целое на любой размер. Для нас важно, что координаты не целые переменные и значит не могут быть заданы параметрами, а только элементами вещественного массива r().

Еще одно замечание. Здесь все координаты отсчитываются не от левого нижнего угла области графика, а от левого верхнего угла вниз. В команде #g преобразование координат происходило автоматически и это сделано только в языке ACL, а здесь это невозможно из-за сложности афинных преобразований. Поэтому преобразование координат, если это необходимо, следует выполнять уже в ACL программе. Перейдем к описанию операций.

.

 #pat [op=open; b=i;]

Эта операция открывает новый path и устанавливает курсор в точку с координатами (x,y) где x=r(i), y=r(i+1).

.

 #pat [op=cont; n=;]

Эта операция позволяет продолжить path записанный в память с номером [n]. То есть она открывает не пустой path, а уже с имеющимися элементами.

.

 #pat [op=close; n=; mo=;]

Эта операция записывает path в память компьютера как элемент с номером [n]. Если [mo=1;], то path будет замнут, то есть последняя точка соединится с первой прямолинейным отрезком.

.

 #pat [op=line; b=i;]

Эта операция добавляет к текущему path прямолинейный отрезок из текущей точки в точку (x,y) где x=r(i), y=r(i+1).

.

 #pat [op=quad; b=i;]

Эта операция добавляет к текущему path квадратичный отрезок из текущей точки в точку (x2,y2). Точка (x1,y1) используется для определения касательной на концах кривой линии. А именно, прямолинейный отрезок (тек. точка)-(x1,y1) будет касательной в тек. точке, а прямолинейный отрезок (x1,y1)-(x2,y2) будет касательной в точке (x2,y2). Здесь x1=r(i), y1=r(i+1), x2=r(i+2), y2=r(i+3).

.

 #pat [op=cub; b=;]

Эта операция добавляет к текущему path кубичный отрезок из текущей точки в точку (x3,y3). Точки (x1,y1) и (x2,y2) используются для определения касательных на концах кривой линии. А именно, прямолинейный отрезок (тек. точка)-(x1,y1) будет касательной в тек. точке, а прямолинейный отрезок (x2,y2)-(x3,y3) будет касательной в точке (x3,y3). Здесь x1=r(i), y1=r(i+1), x2=r(i+2), y2=r(i+3), x3=r(i+4), y3=r(i+5).

.

 #pat [op=app; n=; mo=;]

Эта операция позволяет пользователю добавить записанный path с номером [n] к текущему path. Если [mo=1;] текущий path будет соединен с записанным прямолинейным отрезком.

.

 #pat [op=rect; b=i; n=;]

Эта операция сразу создает новый path и запоминает его с номером [n]. При этом path представляет собой прямоугольник определенный x,y,w,h где x=r(i), y=r(i+1), w=r(i+2), h=r(i+3). Здесь (x,y) -- координаты левого верхнего угла, w -- ширина, h -- высота.

.

 #pat [op=ell; b=i; n=;]

Эта операция сразу создает новый path и запоминает его с номером [n]. При этом path представляет собой эллипс, вписанный в прямоугольник, определенный x,y,w,h где x=r(i), y=r(i+1), w=r(i+2), h=r(i+3). Здесь (x,y) -- координаты левого верхнего угла, а w -- ширина, h -- высота.

.

 #pat [op=arc; b=i; n=; mo=;]

Эта операция сразу создает новый path и запоминает его с номером [n]. При этом path представляет собой часть эллипса, вписанного в прямоугольник, определенный x,y,w,h, где x=r(i), y=r(i+1), w=r(i+2), h=r(i+3). Здесь (x,y) -- координаты левого верхнего угла, w -- ширина, h -- высота. Кроме того, r(i+4) задает начальный угол в радианах, r(i+5) задает угловую ширину. Параметр [mo] = 0,1,2 определяет тип объекта. Если [mo=0;], дуга открыта. Если [mo=1;], дуга есть хорда, если [mo=2;], дуга есть кусок пирога. Как выглядит каждая модификация можно узнать эмпирически с помощью специальной ACL-программы, которую каждый может написать себе сам.

Команда #path позволяет сгенерировать до 100 контуров, которые затем могут быть многократно использованы на рисунке. Но перед рисованием, хотя бы один контур должен быть задан, исходно они отсутствуют. Что касается самой команды рисования #eg, то она в основном определяет условия рисования, то есть преобразования координат рисунка и так далее и способ рисования, то есть линия или заполненная область. В названии операции достаточно писать только первые 3 буквы. Как уже отмечалось, данная команда использует вещественные координаты, которые могут быть математически преобразованы операциями преобразования. Поэтому эти координаты являются элементами реального массива. Кроме того, данная команда использует объект path как комплексную траекторию которая описывает форму графического объекта. Замкнутый path может быть заполнен или нарисован как кривая линия, имеющая толщину и структуру. Заполнение может быть сделано сплошным цветом, градиентом цвета и текстурой. Кроме того замкнутый path может определять область, которая только вырезает фрагмент картинки, а все детали вне этой области становятся невидимыми. Такая процедура называется клип (clipping). Рассмотрим какие операции имеет эта команда.

.

 #eg [op=open; twi=; the=; col=;]

Эта операция открывает новый график для рисования. Параметры [twi] и [the] определяют полную ширину и высоту графика в пикселах экрана. Область графика заполняется цветом с номером [col]. Цвета определяются в массиве цветов от 1 до 256. Этот массив определяется командой #color. Положение всех элементов на графике отсчитывается от левого верхнего угла этого прямоугольника. Помните, что операции выполняются с так называемыми координатами пользователя, которые потом масштабируются на поверхность экрана или бумаги принтера. Для экрана координаты пользователя соответствуют 72 на дюйм (600 на 21.2 cm). Это значение соотвествует размеру картинки на экране с разрешением 800*600 . Однако пользовательские координаты могут быть очень просто промасштабированы на любое другое разрешение.

.

 #eg [op=cont; n=;]

Эта операция позволяет продолжить рисование графика, который был предварительно сохранен в памяти компьютера с номером [n]. Она просто открывает картинку для рисования на ней объектов.

.

 #eg [op=close; c=; sav=; form=;] FT

Эта операция закрывает рисование и сохраняет картинку соответсвенно значению параметра [sav] аналогично команде #w. А именно, если [sav] > 0, то картинка спасается в памяти компьютера с номером [sav]. Максимальный возможный номер 100. Если [sav=0;], картинка будет показана на экране как кнопка в отдельном окне по центру экрана. В этом случае, если [c=0;], программа работает дальше, а если [c=1;], то программа ждет, когда пользователь закроет окно. Если [sav=-1;], то картинка будет показана и одновременно будет записана в файл в текущую рабочую папку, имя файла без расширения задается параметром [form]. Если [sav=-2;], картинка только спасается в файл. Аргумент FT задает заголовок окна в том случае, когда картинка показывается на экране. Если картинка показана на экране с [c=1;], то модификатор будет возвращен в параметр s(1) как значение от 16 до 27 (см. подробности в описании команды #w в разделе 13). Также работает параметр [emp]. Если он равен 0, то выполняется старый режим и картинка размещается в центре экрана, а если 1, то дополнительно параметры [xp] и [yp] указывают координаты левого верхнего угла окна относительно левого верхнего угла экрана.

.

 #eg [op=trans; b=i;]

Эта операция устанавливает режим перемещения всех последующих координат на вектор (x,y) где x=r(i), y=r(i+1). Так как все вертикальные координаты отсчитываются от верхней границы вниз, то для подъема объекта при трансляции необходимо задавать отрицательную y-компоненту.

.

 #eg [op=rotate; b=i;]

Эта операция устанавливает режим вращения всех последующих объектов на угол a=r(i) в радианах. Центр вращения исходно есть точка (0,0) но может быть изменен операцией трансляции.

.

 #eg [op=scale; b=i;]

Эта операция устанавливает режим масштабирования всех последующих объектов с множителями sx вдоль оси x и sy вдоль оси y где sx=r(i), sy=r(i+1).

.

 #eg [op=shear; b=i;]

Эта операция устанавливает режим shear преобразования (сдвиг верхней точки горизонтально на x и правой точки вертикально на y) где x=r(i), y=r(i+1).

.

 #eg [op=clip; n=;]

Эта операция устанавливает режим обрезания деталей всех последующих объектов, расположенных вне заданного замкнутого пути, который определен как path с номером [n].

.

 #eg [op=paint; col=; mo=; b=i; c=; sty=;]

Эта операция задает способ закрашивания областей. Возможны три модификации, определяемые параметром [mo]. Если [mo=1;], закрашивание делается сплошным цветом, который определяется элементом массива цветов с индексом, равным значению параметра [col]. Если [mo=2;], то закрашивание будет производиться градиентом цвета, который задается двумя точками и двумя цветами. Цвета задаются индексами [col] и [col]+1. Координаты точек x1, y1, x2, y2 задаются как x1=r(i), y1=r(i+1), x2=r(i+2), y2=r(i+3),. В этом случае используется дополнительно параметр [sty]. Если [sty=0;], то заданные точки определяют область перехода цветов, а за пределами этой области закраска однородная. Если [sty=1;], то заданные точки определяют половину периода. То есть за пределами этой области цвета продолжают периодически изменяться от одного цвета до другого. Наконец если [mo=3;], заполнение делается текстурой, которая определяется как много копий картинки, записанной в памяти компьютера с номером, задаваемым параметром [c]. Параметр [c] используется потому, что [n] необходим для других целей.

.

 #eg [op=fill; n=;]

Эта операция реально создает объект заполнением замкнутой области из path записанного в памяти компьютера с номером [n]. Рисование происходит при всех условиях, определенных предварительно типа трансляции, масштабирования, способов заполнения и так далее.

.

 #eg [op=draw; n=; b=i; le=; mo=; sty=;]

Эта операция реально создает объект рисованием контура из path записанного в памяти компьютера с номером [n]. Контур рисуется как сложная линия, имеющая толщину и структуру типа пунктира, формы углов и т. д.. Толщина задается элементом r(i). Параметр [mo] = 0,1,2 задает структуру обработки концов толстой линии (0=овальный как бочка, 1=круглый, 2=прямоугольный). Параметр [sty] = 0,1,2 определяет структуру углов толстой линии (0=под 45 градусов, 1=круглый, 2=скошенный). Значение r(i+1) задает предел для sty=0;. Параметр [le=l;] задает длину массива, который описывает структуру пунктира. Сам массив берется из r(i+2) до r(i+1+l). Первый элемент есть длина линии, второй есть длина пустоты и так далеe. Структура копируется вдоль всей длины линии. Наконец элемент r(i+2+l) определяет фазу пунктира, а именно длину первого сегмента. Чтобы понять смысл всех этих свойств, достаточно их попробовать с помощью какой-либо специальной ACL-программой, которую пользователь может написать сам.

.

 #eg [op=text; b=i; sty=; tki=; tsi=; emp=; form=;] FT

Эта операция рисует строку текста. Положение текста (x,y) задается r(i) и r(i+1). Вертикальная координата отсчитывается от верхнего края области графика. Но если [emp] > 0, то отсчет идет от нижней области графика вверх. Не забывайте обнулять [emp] после использования. Этот режим можно использовать только в том случае, если не задано масштабирование координат [op=scale]. Параметр [sty] определяет способ постановки текста. Существует 9 способов. Если [sty]=1,4,7 то текст ставится началом (левой сторой), если [sty]=2,5,8 то он ставится средней точкой и если [sty]=3,6,9 - он ставится концом (правой стороной). Далее если [sty]=1,2,3 то текст ставится низом, если [sty]=4,5,6 то текст ставится на середине высоты, если [sty]=7,8,9, то он ставится верхом. Тип фонта определяется [tki] = 0,1,2,3 (0-plain, 1-bold, 2-italic, 3-bold-italic). Размер фонта определяется [tsi]. Имя семейства фонтов (Family name) задается параметром [form]. Можно использовать любое семейство, установленное на данном компьютере. Список всех фонтов можно определить независимо.

Здесь следует сделать замечание, касающееся имен фонтов, состоящих из нескольких слов, разделенных пробелами. Такие фонты нельзя задать непосредственно как [form=Times New Roman;], так как пробелы в определении текстовых параметров недопустимы (они воспринимаются как разделители). Обойти эту проблему можно используя текстовую ссылку. То есть сначала напечатать имя фонта в массив t(), а затем сделать ссылку. Указанный выше фонт надо задавать так.
#pri Times New Roman\E #p [form=\Ts(4) s(5)\E]
Такой способ нормально работает. Сам текст задается аргументом команды FT (форматированный текст). Свойства рисования формы букв и другие условия должны быть определены предварительно. Графический текст может включать греческие буквы. См. юникоды в описании команды #g в предыдущем разделе. ACL программа Unicodes позволяет просмотреть все юникоды, рисуемые в любом фонте.

.

 #eg [op=image; n=; b=i;]

Эта операция ставит картинку на графическую поверхность. Картинка может быть взята только из памяти компьютера с номером [n]. Картинка из файла должна быть предварительно записана в память командой #w. Запись картинки в память необходимо производить до того, как открыт графический режим. Это условие является следствием конкретной реализации интерпретатора. Однако здесь, в отличие от команды простой графики, картинка может быть трансформирована в соответствие с предварительно определенными условиями трансформации. Более того, данная операция определяет еще и свои собственные афинные преобразования, которые будут выполнены перед общей трасформацией. Если таковые отсутствуют, то r(i) и r(i+1) определяют координаты центра картинки, отсчитывая от левого верхнего угла графика. Но если [emp] > 0, то отсчет идет от левого нижнего угла графика. Не забывайте обнулять [emp] после использования. Параметр [emp] можно использовать только в том случае, если не задано масштабирование координат [op=scale]. Это не есть чистая трансляция, поскольку команда учитывает размер картинки. Элемент r(i+2) задает угол вращения картинки вокруг ее центра в радианах. Дополнительно r(i+3) и r(i+4) определяют shear преобразование, в то время как r(i+5) и r(i+6) определяют масштабирование вдоль осей x и y. Как и во всех предыдущих операциях, индекс i задается параметром [b]. Замечу, что локальные афинные преобразования для картинки более удобны по сравнению с общими, так как преобразовывают только картинку. Общие преобразования необходимы, когда картинка является частью сложного графика.

.

 #eg [op=mtxt; n=; tki=; tsi=; form=; emp=;] st x y ts FT ...

Эта операция рисует сразу много строк текста. Число строк задается параметром [n]. Информация о строках текста распределена между параметрами и аргументами. Так [tki] указывает тип текста, [tsi] указывает базовый размер текста, [form] указывает фонт как и в операции [op=text;]. Сами строки задаются аргументами. Каждая строка описывается 4 численными аргументами и FT. Численный аргумент (st) может иметь значения 1 2 3 или -1 -2 -3 . Он определяет тип установки текста. А именно 1 для левой границы, 2 для центра, 3 для правой границы. Знак минус указывает что последующие координаты абсолютные, в противном случае они относительные от предыдущей линии. Стартовые значения всегда 0 0, поэтому первые относительные координаты являются и абсолютными. Аргументы (x) и (y) есть как раз координаты, (ts) есть число которое должно быть добавлено к базовому размеру текста [tsi] для получения реального размера. Сам текст одной строки есть последующий аргумент (FT = форматированный текст). Свойства рисования формы букв и другие условия должны быть определены предварительно. Графический текст может включать греческие буквы. См. юникоды в описании команды #g предыдущего раздела. Нормально вертикальная координата отсчитывается от верхней границы графика вниз. Но если [emp] > 0 и (st) отрицательно, то отсчет идет от нижней границы. Не забывайте обнулять [emp] после использования. Этот режим можно использовать только в том случае если не задано масштабирование координат [op=scale].

.

.

 #eg [op=chfont; form=;]

Эту операцию можно запускать независимо от того, открыт или нет графический режим. Она носит вспомогательный характер и позволяет определить установлен ли на данном компьютере заданный фонт. Так как тексты можно писать любым фонтом, имеющимся на компьютере, то возникает проблема переносимости программы с компьютера на компьютер. Дело в том, что фонты являются частью операционной системы и не переносятся вместе с программой. Если они установлены, то программа их использует, если нет, то она заменяет заказанный фонт на свой дежурный фонт, свойства которого легко определить эмпирическим путем, задавая заведомо нереальный фонт. При этом скорее всего текст не уложится точно в отведенное ему место и вообще будет выглядеть чужеродным. Поэтому необходимо тестировать наличие редких фонтов и предусматривать их замену другими фонтами с похожими свойствами, если нужный фонт отсутствует. В этом главное назначение данной операции. Имя фонта, как обычно, задает параметр [form]. Результат выдается в переменную [&]. Она равна нулю, если фонта нет, и единице, если фонт есть. Как задавать сложные фонты смотри в операции text.