Возможности работы со списками проиллюстрированы в Программе 13 (см.
Приложение). Без использования списка в этой программе невозможно было бы
проставить размеры длин ступеней вала, так как местоположение первой размерной
линии может быть выбрано только тогда, когда все ступени вала построены (точнее,
все диаметры ступеней введены). В связи с этим при построении ступеней требуется
запоминать точки для последующего построения размеров. Это можно сделать только
с помощью списка.
Рассмотрим, как используются функции работы со списками в Программе 13.
При построении первой ступени формируется список sp, состоящий из одного
элемента -списка точек р2 и рЗ:
При построении каждой следующей ступени в этот список добавляется новая пара
точек:
Обратим внимание на то, что новая пара добавляется в начало списка, т.е.
порядок следования пар точек в списке обратен порядку построения ступеней
вала.
т.е. на каждом шаге цикла выбирается очередной элемент списка sp и
записывается в рр.
Верхняя и нижняя точки для простановки размера выделяются из рр следующим
образом:
(Отметим, что диаметры будут проставляться, начиная с последней ступени, т.е.
в том порядке, в котором организован список sp.)
Чтобы проставить размеры длин, необходимо сначала проставить один
горизонтальный размер с опорой на базовую точку, а затем последовательно
проставлять остальные размеры от базы. Для этого:
После этого организуется цикл по простановке размеров с помощью FOREACH
аналогично тому, как это было сделано при простановке диаметров. Однако теперь
уже размеры будут проставляться в том же порядке, в каком изображался вал, т.е.
слева-направо.
На примере Программы 13 можно также видеть, как функции LIST, CAR и CADR
используются для внутрипрограммного построения точек. Например,
определяет точку p3, координата Х которой смещена на десять единиц влево по
отношению к точке р1. Аналогичный результат был бы получен с помощью (POLAR pi
PI 10).
5. АВТОЛИСП -ЯЗЫК СОЗДАНИЯ И АНАЛИЗА ИЗОБРАЖЕНИЙ
5.1. Специфика языка Автолисп как части системы AutoCAD
Помимо возможностей Автолиспа, свойственных и другим языкам программирования,
а также возможностей по обработке списков, двойственных версиям языка Лисп, в
Автолиспе имеются и специфические возможности, связанные с тем, что программа на
Автолиспе работает в среде, созданной системой AutoCAD. Эта среда
характеризуется:
определенными параметрами (системными переменными AutoCAD);
определенной организацией экрана (графического или текстового);
наличием изображения (чертежа), построенного на графическом экране и
состоящего из примитивов и блоков;
определенной организацией данных об изображении в памяти ЭВМ - наличием
графической базы данных чертежа, в которой содержатся описания примитивов и
блоков, значения системных переменных, таблицы слоев, типов линий и т.д.
Программа на Автолиспе может воспринимать особенности этой среды и оказывать
влияние на нее.
5.2. Объектная привязка с помощью функции OSNAP
Помимо описанных ранее способов задания точки в программе имеется возможность
задания точки путем объектной привязки, т.е. так же, как это делается в
графическом диалоге с помощью команды OSNAP:
(OSNAP < точка > < режим >)
Предполагается, что на чертеже имеется объект, проходящий через заданную <
точку >.
Аргумент < режим > - это строка текста, характеризующая режим привязки
точно так же, как для команды OSNAP. Например:
"CEN" - центр окружности или дуги;
"BND" - конечная точка отрезка, дуги;
"INS" - базовая точка блока и др.
Если точка, соответствующая заданному режиму, не найдена, то возвращается
NIL.
5.3. Воздействие на экран. Функции GRAPHSCR, TEXTSCR, GRCLEAR, REDRAW,
MENUCMD
В ходе выполнения программа может влиять на состояние экрана.
(GRAPHSCR), (TEXTSCR)
С помощью этих функций можно переключать экран с текстового на графический и
обратно.
Переключение экрана в текстовый режим может оказаться целесообразным, если
программа выводит последовательность строк текста с помощью функций типа
PRINT.
(GRCLEAR)
Функция очищает поле чертежа графического экрана.
(REDRAW)
Функция позволяет восстановить картину, стертую с помощью GCLEAR. Кроме того,
EDRAW может применяться для перерисовывания текущего экрана и выполнять ту же
роль, что и команда графического редактора REDRAW.
(MENUCMD < текст >)
Эта функция позволяет переключаться среди подстраниц в меню ACAD, т.е.,
например, вводить на экран тот раздел меню, который в данный момент необходим
пользоватею. Это дает возможность упростить диалог пользователя при
комбинированной работе с программой и графическим редактором.
Аргумент < текст > имеет следующий вид:
"<тип меню> = < имя раздела меню>"
Для экранного меню <тип меню> - S (другиe типы см. в "Руководстве
пользователя"(АВТОКАД, версия 10. Autodesk Ltd., 1989)).
Таким образом (MENUCMD "S = S3 ") будет лзывать на экран раздел меню S3.
Применение этой функции предполагает знание структуры файла меню, который
используется в процессе работы данной программы (например, стандартного файла
меню ACAD.MNU).
Все перечисленные здесь функции возвращают NIL.
5.4. Работа с системными переменными и символьными таблицами. Функции GETVAR
и SETVAR
Тем, кто полностью освоил графический редактор ACAD, известно, что при вызове
ACAD устанавливаются по умолчанию значения множества системных переменных,
влияющих на режим работы с редактором. В ходе графического диалога эти значения
можно переустанавливать командой SETVAR. Полный список системных переменных, их
типов и значений приведен в "Руководстве пользователя".
(GETVAR <имя переменной >)
С помощью этой функции значение системной переменной может быть считано в
программу. < Имя переменной > должно указываться как строка текста.
(SETVAR < имя переменной > < значение переменной >)
С помощью этой функции значение системной переменной может быть
переустановлено. Возвращается устанавливаемое значение.
Следует иметь в виду, что некоторые системные переменные определены только
для гения (см. "Руководство пользователя"). Такие переменные нельзя
переустановить. Примеры:
(SETQ A (GETVAR "ANOBASE"))
возвращается и записывается в А действительное число, определяющее
направление угла 0 в этом чертеже.
(SETVAR "CMDECHO" 0)
задается значение 0 для переменной CMDECHO, что приведет к исчезновению "эха"
выполняемых команд.
Режим, заданный в последнем примере, удобен на практике, в особенности тогда,
когда программа интенсивно выдает пользователю полезные сообщения, между
которыми эхо команд будет восприниматься как "шум". В любом случае отключение
эха ускоряет выполнение программы.
В ходе работы с редактором ACAD создаются также символьные таблицы LNTYPE,
LAYER другие, определяющие типы линий, слои, ви-ы, блоки, гарнитуры шрифтов.
Данные из этих таблиц могут быть прочитаны с помощью функций TBLNEXT,TBLSEARCH,
описанных в "Руководстве по программированию".
5.5. Формирование и анализ наборов примитивов. Функции типа SS
Программы на Автолиспе могут работать с бъектами специальных типов -
графическими бъектами. К таким объектам относятся:
примитивы ACAD, имеющиеся на экране в процессе работы программы;
совокупности таких примитивов, называемые в Автолиспе наборами.
Графические объекты могут быть "введены в программу", т.е. возможно
именование этих объектов с помощью имен переменных, так же как именование чисел,
строк текстов, точек. После этого можно оперировать с этими объектами, обращаясь
к ним по именам. В том числе можно анализировать и изменять характеристики этих
объектов, что приводит к изменению изображения на экране.
Для лучшего представления о том, как происходит работа с примитивами и
наборами, полезно понять следующее. При выделении каким-либо способом примитива
или набора из базы данных Автолисп формирует внутрипрограммное имя этого объекта
(примитива или набора). На самом деле - это не имя, а ссылка на объект, но для
программиста это не имеет значения. Программист поступает следующим образом: он
записывает это "имя" в какую-либо переменную с помощью SETQ. Затем эта
переменная может быть использована для любого указания на объект. Например, она
может быть подставлена в качестве аргумента в команду, требующую указания
примитива или группы примитивов. Далее такая технология будет проиллюстрирована
на примере.
Основное средство определения наборов примитивов - функция SSGET.
(SSGET [<режим>] [<точка1> [<точка2>]])
Функция возвращает определение (внутрипрограммное имя) выбранного примитива
или отбора (совокупности примитивов), которое может быть "записано" в неременную
Автолиспа (с помощью SETQ);
Если аргумент < режим > отсутствует, то создается набор, выбранный
средствами графического редактора (указание на объекты, захват в рамку и т.п.).
При этом программа выдает такой же запрос на указание способа выбора объетов, а
затем запросы на выбор, как и при работе с графическим редактором.
Возможны следующие значения режима:
"Р", в русском варианте "Т" - выбирается текущий набор;
"L", русское "П" - выбирается последний сформированный на чертеже
примитив;
"W", русское "Р" < точка1 > <точка2> - выбираются примитивы,
попавшие в рамку с углами в < точке1 > и <точке2>;
"С", русское "С" <точка1> <точка2> - выбираются примитивы,
пересекаемые рамкой. Кроме этих возможны еще две конструкции:
(SSGET < точка >)
Здесь выбираются примитивы, проходящие через < точку >.
(SSGET "х" < фильтр >)
Здесь < фильтр > - список, элементами которого являются одна или
несколько точечных пар. Каждая точечная пара строится по следующей форме:
(< код >.< значение >) Здесь <код> - одно из принятых в
Автолиспе чисел, показывающих, по какому признаку выбираются примитивы,
например;
0 - примитивы одного типа;
2 - примитивы, входящие в один блок (INSERT) ;
6 - по типу линии;
7 - по гарнитуре шрифта;
8 - по имени слоя;
62 - по цвету и др.
< значение > - значение признака.
Например,
(SSGET "X" '((8 . "SLI")))
вернет набор, включающий все примитивы, находящиеся на слое, именованном как
"SLI";
(SSGET "X" '((0. "LINE") (62 .1)))
вернет набор, состоящий из всех линий красного цвета (1 - номер красного
цвета).
Выполнение последовательности функций
(SETQ sdel (SSGET "X" '((6 . "CENTER"))))
(COMMAND "ERASE" sdel)
приведет к стиранию всех примитивов, изображенных осевыми линиями.
Следующие функции позволяют анализировать набор, получаемый с помощью
SSGET
(SSLENGTH < набор >)
Функция SSLENGTH возвращает целую величину, представляющую число примитивов в
наборе. Таким образом, программа может "узнать", сколько объектов было выделено
пользователем. По известному числу примитивов можно организовать цикл по
обработке последовательно каждого примитива.
Здесь и далее < набор > - это имя переменной набора, т.е. переменной,
определенной ранее следующим образом:
(SETQ < набор > (SSGET...))
(SSNAME < набор > < номер >)
Функция SSNAME возвращает внутрипрог-раммное имя примитива из < набора
>, идущего под соответствующим номером.
Здесь < номер > - целая величина. Первый по порядку примитив имеет
номер 0. Если < номер > меньше 0 или больше максимального номера примитива
в наборе, то возвращается NIL. Таким образом, конструкция вида
(SETQ < переменная > (SSNAME < набор > <номер>))
позволяет выделить примитив из < набора >, записав его имя в <
переменную >.
(SSMEMB <примитив> <набор>)
Функция SSMEMB проверяет, входит ли < примитив > в < набор >.
Если да, то возвращается имя примитива, если нет, - то NIL.
Здесь и далее < примитив > - имя переменной, значение которой записано
с помощью SETQ, например, путем применения функции SSNAME.
Следующие функции позволяют изменять состав набора:
(SSADD [< примитив > [< набор >]])
Функция SSADD без аргументов создает новый пустой набор. SSADD с аргументом
< примитив > создает новый набор, содержащий только указанный примитив.
Если указаны оба аргумента, то функция добавляет указанный примитив в набор.
Если этот примитив в наборе уже имеется, то ничего не изменится. Функция
возвращает имя набора. Например,
(SETQ S (SSADD р1))
Эта конструкция формирует новый набор, состоящий из одного примитива.
Предполагается, что pi - переменная, в которую ранее было записано имя
примитива. Имя созданного набора записывается в переменную S, которую теперь
можно использовать для указания на набор.
(SSDEL <примитив> <набор>)
Функция удаляет < примитив > из < набора >, если он там
присутствовал, иначе - ничего не меняется. Возвращается имя набора.
Пример работы с набором:
(SETQ SS (SSGET))
; Здесь образуется пауза, в ходе которой пользователь
; укажет несколько объектов на экране (примитивов).
; После нажатия <ENTER> программа определяет
; набор. Формируется внутрипрограммное имя
;этого набора, которое возвращается функцией SSGET в переменную SS
;Теперь в программе данный набор можно обозначить как SS.
(SETQ L (SSLENGTH SS))
;Определено количество примитивов в наборе SS и записано в L.
(SETQ SL (SSNAME SS (1-L)))
;Выделен последний примитив в наборе SS, его внутрипрограммное
;имя возвращено функцией SSNAME в переменную SL
(SETQ SN (SSADD SL))
;Создан новый набор, содержащий последний
;примитив из набора SS. Имя нового набора записано в переменную SN.
(SETQ SS (SSDEL SL SS))
;Из SS удален последний элемент. Получавшийся в результате набор будет ;также
назван SS.
(COMMAND "ERASE" SS)
;Набор SS удаляется с экрана, т.е. стираются все примитивы, ранее ;
помеченные пользователем, кроме последнего (SL).
5.6. Работа с примитивами. Функции ENTNEXT, ENTLAST, ENTSEL, ENTDEL.
Операция REDRAW над примитивом
Примитивы в ACAD могут быть простыми (линия, окружность, строки текста,...)
или составными (полилинии, вставки блоков). Составные примитивы делятся на
субпримитивы.
Например, для полилинии субпримитивами являются вершины, для вставки блоков -
атрибуты. С помощью функций типа SS, рассмотренных в п.5.5, могут быть получены
либо наборы примитивов, либо примитивы (простые или составные), но не
субпримитивы. Функции, рассматриваемые в данном параграфе, позволяют работать с
примитивами и субпримитивами.
Выбор примитивов:
(ENTNEXT [<примитив>])
Функция возвращает внутрипрограммное имя примитива или субпримитива:
если < примитив > не указан, то возвращается первый из примитивов
чертежа (неудаленных);
если указан простой примитив, то ENTNEXT возвращает примитив, следующий за
указанным;
если указан составной примитив; то ENTNEXT возвращает первый субпримитив
указанного примитива;
если указан субпримитив, то ENTNEXT возвращает субпримитив, принадлежащий
тому же примитиву, что и указанный, и следующий за указанным;
если запрашиваемый объект отсутствует, то возвращается NIL.
(ENTLAST).
Функция возвращает внутрипрограммное имя последнего примитива на чертеже
(неудаленного). Субпримитив с помощью ENLAST не выделяется.
(ENTSEL [ < подсказка > ])
Эта функция создает паузу и может вывести текст < подсказки >.
Пользователь в ответ должен указать примитив на экране.
Функция возвращает результат в виде списка из двух элементов:
первый элемент - внутрипрограммное имя примитива;
второй элемент - точка, в которой было проведено указание, эта точка
возвращается в виде списка координат.
Возвращаемый результат может быть "записан в переменную" с помощью SETQ. Эта
переменная впоследствии может быть задана в ответ на любую команду ACAD (в
функции COMMAND), требующую выбрать объект и учитывающую точку указания. Кроме
того, результат, записанный в данную переменную, может быть "разложен .на
составные части": (CAR < переменная >) возвращает имя примитива, (CADR
< переменная >) вернет точку указания.
Удаление, восстановление и обновление примитивов:
(ENTDEL < примитив >)
С помощью этой функции примитив может быть удален из чертежа. Субпримитивы
таким способом не удаляются.
Пока не кончился сеанс работы с данным чертежом, удаленный примитив может
быть восстановлен с помощью той же функции (ENTDEL < примитив >)
(REDRAW < примитив >)
Функция возобновляет изображение примитива после очистки экрана функцией
GRCLEAR. Таким образом, рассмотренная в п.5.3 функция REDRAW может также
управлять изображением примитива:
(REDRAW <примитив> <режим>)
Действие REDRAW зависит от значения аргумента < режим >: 1 - примитив
перерисовывается; 2 - примитив стирается; 3 - примитив подсвечивается; 4 -
подсветка примитива отменяется.
5.7. Работа с данными примитивов и модификация примитивов. Функции ENTGET,
ENTMOD, ENTUPD
Каждый примитив с точки зрения системы AutoCAD представляет собой достаточно
сложную совокупность данных. Представление об этих данных дают правила
расположения данных в DXF-файле, который является текстовым описанием чертежа
(см. "Руководство пользователя").
Программа на Автолиспе может сформировать сложный список, в котором будут
содержаться все данные о примитиве. После этого средствами обработки списков
можно выделять, анализировать и модифицировать отдельные лепные. За модификацией
данных примитива может следовать отображение модифицированного примитива на
чертеже.
Таким образом, программа на Автолиспе может анализировать и видоизменять
чертеж, построенный, например, в режиме диалога с редактором ACAD.
Формирование списка данных примитива:
(ENTGET < примитив >)
Функция формирует и возвращает список, содержащий данные о примитиве.
Структура списка напоминает перечень данных о примитиве в DXF-файле и зависит
от типа примитива и от того, насколько характеристики примитива соответствуют
умолчанию.
Правила построения списка:
каждый элемент списка является списком (будем называть его субсписком);
большинство субсписков имеют вид точечных пар (кроме субсписков, описывающих
точки);
первый элемент каждого субсписка - это код данных, второй элемент (и
последующие) - значение (значения), соответствующие коду;
первый субсписок имеет вид (-1 . имя-примитива);
второй: (0. тип-примитива);
третий: (8. имя-слоя);
далее следуют некоторые атрибуты примитива в том случае, если их значения
отличаются от значений по умолчанию, набор этих атрибутов зависит от версии
ACAD, примером может служить (62. код-цвета);
далее следуют характеристики примитива, состав которых зависит от типа
примитива.
Например, для отрезка прямой (тип = "LINE"):
(10 координаты-начальной-точки)
(11 координаты-конечной-точки)
Для окружности (тип = "CIRCLE"):
(10 координаты-центра)
(40. радиус)
Для дуги (тип = "ARC"):
(10 координаты-центра)
(40. радиус)
(50 . начальный-угол)
(51. конечный-угол)
Пример списка, возвращаемого функцией ENTGET для окружности, находящейся на
слое "О", синего цвета (номер цвета - 5) с центром в точке (2 2 0) и с радиусом
1.5, изображенной пунктирной линией ("DASHED"):
((-1. <внутрипрограммное-имя-примитива>)
(0 . "CIRCLE")
(8. "0")
(6 . "DASHED")
(62.5)
(10 2.0 2.0 0.0) (40. 1.5)
)
Модификация примитива:
(ENTMODE < примитив >)
С помощью этой функции можно изменить изображение примитива на экране.
Предполагается, что до обращения к ENTMOD происходила модификация данных в
списке, полученном с помощью ENTGET.
Пример:
(SETQ PR (ENTNEXT))
; Выделяется первый примитив в базе данных,
; имя которого возвращается в PR
(SETQ SPR (ENTGET PR))
;Формируется список из данных, описывающих примитив PR, этот список
;возвращается в SPR
; Здесь следуют операции над списком SPR
; по изменению каких-либо данных в этом списке
(ENTMODE SPR)
После выполнения последней функции на экране вместо примитива PR, ранее
выбранного пользователем, появится "обновленный" примитив, отличающийся
какими-либо характеристиками. Для программы это по-прежнему будет примитив PR, и
с ним можно продолжать работу.
На применение ENTMODE накладываются некоторые ограничения:
не может быть изменен тип примитива;
такие атрибуты как Тип линии (и некоторые другие), могут принимать только те
значения, которые к данному моменту определены в сеансе работы с ACAD;
однако слой может быть задан и новый, отсутствовавший к данному моменту.
Функция ENTMODE может выполняться над примитивом или субпримитивом. Если
ENTMODE выполняется над субпримитивом, то видимого изменения на экране не
происходит. 122
(ENTUPD < примитив >)
С помощью этой функции можно изменить изображение примитива после того, как
изменены субприпримитивы этого примитива с помощью ENTMODE.
Однократное изменение сложного примитива происходит гораздо быстрее, чем его
многократная перерисовка после изменения каждого субпримитива.
5.8. Примеры
Приведенные ниже примеры демонстрируют различные применения функций для
работы с примитивами и наборами. Каждая из этих программ - не только
иллюстрация, но и практически полезная функция, которая может пригодиться
конструктору, работающему с редактором AC AD. Эти программы иллюстрируют
возможности расширения системы AutoCAD с помощью Автолиспа.
; Программа 10: Удаление предпоследнего элемента
; чертежа [4]
(DEFUN С: ERASE2 ( )
(SETQ secondlast (ENTDEL (ENTLAST)))
(ENTDEL (ENTLAST))
(ENTDEL secondlast)
)
Известно, что в редакторе ACAD имеется команда ERASE ("СОТРИ"), которая
позволяет стереть последний элемент чертежа или элемент, указываемый
конструктором. Однако, оба эти способа могут оказаться неудобными в следующей
ситуации: конструктор создает сложный насыщенный чертеж и ему не сразу удается
правильно построить нужный элемент. Конструктор хотел бы повторить попытку, но
при этом сохранить неправильный элемент в качестве "отправной точки", а после
удачного построения - стереть этот элемент. Имея описанную выше функцию, он
сможет это сделать, подав команду ERASE2.
Программа" 10 решает задачу просто и изящно:
стирается последний примитив (но его имя записывается в переменную
secondlast);
стирается примитив, ставший последним, т.е. бывший предпоследний;
путем повторного применения функции ENTDEL по отношению к примитиву
secondlast стертый последний элемент восстанавливается на экране.
Программа 11, приведенная ниже, дает возможность значительно упростить
решение задачи, нередко встречающейся на практике при конструировании сложных
объектов, - перенесение всех примитивов, находящихся на одном слое, на другой
слой [6]
.
Средства ACAD позволяют решить такую задачу только с помощью метода
последовательного указания всех переносимых примитивов -долгого и неудобного.
Алгоритм, реализуемый Программой 11:
Установить переменную found в NIL (found "NIL свидетельствует о том, что на
слое не найдено ни одного примитива).
Ввести с клавиатуры и запомнить имена старого и нового слоя.
Выдать сообщение о том, что поиск начался.
Записать в entn имя первого примитива чертежа
ПОКА entn существует ЦИКЛ
Получить в cnt список, соответствующий entn
ЕСЛИ имя слоя в списке ent совпадает с именем
старого слоя ТО
Заменить слой примитива новым слоем
Установить found =t (примитив найден)
ВСЕ ЕСЛИ
Записать в entn имя очередного примитива
ВСE ЦИКЛ ПОКА
ЕСЛИ founds NIL (примитив отсутствует) ТО
Выдать сообщение об отсутствии примитивов
на слое
ВСЕ ЕСЛИ
КОНЕЦ
; Программа 11 Перенос примитивов с одного слоя
; на другой
(DEPUN C:CNGLAY (/cmd entn ent)
(SETVAR "CMDECHO" 0)
(SETQ found NIL)
(SETQ oldlyr (STRCASE (GETSTRINO