Элементарная машинная графика
Машинная графика — чрезвычайно перспективная область для применения языка Форт. Поскольку для задач машинной графики постоянно ведется поиск все новых и новых способов их постановки и решения, то здесь как нигде требуется очень гибкий инструментальный язык, позволяющий быстро реализовывать и проверять на практике разные варианты решений. Принцип обратной оперативной связи между постановкой задачи и ее решением позволяет программисту добиваться существенно более высоких профессиональных результатов, чем при традиционных подходах.
Вместе с тем развитие машинной графики в значительной степени определяется наличием тех или иных аппаратных средств. Чрезвычайное разнообразие их конкретных характеристик делает невозможным создание какого-либо универсального языка, применимого для всех возможных случаев. Язык Форт позволяет программисту самому быстро создавать необходимые инструментальные средства, применяя универсальные приемы с учетом особенностей конкретной задачи.
Рассмотрим две очень разные по сложности задачи построения графиков функций. В первой используется алфавитно-цифровой терминал, во второй — графопостроитель с широким набором возможностей.
В случае использования алфавитно-цифрового терминала задача состоит в том, чтобы отобразить в нем график функции, заданной вектором своих значений. Введем понятие вектора, элементы которого нумеруются от нуля, по аналогии со словом QUAN (см. ). Для получения адреса элемента вектора и для присваивания ему нового значения можно использовать те же слова AT и TO.
: ?+ ( N->N) DUP 0< ABORT" ОТРИЦАТЕЛЬНОЕ ЗНАЧЕНИЕ" ; : В-АДР ( I:ИНДЕКС,PFA->A[I]:АДРЕС) 2DUP @ U< IF SWAP 1+ 2* + EXIT THEN SWAP CR . ." - НЕДОПУСТИМЫЙ ИНДЕКС ДЛЯ ВЕКТОРА " BODY> BODY> BODY> >NAME ID. ABORT ; : В-АДР0 DOES> ( I:ИНДЕКС,PFA->A[I]:АДРЕС) В-АДР ; : В-ПРИСВ DOES> ( N:3НАЧ,I:ИНДЕКС,2CFA->) 2+ В-АДР ! ; : VQUAN ( N:ВЕРХН.ИНДЕКС->) ?+ CREATE В-ПРИСВ HERE 2- @ , В-АДР0 HERE 4 - @ , 1+ DUP , 2* HERE SWAP DUP ALLOT ERASE DOES> ( I:ИНДЕКС,1CFA->N[I]:ЗНАЧ) 4 + В-АДР @ ; : В-РАЗМ ( ->N:ЧИСЛО ЭЛЕМЕНТОВ) ' 6 + @ [COMPILE] LITERAL ; IMMEDIATE
Невысокая разрешающая способность алфавитно-цифрового терминала с точки зрения машинной графики вполне допускает использование стандартных целых чисел для представления значений вещественных функций. Более того, такие дискретные вычислительные приемы оказываются значительно более удобными для задач машинной графики, чем работа с числами в формате плавающей точки. Например, тригонометрические функции можно вычислять следующим образом, задавая их аргумент в градусной мере:
( 18.06.86 SIN COS) ( ТАБЛИЦА СИНУСОВ ОТ 0 ДО 90 ГРАД.) HERE 0000 , 0175 , 0349 , 0523 , 0698 , 0872 , 1045 , 1219 , 1392 , 1564 , 1736 , 1908 , 2079 , 2250 , 2419 , 2588 , 2756 , 2924 , 3090 , 3256 , 3420 , 3584 , 3746 , 3907 , 4067 , 4226 , 4384 , 4540 , 4695 , 4848 , 5000 , 5150 , 5249 , 5446 , 5592 , 5736 , 5878 , 6018 , 6157 , 6293 , 6428 , 6561 , 6691 , 6820 , 6947 , 7071 , 7193 , 7314 , 7431 , 7547 , 7660 , 7771 , 7880 , 7986 , 8090 , 8192 , 8290 , 8387 , 8480 , 8572 , 8860 , 8746 , 8829 , 8910 , 8988 , 9063 , 9135 , 9205 , 9772 , 9336 , 9397 , 9455 , 9511 , 9563 , 9613 , 9659 , 9703 , 9744 , 9781 , 9816 , 9840 , 9877 , 9903 , 9925 , 9945 , 9962 , 9976 , 9986 , 9994 , 9998 , 10000 , : SIN180 ( N->SIN N, 0<=N<=180) DUP 90 > IF 180 SWAP - THEN 2* [ DUP ] LITERAL + @ ; DROP : SIN ( N->SIN N) 360 MOD DUP 0< IF 360 + THEN DUP 180 > IF 180 - SIN180 NEGATE ELSE SIN180 THEN ; : COS ( N->COS N) 90 SWAP - SIN ;
Аналогично и другие элементарные функции можно с успехом вычислять по классическим итерационным схемам:
( DSQRT SQRT ИЗВЛЕЧЕНИЕ КОРНЯ ПО СХЕМЕ НЬЮТОНА) : DSQRT ( D1->D2) 2DUP D0< ABORT" ОТРИЦАТЕЛЬНЫЙ АРГУМЕНТ" 2DUP 2. D< IF EXIT THEN ( ДАЛЕЕ ПО СХЕМЕ: X[0]=X/2; X[I+1]=X[I]/2+X/X[I]/2) 2DUP 2. D/ SWAP BEGIN 2OVER 2OVER D/ 2OVER D+ 2. D/ 2SWAP 2OVER D- DABS 2. D< UNTIL 2SWAP 2DROP ; : SQRT ( N1->N2) S>D DSQRT DROP ;
Некоторое неудобство при таком представлении функций доставляет необходимость помнить о масштабном множителе, но это с лихвой возмещается простотой приемов их вычисления.
Используя описанные вспомогательные средства, можно задавать векторы со значениями интересующих нас функций. Работая с простыми значениями, будем пользоваться их описанием через слово QUAN. Пусть наша функция в обычной записи имеет вид: y = sin 3x + sin 120x. Зададим ее определение через введенные инструментальные определения, выбрав 100 в качестве масштабного множителя:
70 VQUAN U : U! ( -> УСТАНОВКА ВЕКТОРА U) В-РАЗМ U 0 DO 3 I * SIN 120 I * SIN + 100 / I TO U LOOP ; U!
При желании можно проверить правильность установки значений, распечатав их как числа:
: B-? ( -> РАСПЕЧАТКА ВЕКТОРА) >IN @ >R [COMPILE] В-РАЗМ R> >IN ! ' SWAP 0 DO I OVER EXECUTE 8 .R LOOP DROP ; B-? U
В результате будет напечатан следующий текст:
0 93 -78 15 109 -62 30 124 -47 45 138 -34 58 151 -21 70 162 -10 80 172 0 89 179 4 95 185 9 98 188 11 100 188 10 98 186 7 95 181 2 89 177 -4 80 166 -14 70 155 -25 58 143 -38 45 129 -52 30 114 -67 15 99 -83 0 83 -99 -15 67 -114 -30 52 -129 -45 OK
Для построения графика определим список слов PLOT (график) и в нем ряд вспомогательных переменных:
VOCABULARY PLOT PLOT DEFINITIONS QUAN RHO 4 TO RHO ( ШИРИНА СТОЛБЦА ДЛЯ Y-КООРД.) QUAN MU0 16 TO MU0 ( МАКСИМАЛЬНАЯ ВЫСОТА ОКНА) QUAN NU0 64 RHO - TO NU0 ( МАКСИМАЛЬНАЯ ШИРИНА ОКНА) QUAN WSTA ( АДРЕС НАЧАЛА ОКНА) QUAN MU ( ТЕКУЩАЯ ВЫСОТА ОКНА) QUAN NU ( ТЕКУЩАЯ ШИРИНА ОКНА) QUAN WX ( X-КООРДИНАТА ОКНА) QUAN WY ( Y-КООРДИНАТА ОКНА) QUAN KAPPA ( ЧИСЛО ЗНАЧЕНИЙ СИГНАЛА) QUAN LAMBDA ( ДИАПАЗОН ЗНАЧЕНИЙ СИГНАЛА) QUAN MA ( МАКСИМАЛЬНОЕ ЗНАЧЕНИЕ СИГН.) QUAN MIN0 ( МИНИМАЛЬНОЕ ЗНАЧЕНИЕ В ОКНЕ) QUAN VAL ( ВЕКТОР СИГНАЛА)
Пусть прямоугольный экран терминала, на котором строится график, содержит MU0 строк по NU0 литер каждая. Первые RH0 позиций в каждой строке занимает надпечатка ее координаты по оси ординат. Общий размер графика определяется значениями LAMBDA (диапазон значений функций) и KAPPA (число значений). Этот прямоугольник может как умещаться целиком на экране терминала, так и заметно превышать его.
Определим понятие «окна», которое перемещается по графику и показывает на экране терминала соответствующую его часть. Переменные WX и WY задают координаты левого верхнего угла окна относительно левого верхнего угла графика. Определим слова, выполняющие инициализацию и перемещение окна по полю графика:
FORTH DEFINITIONS PLOT : НАЧАТЬ ( -> ИНИЦИАЛИЗАЦИЯ) PLOT HERE NU0 MU0 * ALLOT TO WSTA ; PLOT DEFINITIONS : ВВЕРХ ( N-> СДВИНУТЬ ОКНО НА N ПОЗИЦИЙ ВВЕРХ) ?+ WY SWAP - 0 MAX TO WY ; : ВЛЕВО ( N-> СДВИНУТЬ ОКНО НА N ПОЗИЦИЙ ВЛЕВО) ?+ WX SWAP - 0 MAX TO WX ; : ВНИЗ ( N-> СДВИНУТЬ ОКНО НА N ПОЗИЦИЙ ВНИЗ) ?+ WY + MU + LAMBDA MIN MU - TO WY ; : ВПРАВО ( N-> СДВИНУТЬ ОКНО НА N ПОЗИЦИИ ВПРАВО) ?+ WX + NU + KAPPA MIN NU - TO WX ;
Определим также слова для вычисления параметров графика и распечатки окна по его текущим координатам. Вспомогательное слово MU по значению функции определяет, где находится соответствующая точка графика: ниже окна, внутри или выше него. Если точка попадает в окно, будем отмечать ее положение знаком * (звездочка), а если точка находится выше или ниже окна, то соответственно в самой верхней или самой нижней строке окна поставим знак + (плюс), чтобы видеть направление, в котором следует искать данную точку на графике. Ось абсцисс, если она попадает в окно, будем отмечать строкой из знаков «минус». Надпечатку координат по оси абсцисс будем выполнять дважды — в верхней и нижней строках терминала.
: -MU ( N:ЗНАЧЕНИЕ->-1:НИЖЕ ОКНА/ M:СМЕЩЕНИЕ ОТ ВЕРХА ОКНА ВНИЗ,0/ +1:ВЫШЕ ОКНА ) MIN0 - DUP 0< IF DROP -1 EXIT THEN MU 1- SWAP - DUP 0< IF DROP 1 EXIT THEN 0 ; : ФУНКЦИЯ ( -> СЛЕДУЮЩЕЕ СЛОВО: ИМЯ ВЕКТОРА ЗНАЧЕНИЙ) >IN @ >R [COMPILE] В-РАЗМ R> >IN ! DUP TO KAPPA NU0 MIN TO NU ' TO VAL ( ВЫЧИСЛЕНИЕ MA И LAMBDA ) 0 VAL DUP ( X[0],X[0] ) KAPPA 0 DO I VAL ( MIN[I-1],MAX[I-1],X[I] ) DUP >R MAX SWAP R> MIN ( MAX[I],MIN[I]) SWAP LOOP DUP TO MA SWAP - 1+ DUP TO LAMBDA MU0 MIN TO MU 0 TO WX 0 TO WY ; : ОСЬ-X ( НАДПЕЧАТКА ОСИ X) CR RH0 SPACES WX NU 1- RH0 - WX BEGIN DUP 10 MOD DUP IF 10 SWAP - THEN DUP >R + 2DUP >= WHILE R> SPACES C" ! EMIT DUP 0 <# #S #> DUP >R TYPE 1+ R> + REPEAT RDROP 2DROP ; : ГРАФИК ( -> РИСОВАТЬ ГРАФИК ПО СИГНАЛУ И КООРДИНАТАМ ОКНА) MA WY - MU 1- - TO MIN0 ( УСТАНОВИТЬ MIN0) WSTA MU NU * BLANK ( ЗАЧИСТИТЬ ОКНО) 0 -MU 0= IF NU * WSTA + NU C" - FILL THEN ( ПРОВЕСТИ ОСЬ X) ( ЦИКЛ ПО ОСИ X ДЛЯ ЗАПОЛНЕНИЯ ОКНА) WX NU + WX DO I VAL ( X[I]) -MU DUP IF ( ВНЕ ОКНА) 1+ IF ( ВЫШЕ ОКНА) C" + 0 ELSE ( НИЖЕ ОКНА) C" + MU 1- THEN ELSE DROP C" * SWAP THEN NU * I WX - + WSTA + C! LOOP ОСЬ-X MU 0 DO CR MA WY - I - RH0 .R I NU * WSTA + NU TYPE LOOP ОСЬ-X ;
Следующий протокол показывает работу введенных определений по вычерчиванию графика интересующей нас функции:
> НАЧАТЬ ФУНКЦИЯ U ГРАФИК !0 !10 !20 !30 !40 !50 188 * * 187 186 * 185 * 184 183 182 181 * 180 179 * 178 177 * 176 175 174 173++++++++++++++++++++++ ++ ++ ++ ++ ++ ++ +++++++++++++++++++ !0 !10 !20 !30 !40 !50 OK > 175 ВНИЗ 10 ВПРАВО ГРАФИК !10 !20 !30 !40 !50 13+ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + 12 11 * 10 * 9 * 8 7 * 6 5 4 * 3 2 * 1 0----------*---------------------------------------*--------- -1 -2 + + + + + + + + + + ++ ++ ++ !10 !20 !30 !40 !50 OK
Вычерчивание графиков на графопостроителе чрезвычайно осложняется обилием деталей чертежа, которые надо учитывать. При этом собственно вычисление значений функции составляет незначительную часть в общем объеме работы. Многие элементы чертежа имеют, как правило, какое-нибудь стандартное значение, принимаемое по умолчанию, и программист может их только переопределять (например, заголовок, штамп, оформление полей, выбор шрифтов и цвета надписей и т.д.). С каждым из этих крупных элементов можно связать список слов, обрабатывающих те или иные его компоненты, и для установки нужного значения исполнять соответствующее слово в контексте данного элемента. Сходные действия (например, задание шрифта или цвета) в разных контекстах могут обозначаться одними и теми же словами, что создает большие удобства для программиста. Вместе с тем и синтаксис для задания действий графопостроителя можно сделать максимально простым и удобным для пользователей-непрограммистов. Например, определение элементов чертежа можно задавать так:
ГРАФИК XY X ОТ 0 ДО 4 ШАГ 1 Y ЛОГАРИФМ ОТ 1 ДО 100 Х ШТРИХ РАЗМЕР 0.25 РАМКА ЗАГОЛОВОК" ЧЕРТЕЖ 1" РИСУЙ
В результате будет нарисована прямоугольная рамка с заголовком «ЧЕРТЕЖ 1» надпечаткой оси абсцисс в виде штрихов, пересекающих рамку, и логарифмической разметкой оси ординат. Огромное число других значений, необходимых для построения чертежа, принимается по умолчанию.Далее можно начертить собственно график функции, используя, например, ее задание через вектор значений:
ФУНКЦИЯ U МАСШТАБ 100 ЦВЕТ КРАСНЫЙ РИСУЙ
Аналогично наносим на чертеж дополнительные элементы:
ПОДЗАГОЛОВОК ВПРАВО ТЕКСТ" Y=SIN 3X + SIN 120X" РИСУЙ
и т.д. Указанные действия можно не только непосредственно исполнять, но и компилировать, задавая впоследствии их исполнение через одно слово — имя скомпилированной программы.