Справочник программиста на персональном компьютере фирмы IBM. Вывод на терминал
Страница 38. Рисование линий на экране. Часть 2




   Оператор DRAW может  включать  строковые  переменные,  которые
состоят из набора допустимых кодов.  Это свойство позволяет прог-
рамме повторно использовать  части  фигур в различных рисунках. В
операторе  DRAW имя строки должно быть помещено за буквой X и  за
ним должны следовать точка с запятой. Например:

100 S$ = "U12R15U45L32"
110 DRAW "XS$;"

В одном операторе DRAW может содержаться несколько строк, переме-
жаемых другими кодами.  Отметим, что любые числа, используемые  с
кодами в операторах DRAW могут сами быть переменными. Таким обра-
зом  с  помощью  одного оператора DRAW могут  выводиться  фигуры,
отличающиеся по форме, цвету, масштабу  и ориентации. Надо помес-
тить знак равенства между буквенным кодом и именем переменной,  а
за именем поместить точку с запятой.   Например, чтобы установить
код  цвета, определяемый переменной, напишите  DRAW  "C=PCOLOR;".
Компилятор Бейсика требует,  чтобы  ссылка на эти переменные осу-
ществлялась с помощью функции VARPTR$. В этом случае такой опера-
тор будет иметь вид  DRAW  "X"  +  VARPTR$(S$)  или  DRAW  "C=" +
VARPTR$(PCOLOR). Сложные рисунки могут быть сохранены в массиве и
затем возвращены на экран в любой момент. Обсуждение этого вопро-
са см. в [4.4.6].

   Низкий уровень.


   Нижеприведенная  процедура использует алгоритм Брезенхэма  для
вывода прямой линии, соединяющей любые две точки.  Она использует
функцию  BIOS установки точек и ее можно убыстрить если  заменить
эту функцию на встроенную процедуру,  использующую прямое отобра-
жение  в  память.  Как и все быстрые алгоритмы  данная  процедура
избегает операций умножения и деления.  Линия рассматривается как
набор сегментов двух типов: тех которые расположены диагонально и
тех, которые расположены горизонтально или вертикально. Для линий
с  наклоном  больше 1 прямые  сегменты  вертикальны, в  противном
случае они горизонтальны;  первая  задача алгоритма состоит в вы-
числении наклона. Затем вычисляется выравнивающий фактор, который
следит чтобы некоторое число прямых  сегментов имело большую дли-

ну,  чем остальные.  И, наконец, сложный цикл поочередно  выводит
диагональные и прямые сегменты.  BX поочередно принимает то поло-
жительные,  то отрицательные значения, отмечая какой тип сегмента
выводится.  Ниже готовятся  данные для вывода диагонали из одного
угла экрана в противоположный:

;---в сегменте данных
START_X                   DW   0
END_X                     DW   319
START_Y                   DW   0
END_Y                     DW   199
COLOR                     DB   2
DIAGONAL_Y_INCREMENT      DW   ?
DIAGONAL_X_INCREMENT      DW   ?
SHORT_DISTANCE            DW   ?
STRAIGHT_X_INCREMENT      DW   ?
STRAIGHT_Y_INCREMENT      DW   ?
STRAIGHT_COUNT            DW   ?
DIAGONAL_COUNT            DW   ?

;---установка режима дисплея
               MOV  AH,0       ;функция установки режима
               MOV  AL,4       ;цветной 320*200
               INT  10H        ;установка режима
;---установка начальных инкрементов для каждой позиции точки
               MOV  CX,1       ;инкремент для оси x
               MOV  DX,1       ;инкремент для оси y
;---вычисление вертикальной дистанции
               MOV  DI,END_Y   ;вычитаем координату начальной
               SUB  DI,START_Y ;точки из координаты конечной
               JGE  KEEP_Y     ;вперед если наклон < 0
               NEG  DX         ;иначе инкремент равен -1
               NEG  DI         ;а дистанция должна быть > 0
KEEP_Y:        MOV  DIAGONAL_Y_INCREMENT,DX
;---вычисление горизонтальной дистанции
               MOV  SI,END_X   ;вычитаем координату начальной
               SUB  SI,START_X ;точки из координаты конечной
               JGE  KEEP_X     ;вперед если наклон < 0
               NEG  CX         ;иначе инкремент равен -1
               NEG  SI         ;а дистанция должна быть > 0
KEEP_X:        MOV  DIAGONAL_Y_INCREMENT,CX
;---определяем горизонтальны или вертикальны прямые сегменты
               CMP  SI,DI      ;горизонтальные длиннее?
               JGE  HORZ_SEG   ;если да, то вперед
               MOV  CX,0       ;иначе для прямых x не меняется
               XCHG SI,DI      ;помещаем большее в CX
               JMP  SAVE_VALUES;сохраняем значения
HORZ_SEG:      MOV  DX,0       ;теперь для прямых не меняется y
SAVE_VALUES:   MOV  SHORT_DISTANCE,DI  ;меньшее расстояние
               MOV  STRAIGHT_X_INCREMENT,CX  ;один из них 0,
               MOV  STRAIGHT_Y_INCREMENT,DX  ;а другой - 1.

;---вычисляем выравнивающий фактор
               MOV  AX,SHORT_DISTANCE  ;меньшее расстояние в AX
               SHL  AX,1       ;удваиваем его
               MOV  STRAIGHT_COUNT,AX  ;запоминаем его
               SUB  AX,SI      ;2*меньшее - большее
               MOV  BX,AX      ;запоминаем как счетчик цикла
               SUB  AX,SI      ;2*меньшее - 2*большее
               MOV  DIAGONAL_COUNT,AX  ;запоминаем
;---подготовка к выводу линии
               MOV  CX,START_X ;начальная координата x
               MOV  CX,START_Y ;начальная координата y
               INC  SI         ;прибавляем 1 для конца
               MOV  AL,COLOR   ;берем код цвета
;---теперь выводим линию
MAINLOOP:      DEC  SI         ;счетчик для большего расстояния
               JZ   LINE_FINISHED  ;выход после последней точки
               MOV  AH,12      ;функция вывода точки
               INT  10H        ;выводим точку
               CMP  BX,0       ;если BX < 0, то прямой сегмент
               JGE  DIAGONAL_LINE  ;иначе диагональный сегмент
;---выводим прямые сегменты
               ADD  CX,STRAIGHT_X_INCREMENT  ;определяем инкре-
               ADD  DX,STRAIGHT_Y_INCREMENT  ;менты по осям
               ADD  BX,STRAIGHT_COUNT  ;фактор выравнивания
               JMP  SHORT MAINLOOP  ;на следующую точку
;---выводим диагональные сегменты
DIAGONAL_LINE: ADD  CX,DIAGONAL_X_INCREMENT  ;определяем инкре-
               ADD  DX,DIAGONAL_Y_INCREMENT  ;менты по осям
               ADD  BX,DIAGONAL_COUNT  ;фактор выравнивания
               JMP  SHORT MAINLOOP  ;на следующую точку
LINE_FINISHED:

 
« Предыдущая статья   Следующая статья »