Научиться пользоваться языком программирования лучше всего на примерах конкретных работающих программ. В этой главе мы рассмотрим несколько относительно простых программ и детально разберем их код. Программы организованы по следующему принципу. Есть пустой шаблон eps-файла с названием exam.eps. В нем заданы все команды языка vkPS в преамбуле и указана только одна команда run, аргументом которой является имя файла. Соответственно все примеры программ записаны в отдельные файлы с названиями exam-01.psf, exam-02.psf и так далее. Чтобы просмотреть все примеры нужно просто последовательно менять имена файлов в этой команде. Мы же будем разбирать тексты именно из этих файлов, потому что команды шаблона мы уже обсудили. Вам понадобится некоторое время чтобы их запомнить. Я хочу специально заметить, что команды разрабатывались для программы постсткрипт-генератора, который использует их очень много раз. Поэтому сокращение имен существенно уменьшает размер файлов.
В языке Постскрипт нет специальной команды, задающей градиент цвета. Но есть циклы, которые позволяют это сделать достаточно эффективно. Первая программа раз раз это и демонстрирует. Вот ее текст
100 100 tr /rc 0 def /R 90 def
30 {rc rc 1 srgb R 0 m 0 0 R 0 360 arc fir /rc rc 0.03 add def /R R 2 sub def} repeat
/lep{/x 3 def /a 0.01 def 30 {x x 90 x sub mul a mul l /x x 3 add def} repeat
/x x 6 sub def 30 {x x 90 x sub mul a neg mul l /x x 3 sub def} repeat}def
0 0 m lep 90 r lep 90 r lep 90 r lep clip /rc 1 def /R 90 def
30 {rc 0 0 srgb R 0 m 0 0 R 0 360 arc fir /rc rc 0.03 sub def /R R 3 sub def} repeat
Чтобы показать вам результат выполнения этой постскрипт программы, я ее выполнил с помощью Gsview, затем выбрав оптимальный размер, скопировал картинку в буфер. Сама программа Gsview не умеет спасать картинку в одном из растровых форматов. Для этого следует привлекать сторонние программы. Вполне годится программа Paint операционной системы Виндовс. На моем компьютере эта программа почему то не запускается и я использовал старую версию программы Paint Shop Pro. Впрочем для этой цели годится любая бесплатная программа, способная показывать файлы с растровыми картинками и перекодировать их из одного формата в другой. Я рекомендую, например, сайт "http://www.dimin.net". Итак я спас картинку в формате
Картинка рассчитана на размер 200*200, именно такой размер указан в строке Bounding Box в файле шаблона. Чтобы сдвинуть начало координат в центр картинки используется команда 100 100 tr. Далее задаются значения для двух переменных rc и R. Первая из них задает интенсивность одной из компопнент цвета, вторая - радиус круга. После этого 30 раз в цикле задается цвет командой rc rc 1 srgb, после этого начальная тоска устанавливается на окружность и задается контур в виде этой окружности, после чего область заполняется цветом. Затем интенсивность цвета увеличивается на шаг, а радиус уменьшается. Так нарисован синий круг, цвет которого постепенно переходит в белый по мере умеьшения радиуса. В Постскрипте нет смешения цветов - каждый новый объект полность закрывает старый. В этом смысле область внутри круга наименьшего радиуса 30 раз меняет цвет. Но все это делается достаточно быстро, так что мы может об этом не беспокоиться.
Затем определяется процедура lep. Она необходима, потому что будет использоваться несколько раз. Эта процедура вычерчивает контур одного лепестка. Она сначала чертит параболу a*x(90-x) при положительном значении a и при изменении x от 3 до 90 с шагом 3. Это делает первый цикл. По выходе из цикла переменная x имеет значение 93. Нам надо вычесть 6 и двигаться в обратном направлении, постепенно уменьшая x до нуля. При этом у коэффициента a меняется знак. Контур одного лепестка готов. Затем устанавливает курсов в точку 0 0 и запускаем процедуру lep. Затем вращаем систему координат на 90 градусов и снова запускаем процедуру и точно так же еще 2 раза. Таким образом мы начертили контур из 4-х лепестков. Объявляем этот контур контуром обрезки с помощью команды clip. Теперь все что будет рисоваться появится только внутри этого контура, а все-то окажется вне контура - рисоваться не будет. И после этого мы снова рисуем окружности, только красного цвета. Но они закрашивают только лепестки.
Вот и все. Лично я плохо представляю как можно сделать этот рисунок с помощью мышки и тыканием в иконки. А методом программирования он делается достаточно просто. Конечно на языке ACL его тоже можно запрограммировать. Но тогда результатом будет растровая картинка, и если нужно высокое разрешение 600 dpi, то ее размер будет очень большим. А размер чистого постскрипт кода всего 504 байта. Ну и шаблон еще 1354 байта. Но это все равно намного компактнее любого другого способа. Любопытно еще и то, что при спасении картинок такого типа в формате jpg они очень много теряют, потому что этот формат искажает изображение и это очень заметно.
В этом примере я покажу два приема: как писать текст фасонно и как использовать мой самодельный русский фонт. Снова сначала посмотрим текст, а затем его обсудим.
(cyr30fnt.psf) run
8 8 m 184 0 rl 0 184 rl -184 0 rl 0 -184 rl 1 0.8 0.8 srgb fir
1 1 1 srgb 50 20 m (http://vkacl.narod.ru)H
% аргументы: текст, размер, радиус, начальный угол, шаг, начальный уровень серого, шаг
/rtt{/gd ed /gv ed /san ed /an ed /rad ed /ts ed /tt ed ts PB
tt {/char ed ( ) dup 0 char put gv sg an cos rad mul an sin rad mul m gs an 90 sub r sc gr
/an an san sub def /gv gv gd add def} forall}bd
gs 100 100 tr (Postsript allows one to make all) 16 80 220 8 0.2 0.02 rtt gr
1 0 0 srgb 60 140 m (Esli ho-e<x)R 40 120 m (mnogogo dobitxsq)R 60 100 m (Budx gotow)R
40 80 m (postskriptu)R 90 60 m (nau-itxsq)R
В данной программе мы предполагаем использовать самодельный фонт, которого нет у интерпретатора Gstools. Нет его и у всех тех, кому этот файл будет передаваться. Поэтому соответсвующий файл нужно поставить в постскрипт файл. На самом деле его удобнее держать в отдельном файле и просто добавить новую команду чтения из файла. Этот файл к вам пришел вместе с дистрибутивом этой книги. Просто держите его в той же папке, где и все остальные файлы и больше ничего не понадобится. Следующая строка очень простая - она просто рисует прямоугольник розового цвета. После этого я поместил на картинку ссылку на мой сайт. Если вы получили данный продукт другим путем, то советую сходить на сайт. Там есть много всего другого, не менее интересного. Написание текста вдоль окружности - это своего рода стандартная операция. Поэтому удобно ее оформить в виде процедуры с параметрами. В таком виде она выглядит как настоящая самостоятельная программа и ее можно будет переносить без изменений в любую другую программу. Итак процедура rtt. Для удобства ее использования перед ней стоит подсказка на ее аргументы. У нее 7 аргументов. Их порядок дан так как их надо ставить перед вызовом процедуры. Это сам текст (строка), его размер в pt, радиус окружности, начальный угол на окружности, шаг по углу, начальный уровень серого и шаг приращения (снова градиент цвета, на этот раз серого).
Процедура совершенно автономна и мы можем ее обсудить прямо сейчас. Предполагается, что текущая позиция курсора определена. Сначала идет считывание аргументов в переменные в обратном порядке. Следует запомнить имена переменных, далее они будут использоваться. После этого ставим размер шрифта и заказываем Palatino-Bold фонт. Я перепробовал все фонты, мне больше всего понравился этот. Но если кому не нравится можете все переписать. Далее ставим текстовую строку и объявдяем процедуру для цикла forall. Как вы уже знаете это цикл запускает процедуру столько раз, сколько элементов в объекте и каждый раз помещает в стек очередной элемент объекта. В нашем случае это символ. Поэтому сначала необходимо считать этот элемент. Но тут есть одна тонкость, которая плохо прописана в описании. Дело в том, что цикл помещает в стек не сам элемент, то есть символ, а его ASCII номер, то есть число. И команда show с ним не работает. Число надо преобразовать в текст. Поэтому ставим пустую строку из одного пробела, дублируем ее, затем ставим индекс элемента 0 и наше число и выполняет команду put. В результате такой процедуры в стеке оказывается наша буква именно как текст, теперь все в порядке. Итак буква есть. Затем задаем текущий уровень серого, и вычисляет координаты, на которые надо поместить букву, по обычным формулам x = r*cos(a), y = r*sin(a). Когда все готово ставит курсор на это место. Но букву надо повернуть на этот угол минус 90 градусов. Запоминаем параметры графической среды, заказываем поворот и рисуем букву центром на позицию курсора. Затем восстаналиваем параметры. Собственно это понадобилось только для того, чтобы не крутить систему координат обратно. Работа сделана. Осталось только уменьшить угол на шаг и увеличить уровень серого на шаг. Вот как все просто когда знаешь язык. К сожалению такое знание приходит не сразу.
Итак начинаем рисовать текст по кругу. Опять запонимаем параметры графической среды, далее я не буду на этом останавливаться. Это всегда надо делать если используете трансляцию или масштабирование или поворот. Или каждый раз надо делать обратные операции. Переносим начало координат в центр картинки, задаем аргументы процедуры и выполняем процедуру. И все дела. Ну а дальше пишем русский текст. Текст стандартных параметров пишется с помощью команды R. Очень просто. Сложность только в том - как задать символы. Постскрипт интерпретатор не понимает русский язык. Вместо русских букв надо писать латинские с теми же самыми номерами. При создании этого фонта я стремился к тому, чтобы соответствие было максимально наглядным. Но некоторые символы не имеют аналогов в латинском. Вообще говоря, таблицу соответствия легко составить в виде постскрипт программы. Я сделаю это в следующем примере. А пока все.
Вообще говоря, проблема существует не только с русским шрифтом, но и с греческим, который часто используется при написании математических, физических или химических формул. В Постскрипте есть стандартный фонт Symbol, который как раз создан для этих целей, а в языке vkPS он обозначается одной буквой G. Поэтому желательно иметь программу, которая показывает соответствие символов в трех этих шрифтах. Программа, которую я написал, выглядит следующим образом
(cyr30fnt.psf) run
% аргументы: текст, x y d
/ts{/d ed /y ed /x ed /tt ed
tt {/char ed ( ) dup 0 char put dup dup x y m H ( )H G ( )H R /y y d sub def} forall}bd
(ABCDEFGHIJKL) 20 180 15 ts (MNOPQRSTUVWX) 80 180 15 ts (YZ=-><";?$@&) 140 180 15 ts
Как видно, набивать нужно только латинские символы, а появляются другие. Я поясню здесь, что в русском шрифте всего 64 знака, то есть только буквы: 32 больших и 32 маленьких. Поэтому символы типа чисел и специальных знаков надо набирать латинским или греческим шрифтом. Однако, в латинском языке только 26 знаков, так что для остальных 12 надо использовать другие символы клавиатуры. По этой причине для 26 символов большие буквы от маленьких отличаются лишь изменением регистра, но для оставшихся шести это не так. В Таблице не уместилось всего два знака. Но кому нужно тот сам сообразит что нужно исправить, чтобы их поместить. Либо увеличить размер области, я все рисунки рисую стандартно на размер 200 на 200 pt. Либо промасштабировать картинку в сторону уменьшения.
Итак разберем программу. Собственно тут и разбирать нечего. Она очень похожа на предыдущую. Снова цикл forall, но на этот раз каждый символ два раза дублируется и рисуется разными фонтами через пробел. Ну и после рисования каждого символа координата y уменьшается на заданный шаг. Заодно я показал как рисовать вертикальные таблицы чисел. Числа можно поместить в массив и точно также использовать цикл forall. В принципе мне не сложно сделать специальную опцию кодировки в своем редакторе vkNotePad, при которой русский текст в виндовс-кодировке будет автоматически преводится в нужные латинские символы, но пока это не сделано. Хотя постскрипт генератор программа vkps.exe это делает. Она вообще умеет форматировать достаточно сложные формулы и текст, а также страницы. Там все это написано на фортране, но можно без труда придумать специальные процедуры на постскрипте, которые будут удобно форматировать текст, вписывать его в колонку и так далее. Собственно команды языка vkPS уже позволяют достаточно эффективно писать формулы в одну строку. Этого достаточно для установки надписей на осях координат научных графиков. В следующем примере я покажу пример набивки достаточно сложной формулы
Как я уже неоднократно отмечал, Постскрипт - это язык низкого уровня. Однако, разработав несколько специальных процедур, в нем все же можно достаточно эффективно писать сложные математические выражения. Здесь я привожу программу, с помощью которой написаны показанные формулы.
/int{0 5 rm (\363)G -9.5 -10 rm (\365)s 0 5 rm}bd
/frac{/Xn ed gs 4 6 rm Xf gr gs 4 -9 rm Yf gr 14 Sy Xn{(\276)s}repeat}bd
/Sh{0 rm}bd /Sv{/Y1 ed 0 Y1 rm}bd
/blb{is3 0 -3 rm (\()L 0 3 rm ds3}bd /brb{( )L is3 0 -3 rm (\))s 0 3 rm ds3}bd
is1 15 115 m int ds2 -2 -15 rm (0)L is2 ds1 -2 35 rm (\245)G 0 -20 rm is1
(dt)L 4 Sh (e)L 10 Sv ds2 (i)L(w)G(t)L( - g)G(t)L is2 -10 Sv
( = )L /Xf{10 Sh(i)s}bd /Yf{(w)G( + i)L(g)G}bd 3 frac
15 65 m int (dx exp)L blb (- )G /Xf{5 Sh (x)L(2)it}bd /Yf{(2s)G(2)it}bd 2 frac brb
( =)L -60 -40 rm (= \(2)L(p)G(\))L(1/2)it(s)G ds1
Здесь перед написанием текста программы добавлено еще 6 процедур. Первая из них рисует знак интеграла, вторая с аргументами рисует дробь в два этажа, затем идут две короткие процедуры сдвига по горизонтали и по вертикали, каждая с одним аргументом в виде числа и еще две процедуры рисования больших скобок: левой и правой. Самая сложная процедура дроби. Она требует число - сколько раз рисовать знак минус, который образует черту посередине, и две процедуры Xf и Yf для рисования числителя и знаменателя отдельно. Такая организация позволяет сократить запись кода. Так как все команды состоят из одной или двух букв, то запись получается компактной и при известном навыке записывается быстро. Конечно такие издательские системы как Латех, кроме команд форматирования имеют еще и мощный аппарат шрифтов. Но здесь можно просто модифицировать уже имеющиеся шрифты произвольным образом. Можно сделать процедуру, которая будет последовательно наклонять буквы и получится шрифт italic. Кроме того, как вы уже знаете, можно добавить произвольное число фонтов к тем, что поставляются стандартно. Я вовсе не хочу сказать, что Постскрипт способен заменить такие мощные программы как Ворд или Латех. Но эти системы не умеют рисовать графики. А постскрипт умеет. И вы всегда можете поставить на график любую формулу. И на чертежи тоже, кому надо. Вот это важно.
На Постскрипте легко вычерчивать математические кривые, заданные простыми уравнениями. В этом примере начерчена спираль Архимеда. Вот код программы.
(cyr30fnt.psf) run
gs 100 100 tr 3 slw
0 0 m /fi 1 def /k 0.1 def
900{/ra fi k mul def fi cos ra mul fi sin ra mul l /fi fi 1 add def}repeat sl
1 slw 1 0 0 srgb -100 0 100 0 ls 0 -100 0 100 ls
50 -5 50 5 ls -5 50 5 50 ls -50 -5 -50 5 ls -5 -50 5 -50 ls
gr 144 80 m (50)L 109 44 m (-50)L
110 12 m (r = kf, k = 0.1)G 10 30 m (Spiralx)R 10 14 m (Arhimeda)R
При написании программ с учетом команд языка vkPS нельзя вводить переменные с такими же именами, как и эти программы. Например, в данном случае в качестве радиуса и фазы так и хочется взять r и f. Однако именно эти буквы уже заняты под команды. В частности f используется для задания фонта в командах фонтов. Поэтому все занятые буквы лучше выписать в Таблицу и каждый раз проверять по таблице - какие имена нельзя использовать в качестве переменых. Это особенно важно в самом начале, когда еще нет опыта. Итак рассмотрим программу, она достаточно простая. Спираль рисуется точно по формулам, причем фаза проходится с шагом один градус через 900 точек. Но для программы это не помеха, все очень компактно делается в цикле. Сама спираль рисуется в системе координат с центром в центре рисунка. А вот тексты уже помещаются на реальные координаты. Для этого используется стандартный прием gs gr. Определить координаты размещения текстов помогает сама программа Gsview, поскольку она показывает координаты курсора мыши в каждый момент времени.
Данный пример показывает, что на Постскрипте предельно просто рисовать и сложные двумерные распределения. Здесь показано моделирование дифракции параллельного монохроматического пучка света на точечном объекте. Вот код программы.
1 0.5 0.5 srgb 0 0 200 200 rectfill 10 10 180 180 rectclip
100 100 tr 1 slw /ra 1 def /k 0.2 def
/cir{ra 0 m 0 0 ra 0 360 arc ra dup mul k mul cos dup mul sg sl}bd
140{cir /ra ra 1 add def}repeat
Обратите внимание - такая сложная картинка описывается всего в 4 строчки текста. Я объясню, что все это значит. Из оптики известно, про при дифракции на точечном препятствии (поглотителе, например) образуются кольца Френеля, то есть интенсивность света становится слабо неоднородной. Контраст всегда можно усилить математическими методами. Кольца Френеля изменяют свою интенсивность по закону
В процессе написания этой программы я сделал несколько опечаток и программа Gsview показала на ошибки. Я не описываю здесь методов понимания ошибок, но это все интуитивно понятно. Программа показывает кусок текста, где она зафиксировала ошибку, пишет название ошибки на английском языке (об этом надо бы написать отдельно, но очень не хочется, если кто-либо попросит - напишу) и самое главное - указывает состояние стека на момент ошибки. По состоянию стека можно догадаться - где была сделана ошибка. Но часто хватает самого факта фиксации ошибки, достаточно просто аккуратно проверить весь код, а если он большой, то разбить его на части и попробовать выполнить по частям. Поиск ошибок - это особое искусство, но научить этому очень сложно. Да и потом у каждого свои методы.
Изображение трехмерных объектов связано с определенными вычислениями координат проекции трехмерного объекта на двумерную плоскость. Кроме того, при этом существует проблема с порядком изображения, чтобы ближние предметы закрывали дальние, а не наоборот. В данной программе использованы формулы центральной проекции из точки зрения. Предполагается, что у трехмерного объекта есть начало координат и точка зрения задается тремя координатами направления и отдельно расстоянием до точки зрения по этому направлению. Координаты затем нормируются на единичный вектор. Чтобы было понятнее я приведу здесь формулы центрального проектирования, тем более что они могут быть полезными в большом числе случаев. Итак обозначим координаты направления до точки зрения как vx,vy,vz. Будем считать, что они уже отнормированы. Расстояние до точки зрения обозначим за R. Трехмерная точка на объекте будет иметь координаты x,y,z, а двумерная точка на плоскости рисунка - координаты x1,y1. Формулы для пересчета координат x,y,z в координаты x1,y1 следующие: . vp = (vx2+vy2)1/2, . h = z*vz -- y*vy -- x*vx, . r = (vp*(1 -- h/R))--1,
x1 = r*(x*vy -- y*vx), . y1 = r*(z -- vz*h). Программа рисует серию столбиков определенной высоты, расположенных по кругу на плоскости z = 0. Соответственно столбики в трехмерном пространстве имеют на концах те же самые координаты x,y, но разные координаты z = 0,Z. Столбики отбрасывают тень в параллельных лучах, то есть источник света находится очень далеко. У тени координаты обоих концов имеют z = 0, и один конец совпадает со столбиком, а второй пересчитывается из второго конца столбика по закону . x2 = x - (Z/uz)*ux, . y2 = y - (Z/uz)*uy . Итак для каждого столбика имеем три трехмерные координаты, которые надо спроектировать и начертить. Программа, которую я написал, выглядит так:
100 100 tr 5 slw /vx 1 def /vy 1 def /vz 1.3 def /R 400 def /Z 20 def /ra 80 def
/ux 0 def /uy 1 def /uz 1 def /t Z uz div def
/o vx vx mul vy vy mul add vz vz mul add sqrt def /vx vx o div def /vy vy o div def /vz vz o div def /vp vx vx mul vy vy mul add sqrt def
/pro{/h z vz mul y vy mul sub x vx mul sub def /ro 1 1 h R div sub vp mul div def
/x1 x vy mul y vx mul sub ro mul def /y1 z vz h mul sub ro mul def}bd
/xy{/x fi cos ra mul def /y fi sin ra mul def}bd
/bc{xy /z 0 def pro x1 y1 x1 y1 /x x t ux mul sub def
/y y t uy mul sub def pro x1 y1 0.5 sg ls xy /z Z def pro x1 y1 0 sg ls}bd
/fi 90 def 11{bc /fi fi 18 add def}repeat /fi 90 def 10{bc /fi fi 18 sub def}repeat
Здесь сначала определяются все параметры объекта и направления проектирования. Если их изменить, можно получить другой рисунок. Затем нормируется направление проектирования и вычисляется постоянная величина vp. После этого описывается процедура pro, которая использует координаты x,y,z и сама определяет координаты x1,y1. Далее описывается процедура xy, которая вычисляет x,y координаты столбиков по радиусу и углу fi. После этого определяется основная процедура, которая и рисует. Она сначала рисует тень, а затем столбик. При этом общие координаты для тени и столбика сразу два раза записываются в стек, чтобы их потом не пересчитывать. Цикл по углу сделан два раза начиная от 90 градусов сначала увеличивая угол, а затем уменьшая. Это необходимо, для того, чтобы дальние столбики не загораживали ближние. Вот и все.
.
Полезно показать еще один пример, более важный практически. Речь идет о трехмерной пластинке, которая может изображать кристалл, или подставку или любой прибор, например, детектор. Эта трехмерная пластинка видна в общем случае своими тремя гранями, так что остальные можно не рисовать. Удобно задавать ее трехмерные координаты и затем проектировать на нужное направление. Для простоты рассмотрим квадратную пластинку определенной толщины. Соответственно имеем два входных параметра для объекта: a -- сторона квадрата, и b -- толщина пластинки. Кроме того, надо определить 3 координаты направления vx, vy, vz и расстояние R. Это задает точку зрения, из которой мы смотрим проекцию. Эти величины можно менять и получать разные проекции разных пластинок. Остальной код стандартный и не меняется. Он автоматически определяет нужные координаты и рисует нужные области. На самом деле можно менять еще цвет трех граней. В примере используются оттенки серого, то есть меняется только светлость. При этом 0 -- черное, 1 -- белое. Светлось задается параметрами c1, c2, c3 для граней передней, боковой справа и верхней. Остальной код делает следующее. Сначала задает стартовую точку отнсительно начала координат в центре области в трехмерном пространстве. Это x0, y0, z0. Затем нормирует вектор направления (vx,vy,vz) на единичную длину. Затем определяет параметр и процедуру проектирования, это все как в прошлом примере. После этого указывает 5 точек и светлость каждой грани в обратном порядке. Точнее координаты каждой точки в прямом порядке, а сами точки в обратном. Можно делать любой порядок, но так проще программировать. И наконец в двойном (3,5) цикле делается рисунок.
100 100 tr 1 slw /vx -2 def /vy 1 def /vz -1 neg def /R 99999 def
/a 100 def /b 20 def /c1 0.9 def /c2 0.5 def /c3 0.7 def /x0 a 2 div neg def /y0 b 2 div neg def /z0 a 2 div neg def
/o vx vx mul vy vy mul add vz vz mul add sqrt def /vx vx o div def /vy vy o div def /vz vz o div def /vp vx vx mul vy vy mul add sqrt def
/pro{/h z vz mul y vy mul sub x vx mul sub def /ro 1 1 h R div sub vp mul div def
/x1 x vy mul y vx mul sub ro mul def /y1 z vz h mul sub ro mul def}bd
c3 0 b neg 0 a neg 0 0 0 b 0 a 0 0 x0 y0 z0 a add c2 0 0 a neg 0 b neg 0 0 0 a 0 b 0 x0 a add y0 z0
c1 0 0 a neg a neg 0 0 0 0 a a 0 0 x0 y0 z0
3 { /z ed /y ed /x ed pro x1 y1 m 4 {/w ed /v ed /u ed /x x u add def /y y v add def /z z w add def pro x1 y1 l }repeat fr }repeat