Глава 12. AutoLISP в среде Visual LISP.
Часть 1.
Основным средством разработки приложений в версиях системы AutoCAD с 10-й по 15-ю (т. е. 2000) является язык программирования AutoLISP — вариант языка LISP, в который добавлены функции доступа к объектам и таблицам AutoCAD. Основой языка LISP является работа со списками, которые могут иметь произвольную длину и включать элементы разной природы (числа, текстовые строки и т. д.). В то же время в языке пользователю доступны обычные арифметические вычисления, логические операции, работа с файлами и т. д.
Рис. 12.1. Диалоговое окно Visual LISP для AutoCAD
С помощью AutoLISP можно писать программы или вводить в командной строке выражения, которые затем вычисляет система AutoCAD. Для чтения и разбора выражений в систему AutoCAD встроен интерпретатор AutoLISP.
Для написания и отладки программ и приложений на языке AutoLISP в состав системы AutoCAD 2000 введена интегрированная среда разработки Visual LISP, обращение к которой осуществляется через пункт AutoLISP, Редактор Visual LISP (Visual LISP Editor) падающего меню Сервис (Tools) или команду VLISP, которые вызывают диалоговое окно Visual LISP для AutoCAD (Visual LISP for AutoCAD) (рис. 12.1).
Большая часть излагаемых в данной главе вопросов языка может быть также использована и для изучения теми читателями, которые пишут или собираются писать программы на языке AutoLISP в версиях системы с 10-й по 14-ю.
В AutoCAD входит интерпретатор AutoLISP, начинающий работать, если вы в командной строке в ответ на стандартную подсказку Команда: (Command:) введете открывающую скобку "(". В этот момент AutoCAD настраивается на прием и вычисление выражения AutoLISP, которое должно иметь форму списка, т. е. начинаться открывающей скобкой и заканчиваться закрывающей скобкой — ")", а элементы списка должны отделяться друг от друга пробелами. Например, для получения суммы трех чисел вы можете ввести:
(+ 223.75 16.002 89.899)
Как только вы завершите ввод своего выражения нажатием на клавишу <Enter>, система AutoCAD вычислит его и выдаст результат (рис. 12.2):
329.651 (замечу, что десятичная точка в вещественных числах играет роль разделителя между целой и дробной частями).
Рис. 12.2. Использование вычислителя выражений интерпретатора AutoLISP
Замечание
Клавиатурный ввод выражений AutoLISP встречается сравнительно редко. Обычно выражения AutoLISP записываются в текстовый файл с расширением Isp, который затем загружается в AutoCAD с помощью функции load (см. разд. 12.2).
В приведенном примере для вычисления было подано выражение, открывающееся и закрывающееся круглыми скобками и состоящее из четырех элементов: первый — имя функции сложения ("+"), остальные — аргументы функции, т. е. слагаемые (223.75, 16.002 и 89.899). Это иллюстрирует форму вычисляемых выражений языка AutoLISP:
(<функция> [<аргумент1> [<аргумент2> ... [<аргументN> ]...]])
Здесь <функция> — это имя функции; <аргумент1>, <аргумент2>, ... , <аргументN> — аргументы функции, разделяемые, по крайней мере, одним пробелом. Квадратные скобки в приведенной форме указывают на то, что, начиная с некоторого места, аргументы могут быть опущены. Количество аргументов зависит от синтаксиса функции. Существуют функции, которые вызываются без аргументов. В данной книге изучается значительная часть встроенных функций языка AutoLISP (не рассматриваются функции, работающие с меню и диалоговыми окнами). Полный алфавитный список всех функций AutoLISP приведен в приложении 8.
С точки зрения AutoLISP все, что заключено в круглых скобках, является списком, который нужно обработать как вычисляемое выражение.
Основное правило AutoLISP — баланс скобок, т. е. сколько скобок открыто, столько должно быть и закрыто. Иначе AutoCAD, обнаружив дисбаланс (например, если пользователь забыл ввести конечную скобку), выдаст об этом соответствующее сообщение (рис. 12.3).
Рис. 12.3. Сообщение о дисбалансе скобок
Для разрешения ситуации, изображенной на рис. 12.3, необходимо ввести закрывающую скобку и нажать клавишу <Enter>. После этого вы получите результат вычисления. В интегрированной среде разработки Visual LISP (см. разд. 12.7) предусмотрены механизмы проверки скобок в файлах с текстами программ.
В качестве любого аргумента функции может быть введено другое вычисляемое выражение, ограниченное скобками, или символ AutoLISP (о символах см. разд. 12.2).
Выражения AutoLISP могут быть как простыми, так и составными, т. е. в выражениях могут в качестве элементов использоваться другие выражения, причем разделяющими знаками в выражениях являются круглые скобки, пробелы и двойные кавычки (двойные кавычки должны ограничивать с двух сторон текстовые константы — о текстовых константах см. разд. 12.2). Например:
(- (+ 56.022 78.11) (+ 123.77 78.0))
Здесь "-" — имя функции (вычитание), имеющей два аргумента в виде выражений, каждое из которых является операцией сложения (т. к. в них использована функция сложения "+"). Сначала интерпретатор AutoLISP вычислит выражения во внутренних скобках (в нашем примере получатся результаты 134.132 и 201.77), а затем подставит полученные значения и вычислит выражение (-134.132 201.77), что должно дать результат -67.638 (отрицательное число).
Интерпретатор AutoLISP вычисляет значение каждого введенного выражения и возвращает его в среду AutoCAD. На рис. 12.2 видно, что возвращено значение 329.651. Возвращаемое значение, в свою очередь, либо может быть передано для дальнейших вычислений в выражение более высокого уровня, как в последнем примере (аргументы функции верхнего уровня были вычисляемыми выражениями), либо может быть сохранено в переменной (человеку, знакомому с программированием, ясно, что это такое — это именованные участки памяти для хранения данных) с помощью функции присвоения setq, имеющей следующий синтаксис:
(setq <переменная1> <выражение1> [<переменная2> <выражение2> ... [<переменнаяN> <выражениеN>] ... ] )
Функция setq может использоваться с любым количеством аргументов, которое должно быть обязательно четным и не менее 2. В качестве аргументов <переменная> могут использоваться любые символы (имена) AutoLISP, не занятые в наименованиях функций. О символах речь пойдет в следующем разделе. В качестве аргументов <выражение> могут использоваться выражения AutoLISP, имена переменных или константы (например, числа).
Функция setq — основное средство для сохранения значений, возвращаемых другими выражениями. Пример:
(setq my_d (- (+ 56.022 78.11) (+ 123.77 78.0)))
Значение выражения, которое вычислялось в примере, приведенном выше, будет сохранено в переменной my_d.
В дальнейшем прочитать значение переменной my_d можно с помощью операции ! (восклицательный знак). Если в командной строке системы AutoCAD в ответ на стандартную подсказку Команда: (Command:) ввести !my_d, то система выдаст текущее значение переменной my_d (в нашем примере: 329.651).
В текст программы полезно вставлять комментарии, поясняющие, например, особенности алгоритма. Комментарием считается часть строки, начиная с символа ";" (точка с запятой), после которого не следует сразу же символ "|", и кончая последним символом строки. Закомментированной может быть вся строка программы.
Если комментарий нужно вставить в середину строки, тогда началом комментария должны быть символы ";¦", а концом комментария — символы ";|" (причем конец комментария может располагаться на другой строке).
Примеры (для наглядности комментарии подчеркнуты):
; Закомментирована вся строка
(setq two_pi (+ pi pi)) ; Закомментирован конец строки
(setq md (+ 56.022 ;| внутренний
комментарий | ;78.11))
(command "_CIRCLE" '(0 0) 50); рисование окружности
Символы AutoLISP— это слова, состоящие из группы буквенно-цифровых знаков и являющиеся именами функций или переменных, используемых для хранения данных пользователя. В символах AutoLISP не должны использоваться разделители выражений, к которым относятся круглые скобки, пробелы и двойные кавычки, и служебные знаки -"." (точка), "," (запятая), ";" (точка с запятой), " ' " (апостроф), "/" (косая черта), "\" (обратная косая черта). Написание символов не должно соответствовать написанию .чисел (например, символ не должен состоять из одних цифр).
Зарезервированными являются символы, используемые как имена функций, а также некоторые обозначения констант. Полный перечень функций языка AutoLISP приведен в приложении 8.
Замечание
В именах символов AutoLISP регистр (верхний или нижний) не имеет значения. Поэтому setq, SETQ, SETq, Setq, SETQ обозначают одну и ту же функцию setq.
AutoLISP работает с объектами следующих типов:
Замечание
Тип символа может быть получен с помощью функции type.
Символам AutoLISP могут быть присвоены значения любых типов (этим язык AutoLISP выгодно отличается от языков высокого уровня типа Fortran). Пользователь в явном виде может задавать константы следующих типов: целые, вещественные, строковые и списковые.
Целые числа могут быть как положительными, так и отрицательными или нулями. Диапазон допустимых значении варьирует от -2,147,483,648 до +2,147,483,647 (крайние значения включены в допустимые: запятые могут использоваться как разделители триад цифр).
Замечание
Функция getint, запрашивающая ввод целого числа, допускает только значения в диапазоне между -32768 и +32767. Аналогичный диапазон значений целых чисел допускался в языке AutoLISP предыдущих версий.
К целым числам относятся слова, состоящие только из цифр. Кроме того, целые числа могут иметь знак "+" или "-". Если пользователь пытается задать целое число, не лежащее между описанными выше предельными значениями, то вводимое число преобразуется в вещественное.
Вещественные числа — это слова, которые состоят из цифр и содержат внутри только одну десятичную точку. Могут иметь знак "+" или "-". В состав вещественного числа может входить латинский символ "е" или "Е" с показателем степени числа 10 (научная форма представления вещественных чисел).
Строковые константы начинаются и заканчиваются кавычками (сами кавычки в длину константы не входят) и содержат внутри себя текст, который может состоять из любых знаков, в т. ч. пробелов, скобок, апострофов. Особо рассматривается знак "\", который является служебным и используется для ввода в текстовую строку некоторых специальных знаков (например, кавычек и самого знака "\"). Константа "" считается строкой нулевой длины. В строковые константы могут входить управляющие знаки печати, например: "\n" — переход на новую строку, "\t" — символ табуляции.
Списковые константы — это списки, состоящие из постоянных элементов любого типа (списки, как и функции, открываются и закрываются круглыми скобками). Для того чтобы система AutoCAD не вычисляла выражение как функцию, постоянные списки предваряются апострофом перед открывающей скобкой.
Примеры констант:
57
-100092
12.98207
11.1e+8
"string(2) for printing"
"52\\_l" (первая косая черта является служебной, вторая — значащим символом; если распечатать такую строку, то получим 52\_1)
'(1 5 100 -2)
'("Аа" "Бб" "Вв" "Гг" "Дд" "Ее")
'("Первый" 15 "online" 90.22 "")
Функция setq возвращает значение, совпадающее со значением выражения, присваиваемого последней неременной (<переменнаям>), поэтому тип результата также совпадает с типом этого выражения.
Есть функции, которые преобразуют значения из одного типа в другой (например, функции atoi, fix, float, rtos и др.).
К зарезервированным символам, значения которых пользователям не рекомендуется изменять, помимо имен функций, относятся следующие:
Рассмотренные четыре символа называются еще предопределенными символами.
Замечание
В функциях, требующих в качестве аргумента логическое значение, вместо т можно вводить любое значение, отличное от nil (например, число, строку и т. д.).
Для хранения данных пользователь может вводить свои символы (переменные), не совпадающие по написанию с зарезервированными или ранее занятыми. Появление новых переменных осуществляется с помощью функции setq, например:
(setq r1 15.33 s24 9)
Здесь вводятся переменные r1 и s24, получающие, соответственно, значения 15.33 (вещественное) и 9 (целое). При этом к той части оперативной памяти, которая отведена для текущего рисунка, добавляются участки, занимаемые переменными. Если переменной присвоить значение nil, то такая переменная из памяти удаляется и ее место освобождается для других операций AutoLISP.
Замечание
В многодокументном режиме (когда в одном сеансе системы AutoCAD открыто несколько рисунков) переменные одного рисунка обычно недоступны в другом. Для передачи требуется экспортировать переменные во внедокументное пространство символов.
Переменные могут использоваться в любых выражениях, например:
(setq уу2 (+ 20.56 уу0))
Ввод с клавиатуры выражений AutoLISP не всегда удобен, поэтому можно записать предварительно свои строки на языке AutoLISP в файл с расширением Isp (текст располагается в файле произвольным образом построчно, не обязательно каждое новое выражение начинать с новой строки; желательно заканчивать строку на разделителе — пробеле или скобке), а затем загрузить этот файл с помощью функции load. Синтаксис функции load следующий:
(load <файл> [<сообщение>])
Здесь <файл> — это строка с именем загружаемого файла, а <сообщение> — это строка с текстом сообщения, которое нужно вывести, если загружаемый файл не будет обнаружен. Возвращаемое значение в случае удачной загрузки — результат последнего выражения или имя последней функции, если в файле находятся определения функций (см. функцию defun). В случае если загружаемый файл не найден, возвращается текст сообщения об ошибке.
Например:
(load "longwid.lap" "Файл не найден")
Аргумент <сообщение> может быть опущен, в этом случае при ошибке загрузки выдается стандартное сообщение AutoCAD. Аргумент <файл> обычно содержит только имя файла, который ищется в соответствии с настройкой рабочего каталога и путей поиска файлов поддержки (см. гл. 11). Однако можно указывать и полное имя с путем, — например, "f:/Dis/Progs/longwid.lsp" или "f:\\Dis\\Progs\\longwid.lsp". В качестве разделителя каталогов может использоваться косая черта или обратная косая черта, но обратная черта должна обязательно удваиваться.
Если расширение в имени файла опущено, то система AutoCAD пытается выполнить загрузку, поочередно добавляя следующие расширения:
Расширение vix соответствует файлам приложении, fas — компилированным LISP-файлам, Isp — исходным LISP-файлам. Для непосредственного редактирования пользователю доступны только файлы с расширением Isp, т. к. файлы с расширениями vix и fas не являются текстовыми.
12.3. Выполнение команд AutoCAD из AutoLISP
Очень важной является функция command, которая имитирует ввод пользователя в командной строке AutoCAD с клавиатуры.
Синтаксис функции:
(command [<параметр1> [<параметр2> ... [<параметрN>] ... ] ] )
Возвращаемое значение — nil.
Функция может вызываться без параметров — это равносильно нажатию клавиши <Esc>, что прерывает действие активной команды. Если параметры используются, то их количество может быть любым. Каждый из параметров обычно является текстовой строкой, имитирующей набор символов строки на клавиатуре с последующим нажатием клавиши <Enter>. Возвращаемое значение — nil.
Предположим, что нужно нарисовать отрезками рамку формата размером 210 на 297 (левый нижний угол рамки взять в точке 0,0 и верхний правый — в точке 210,297). Вспомним, что команда рисования отрезков в системе AutoCAD называется ОТРЕЗОК (LINE). Если вы не уверены, что ваша программа будет работать на машине с русской версией AutoCAD, то используйте английские имена команд с подчеркиванием (в данном случае — _LINE). С такими именами команды будут выполняться в любой версии системы. Если бы вы выполняли задачу без программы, то вводили бы с клавиатуры следующие команды и опции:
_LINE
0,0
210,0
210,297
0,297
_С
По этим указаниям AutoCAD вызовет команду _LINE, возьмет в качестве первой точки 0,0, в качестве второй — 210,0, в качестве третьей — 210,297, в качестве четвертой — 0,297, а _С (соответствует опции Замкнуть (Close)) замкнет отрезки на первую точку (опции тоже желательно вводить английские и с подчеркиванием). Программа, имитирующая описанный алгоритм, занимает одну строку и выглядит так:
Листинг 12.1. Построение прямоугольника (вариант 1)
(command "_LINE" "0,0" "210,0" "210,297" "0,297" "_С")
Можно указанное построение выполнить в несколько строк (в несколько обращений к функции command).
Листинг 12.2. Построение прямоугольника (вариант 2)
(command "_LINE" "0,0" "210,0")
(command "210,297" "0,297")
(command "_С")
В листинге 12.2 использовано трехкратное обращение к функции command, в каждом из которых количество параметров должно быть не меньше одного.
Можно координаты точек задавать не как строку, а как списки из двух чисел целого или вещественного типа.
Листинг 12.3. Построение прямоугольника (вариант 3)
(command "_LINE" '(00) ' (210 0) ' (210 297) ' (0 297) "_С")
Апостроф опускать нельзя, т. к. выражение (210 0) без апострофа будет рассматриваться как обращение к функции 210 с аргументом 0.
Там, где требуется ввод чисел, можно в качестве аргументов подавать функции command именно числа, а не строки с числами, например:
(command "_COLOR" "1") (command "_COLOR" 1)
Оба выражения выполняют одну и ту же операцию (в качестве текущего устанавливают красный цвет, который имеет номер 1), но в первом случае номер цвета задается как строка, а во втором — как целое число.
Можно в одной функции command обратиться сразу к нескольким командам системы AutoCAD, например:
(command "_COLOR" 1 "_ZOOM" "_E")
Эта программная строка выполнит команду ЦВЕТ (COLOR) для установки красного цвета и команду ПОКАЗАТЬ (ZOOM) с опцией ГРАНИЦЫ (EXTENTS).
Для имитации варианта, когда пользователь вместо ввода опции нажимает на клавишу <Enter>, в качестве параметра функции command выступает текстовая строка нулевой длины (""). Например, если бы вы в примере, приведенном в листинге 12.1, захотели завершить команду _LINE не опцией _с (замкнуть прямоугольник), а нажатием на клавишу <Enter> (не замыкать прямоугольник), то соответствующая программная строка выглядела бы так:
(command "_LINE" "0,0" "210,0" "210,297" "0,297" "")
Мы уже рассмотрели некоторые функции-AutoLISP, встроенные в систему AutoCAD (setq, load, command). В этом разделе освещаются остальные функции.
Встроенные функции по своему назначению можно классифицировать следующим образом:
Вопросы создания функций пользователя рассмотрены в разд. 12.5, а особенности работы с объектами рисунка AutoCAD — в разд. 12.6.
Полный перечень функций AutoLISP (в алфавитном порядке) приведен в приложении 8.
12.4.1. Арифметические функции
К арифметическим относятся следующие функции (вместе с описанием функций приводится их синтаксис):
Все арифметические функции допускают работу с аргументами целого или вещественного типа. Если все аргументы целые, то возвращается целое значение. Если хотя бы один аргумент вещественный, то результатом будет вещественное число. Некоторые арифметические функции допускают вызов без аргументов — в этом случае возвращается 0 (целое число).
Примеры:
(+1 3) — возвращает 4;
(- -2 1.1 0.3) — возвращает - 3.4;
(* -2 1.1 0.3) — возвращает - 0.66;
(/ -2 1.1 0.3) — возвращает - 6.06061;
(/ 5 12) — возвращает 0 (оба аргумента целые!);
(1 + 1.1) — возвращает 2.1;
(1 - 1.1) — возвращает 0.1;
(~ 3) — возвращает - 4;
(abs -1.1) — возвращает 1.1.
Рассматриваемые в данном разделе функции выполняют различные операции проверок, логические операции, организуют повторения и циклы.
Следующие функции выполняют операции сравнения:
При сравнении строк на возрастание (убывание) используются коды бук-венно-цифровых знаков строк, начиная с первого, по действующей таблице кодировки символов. Возвращается логическое значение (T или nil), nil эквивалентно значению "ложь".
Примеры:
(= -10.0 -10 -10.000) — возвращает T;
(= "STR56" "str56" "Str56") — возвращает nil;
(= "abcde" "abcde") — возвращает Т;
(/= -1.0 -1) — возвращает nil;
(/= "STR56" "Str56" "Str56") — возвращает T;
(/= "abcde" "abcde") — возвращает nil;
(< "a" "b" "k") — возвращает T;
(> 2 2 30) — возвращает nil;
(<= "q" "qa") — возвращает T;
(>= "qa" "q") — возвращает T.
Следующие функции выполняют логические операции:
Примеры:
(and т т nil) — возвращает nil;
(and "a" "b" "k") — возвращает T;
(or т nil т) — возвращает T:
(or "а" "b" "k") — возвращает T;
(not nil) — возвращает T;
(not 15.88) — возвращает nil;
(null ' ()) — возвращает T;
(null "Tram") — возвращает nil.
Условная операция типа if-then-else (если-то-иначе) осуществляется с помощью функции if:
(if <условие> <выражение1> (<выражение2>] )
Типы аргументов: любые.
Возвращаемое значение: вычисленное значение <выражение1>, если значение <условие> отлично от nil (nil в логических операциях используется как "ложь"); или значение <выражение2>, если значение <условие> равно nil (когда <выражение2> опущено, тогда вместо <выражение2> возвращается nil).
Примеры:
(if (> a b) 1 10) — возвращает 1, если a>b, и 10 — в других случаях;
(if (= h1 h2) (+ h1 15) (* h1 2.5)) — возвращает результат вычисле-ния выражения (+ h1 15), если значения h1 и h2 равны; иначе— результат вычисления выражения (* h1 2.5);
(if (< a b) "a<b") — возвращает строку "а<b" или nil (т.к. <выражение2> опущено);
(if nil "Point") — возвращает nil (т. к. <условие> всегда ложно).
Если функция if используется для выполнения по условию не одного выражения, а нескольких, то эти выражения следует объединить с помощью функции рrоgп (иначе будет синтаксическая ошибка, вызванная неправильным количеством аргументов при обращении к функции if):
(рrоgn <выражение1> <выражение2> ... [<выражениеN>])
Функция объединяет несколько (не менее двух!) выражении в одно, когда по синтаксису языка AutoLISP может использоваться только одно (как в функции if).
Типы аргументов: любые.
Рассмотрим использование функции progn на таком примере. Пусть при выполнении условия (> а b) нужно выполнить выражения (setq с -1), (setq d (-cab)) и (* d b), а при невыполнении условия — выполнить выражения (setq с 2), (setq d (* а Ь) ) И (* d с).
Листинг 12.4. Пример совместного использования функций if и progn
(if (> а b)
(progn
(setq с -1)
(setq d (-cab))
(* d b)
); конец progn
(progn
(setq с 2)
(setq d (* а b))
(* d с)
) ; конец progn
); конец if
Для удобства чтения в этом фрагменте текста программы в начале некоторых строк добавлены пробелы, чтобы открывающая скобка оказалась на одном уровне с закрывающей, а внутренние выражения были смещены относительно ближайших внешних на две позиции вправо. К закрывающим скобкам даны комментарии, согласно правилам их написания (см. разд. 12.1).
Условная операция типа case с любым количеством условий осуществляется с помощью функции cond:
(cond (<условие1> [<выражение1> ... ] ) [ (<условие2> [<выражение2> ... ])] ...)
Аргументы: любое количество списков, в каждом из которых на первом месте стоит проверяемое условие, а затем следуют выражения, которые должны быть выполнены (вычислены), если это условие оказалось истинным.
Возвращаемое значение функции cond — последнее вычисленное выражение. Если вычисляемое выражение отсутствует (не задано в списке), то возвращается результат вычисления последнего аргумента <условие>. Если аргументы функции cond не заданы, то возвращается nil.
Примеры:
(cond) — возвращает nil;
(cond (14)) — возвращает 14.
Впрочем, использование функции cond с неполным количеством аргументов не имеет особого смысла.
Рассмотрим задачу: переменной mycolor нужно присвоить текстовое значение с наименованием одного из основных цветов AutoCAD ("красный", "желтый", "зеленый", "голубой", "синий") в зависимости от текущего значения переменной сс, которая принимает значения 1, 2, 3, 4, 5. В случае если значение переменной ее оказалось отличным от перечисленных, то переменной mycolor нужно присвоить "" (пустая строка).
Листинг 12.5. Пример 1 использования функции cond
(setq mycolor (cond
((= cc 1) "красный")
((= cc 2) "желтый")
((= cc 3) "зеленый")
((= cc 4) "голубой")
((= cc 5) "синий")
(Т "")
); конец cond
); конец setq
Функция setq присваивает переменной mycolor результат вычисления функции cond. Значение, возвращаемое функцией cond, вычисляется по следующей схеме. Сначала рассматривается список, заданный в качестве первого аргумента— ((= cc 1) "красный") — и проверяется условие, являющееся первым элементом этого списка. Если оказалось, что значение переменной cc равно 1 и, значит, первое условие возвращает "истину", то дальнейшее рассмотрение аргументов функции cond не выполняется, а в качестве возвращаемого значения принимается "красный". Если первое условие не вычислилось как "истина", то проверяется второе условие и т. д. В качестве последнего, шестого, условия стоит т (т. е. "истина" при любых значениях), поэтому, если значение переменной cc не совпало с 1, 2, 3, 4, 5, то в качестве возвращаемого значения функции cond будет принято "" (пустая строка).
Еще один пример иллюстрирует случай, когда по истинности значения <условие> выполняются несколько операторов.
Листинг 12.6. Пример 2 использования функции cond
(cond
((= cc 1) (setq abc "красный")
(command "_PLINE" "-50,120.45" "97.66,23.124" "45.7,800" "_С")
) ; = cc 1
((= cc 2) (setq abc "желтый")
(command "_CIRCLE" "-50,120.45" "100")
) ; = cc 2
) ; конец cond
В этом примере функция cond проверяет значение переменной cc. Если оно равно 1, то функция setq присваивает переменной abc значение "красный", а затем AutoCAD рисует замкнутую полилинию с тремя вершинами. Если значение cc равно 2, то функция setq присваивает переменной abc значение "желтый", a AutoCAD рисует окружность радиуса 100. Если окажется, что значение cc не равно ни 1, ни 2, то функция cond вернет nil, не изменяя при этом значения переменной abc и не строя никаких новых примитивов AutoCAD. Поскольку функция command является последним выражением, как в первом, так и во втором условиях функции cond, то во всех трех случаях будет возвращено значение nil (nil является возвращаемым значением функции command — см. разд. 12.3). Однако в данном примере функция cond применена не ради возвращаемого значения, а ради изменения значения переменной abc и построения дополнительной полилинии или окружности.
Существует несколько проверочных функций, которые проверяют конкретные значения или типы:
Все эти функции возвращают значение т, если аргумент удовлетворяет требуемому условию, и nil — если не удовлетворяет.
Примеры:
(minusp (-52 24 39)) —возвращает т;
(zerop 0.0) — возвращает т;
(numberp -10.0) — возвращает т;
(numberp "1") — возвращает nil;
(listp "1") — возвращает nil;
(listp ' (-21.82)) — возвращает т;
(boundp ' +) — возвращает т.
Функция eq проверяет равенство двух аргументов (аналогична функции =):
(eq <аргумент1> <аргумент2>)
Типы аргументов: любые.
Тип возвращаемого значения: логическое (т, если значения аргументов совпали, и nil, если не совпали).
Примеры:
(eq -10.0 -10) — возвращает т;
(eq о 3) — возвращает nil;
(eq "abc" "АBC") — возвращает nil.
Функция equal проверяет равенство двух объектов (для чисел и списков из числовых величин равенство проверяется в пределах допуска):
(equal <аргумент1> <аргумент2> [<допуск>])
Типы аргументов: любые.
Тип возвращаемого значения: логическое (т, если абсолютная величина разности аргументов не превосходит значение аргумента <допуск>, и nil, если не совпали; если <допуск> не задан, то его значение считается равным нулю). Для аргументов, не являющихся числами или списками из чисел, <допуск> не используется.
Примеры:
(equal -10.0 -10) — возвращает т;
(equal 3.000 3.002 0.002) — возвращает т;
(equal "font" "font") — возвращает т;
(equal ' (1.00 2.13 2.99) ' (1 2.13 3.0) 0.1) — возвращает Т.
Функция while позволяет организовывать операции цикла по многократно проверяемому условию:
(while <условие> <выражение1> [<выражение2> ... [<выражениеN>] ... ] )
Типы аргументов: любые.
Возвращаемое значение функции while: значение <выражением>, когда последнее вычисленное значение аргумента <условие> отлично от nil. При неудачном задании цикл, организуемый с помощью функции while, может оказаться бесконечным.
Рассмотрим следующий пример. Пусть надо вычислить значение n! (факториал), т. е. произведение целых чисел от 1 до n. В данном разделе восклицательный знак используется как знак функции факториала.
Листинг 12.7. Пример использовании функции while (вычисление факториала)
(setq i 1 factorial 1)
(while (< i n)
(setq i (1+ i))
(setq factorial (* factorial i))
); конец while
Рассмотрим работу примера, когда число n равно 11 (т. е. вычисляется 11!).
В программе используются переменные i (это переменная, являющаяся счетчиком цикла) и factorial (переменная, которая накапливает произведение чисел, формирующее факториал). Перед входом в цикл они получают начальные значения — соответственно, 1 и 1 (по определению 1! считается равным 1).
Функция while проверяет условие (< i n) для текущего значения i и, если результат вычисления условия отличен от nil, то выполняет внутренние операции (две операции с участием функции setq). Таким образом, при первом входе в цикл i равно 1, проверяемое условие (< 1 n) возвращает значение т ("истина"), и функция while увеличивает i на l (получается i=2) и умножает переменную factorial на i: factorial=l*2 (не что иное как 2!).
Далее снова передается управление на вход в цикл (уже при i=2). Проверка условия опять дает результат "истина", поэтому i получает значение 3 (2+1), a factorial — 6 (2*3). И так далее, пока i не станет равным n (11) и программа покинет цикл, не выполняя внутренних операций. Результат: при n=11 factorial=39, 916,800 (запятые разделяют триады цифр).
Функция repeat используется для организации цикла с фиксированным количеством повторений:
(repeat <количество> [<выражение1> ...] )
Типы аргументов: <количество> — целое число (имеют смысл только положительные числа), <выражение1> — любое выражение. После аргумента <выражение1> могут идти другие выражения, которые нужно выполнить внутри цикла.
Возвращаемое значение — значение последнего вычисленного выражения. Если аргумент <количество> имеет нулевое или отрицательное значение, или после аргумента <количество> не заданы выражения, то функция repeat возвращает nil.
Переработаем предыдущий пример (см. листинг 12.7) — вместо while воспользуемся функцией repeat.
Листинг 12.8. Пример использования функции repeat (вычисление факториала) I
(setq i 1 factorial 1)
(repeat (1- n)
(setq i (1+ i))
(setq factorial (* factorial i))
); конец repeat
Поскольку входные значения i=1 и factorial = l! =1, то остается умножить factorial на 2, 3, ... , n. Количество таких умножении равно n—1, что на языке AutoLISP записывается как (i- n). Остальное работает как в предыдущем примере.
К основным вычислительным функциям относятся следующие функции:
Примеры:
(fix -10.083) — возвращает -10;
(fix 40.45) — возвращает 40;
(rem 14 2) — возвращает о (результат целый, т. к. оба аргумента целые);
(rem 12.9 3.5) — возвращает 2.4;
(max 7 3.4 2.0092) — возвращает 7.о (результат вещественный, поскольку есть вещественные аргументы);
(min 7 3.4 2.0092) — возвращает 2.0092;
(gcd 42 12) — возвращает б;
(angle ' (0 0) ' (50.0 50.0)) — возвращает 0.785398;
(distance ' (0 0) ' (250.0 0.0)) — возвращает 250.0;
(sqrt 25) — возвращает 5.0;
(sin 0.0) — возвращает 0.0;
(cos 0.0) — возвращает 1.0;
(atan 1) — возвращает 0. 785398;
(atan -1 0) — возвращает -1.5708;
(ехр 1) — возвращает 2.71828;
(expt 2 2) — возвращает 4 (оба аргумента целые);
(expt -2 2.0) — возвращает 4.0;
(log 1) — возвращает 0.0.
К вычислительным можно отнести также функции побитовых операций:
Все функции побитовых операций работают с целыми аргументами и возвращают целые значения.
Функция logand (побитовое "и") работает следующим образом. Каждое целое число может быть представлено в двоичном виде (иначе говоря, в виде суммы степеней числа 2, в которой перед степенью 2 стоит множитель 0 или 1; показатель степени в данной сумме может быть от 0 до 30). Например:
5 = 1*2^0 + 0*2^1 + 1*2^2 = 1012 , 3 = 1*2^0 + 1*2^1 + 0*2^2 = 112
31-й (старший) разряд в таком представлении отводится под знак (минусу соответствует 1, плюсу соответствует 0). После перевода всех чисел в двоичное представление, выполняется поразрядная (побитовая) операция "и" по следующему правилу:
(logand 0 0)= (logand 1 0)= (logand 0 1)= 0; (logand 1 1) = 1.
Если аргументы не заданы, то возвращается 0. Если задан только один аргумент, то возвращается <число1>.
Пример:
(logand 3 5) — возвращает 1.
Функция logior (побитовое "или") работает по следующему правилу:
(logior 1 0)= (logior 0 1)= (logior 1 1)= 1; (logior 0 0) = 0.
Если аргументы не заданы, то возвращается 0. Если задан только один аргумент, то возвращается <число1>.
Примеры:
(logior 3 5) — возвращает 7;
(logior 2) — возвращает 2.
Побитовый сдвиг (функция lsh) выполняется для двоичного представления чисел (по поводу двоичного представления см. функцию logand). Побитовый сдвиг выполняется влево, если аргумент <сдвиг> положителен, и вправо, если — отрицателен. Целые числа могут иметь не более чем 32 бита (нумерация битов от 0 для младшего разряда до 31 — этот разряд является знаковым), поэтому если старшие разряды при сдвиге влево выходят за 31-й разряд, то они теряются. При сдвиге вправо младшие разряды теряются.
Если аргументы не заданы, то возвращается о. Если задан только один аргумент или аргумент <сдвиг> равен о, то возвращается <число>.
Примеры:
(lsh 2 l) — возвращает 4;
(lsh 3 30) — возвращает -1073741824.
В функции Boole аргумент <оператор> должен иметь значение от 0 до 15, что соответствует 16-ти вариантам булевых функции. Остальные аргументы — произвольные целые числа.
Номер любой булевой функции (они все двуместные, т. е. имеют два аргумента, каждый из которых может быть 0 или 1) кодируется следующим образом: если функция для пары (1 1) возвращает 1, то к ее номеру добавляется 1=2^0; если функция для (1 0) возвращает 1, то добавляется 2=2^1; если функция для (0 1) возвращает 1, то добавляется 4=2^2; если функция для (0 0) возвращает 1, то добавляется 8=2^3. При таком кодировании рассмотренные выше функции logand и logior получат номера 1 и 7.
Булева функция применяется побитно к аргументам <число1> и <число2>, затем к результату и аргументу <число3> и т. д.
Если заданы только два аргумента (<оператор> и <число1>), то возвращается <число1>.
Примеры:
(Boole 15 -3 45) — возвращает - l;
(Boole 8 16 17) — возвращает - 18.
12.4.4. Функции преобразований
Функция type позволяет определять тип символа AutoLISP:
(type <символ>)
Тип аргумента: <символ> (имя функции или переменной).
Если аргумент <символ> не является именем функции или переменной с присвоенным значением, то возвращается nil. Другие варианты возвращаемого значения:
Некоторые из перечисленных вариантов возвращаемого значения доступны только при использовании других средств разработки приложений. Типы данных, доступные в Visual LISP, приведены в разд. 12.2.
Примеры:
(type 15) — возвращает INT;
(type l+) — возвращает SUBR;
(type "15") — возвращает STR;
(type ' (l5 16 17)) — возвращает LIST.
К функциям преобразовании относятся следующие:
Примеры:
(float 15) — возвращает 15.0;
(float -l5) — возвращает -15.0;
(itoa -29031) — возвращает "-29031";
(itoa 0) — возвращает "0";
(atoi "-29031") — возвращает -29031;
(atof "-29.03l") — возвращает -29. 031.
Функция rtos, преобразующая вещественное число в строку, имеет следующие аргументы:
Аргументы <режим> и <точность> имеют тот же смысл и принимают те же значения, что и системные переменные LUNITS и LUPREC. Если аргументы <режим> и <точность> опущены, то в качестве их значений принимаются текущие значения LUNITS и LUPREC. На форму возвращаемого значения может оказывать влияние системная переменная UNITMODE.
Допустимые значения аргумента <режим>:
Возвращается строка, являющаяся результатом преобразования аргумента <число> в символьное представление с заданной точностью.
Примеры:
(rtos 10.9453 1 3) — возвращает "1.095Е+01";
(rtos 10.9453 2 3) — возвращает "10.945".
Функция angtof преобразует строку, представляющую значение угла в различных форматах, в вещественное число, являющееся величиной угла в радианах. Функция angtof по своему действию является обратной к функции angtos.
Аргументы функции angtof: <строка> — строка, <представление> — целое число. Значения аргумента <представление> соответствуют значениям системной переменной AUNITS. Если аргумент <представление> опущен, то в качестве его значения принимается текущее значение переменной AUNITS.
Допустимые значения аргумента <представление>:
Возвращается вещественное значение угла в радианах, для которого аргумент <строка> является строковым представлением (возможно, в другом формате представления углов). Если аргумент <строка> задан в неправильном формате, то возвращается nil.
Примеры:
(angtof "30" 0) — возвращает 0.523599 (перевод угла из градусов в радианы);
(angtof "334.365g" 2) — возвращает 5.25219.
Функция angtos преобразует значения угла в радианах в строку, в соответствии с форматом (функция angtos является обратной к функции angtof).
Аргументы функции angtos:
Значения аргумента <представление> соответствуют значениям системной переменной AUNITS, значения аргумента <точность> — значениям системной переменной AUPREC. Если аргументы <представление> и <точность> опущены, то в качестве их значений принимаются текущие значения переменных AUNITS и AUPREC.
Возвращается строка, являющаяся строковым представлением значения угла. На форму результата оказывает влияние значение системной переменной UNITMODE. Необходимо также учитывать значение переменной ANGBASE.
Примеры:
(angtos l.00 0) — возвращает "57.296";
(angtos 1 0) — возвращает "57.296";
(angtos 3.14000 0) — возвращает "179.909".
Функция distof выполняет преобразование строки, представляющей вещественное значение в одном из форматов линейных единиц, в обычное вещественное число. Функция distof является обратной по отношению к функции rtos.
Аргументы: <строка> — строка, представляющая число в одном из форматов; <режим> — целое число, значения которого соответствуют значениям системной переменной LUNITS (см. описание функции rtos). Если аргумент <режим> опущен, то в качестве его значения принимается текущее значение системной переменной LUNITS.
Возвращаемое значение: вещественное число. Если аргумент <строка> имеет неправильную структуру, то возвращается nil.
Примеры:
(distof "l" 2) — возвращает 1.0;
(distof "1.0945E+01" l) — возвращает 10.945.
Функция trans преобразует координаты точки из одной системы координат (СК) в другую. Аргументы функции:
Аргументы <СК-из> и <СК-в> могут принимать следующие значения:
Цифровой код системы координат может принимать такие значения:
Имеются определенные условия использования этих кодов в аргументах <СК-из> и <СК-в> (иначе говоря, не всякое значение аргумента <ск-из> сочетается с любым значением аргумента <СК-в>, и наоборот).
Коды 0 и l могут использоваться в аргументах <СК-из> и <СК-в> в любых сочетаниях. Это означает, что если <СК-из> = 0, то аргумент <СК-в> может быть или любым цифровым кодом (от 0 до 3), или именем примитива, или вектором выдавливания; аналогично: если <СК-в> = о, то аргумент <СК-из> может быть или любым цифровым кодом (от 0 до 3), или именем примитива, или вектором выдавливания.
Цифровой код 2 сочетается с разными вариантами, но его интерпретация зависит от цифрового кода другой СК. Если вторая СК имеет код 0 или l, то первая СК (с кодом 2) является ЭСК текущего видового экрана. Если вторая СК имеет код з, то первая СК (с кодом 2) является ЭСК текущего видового экрана в пространстве модели.
Цифровой код 3 (ЭСКЛ) используется только в паре с цифровым кодом 2 другой СК (разобран в предыдущем абзаце).
Имя примитива в качестве аргумента <СК-из> используется в том случае. когда координаты точки, хранящейся вместе с примитивом (например, начальная или конечная точки отрезка, центр окружности, вершина полилинии и т. п.), нужно пересчитать из системы координат объекта (СКО) в другую СК. В некоторых операциях (например, при работе с функцией entmake) возникает необходимость пересчитать точку в СКО — здесь имя примитива будет фигурировать в качестве аргумента <СК-в>.
Замечание
Некоторые объекты (например, трехмерные полилинии) не имеют особой СКО и хранят свои данные в МСК.
Свои преимущества имеет и способ, когда трехмерный вектор выдавливания применяется в качестве аргументов <СК-в> или <СК-из> (например, при проектировании объекта на плоскость с известным вектором нормали). Этот способ не может использоваться для тех объектов, у которых в качестве СКО фигурирует МСК.
Возвращаемым значением функции trans является список из трех координат точки (или вектора перемещения) в новой СК. Если значения аргументов <СК-из> и <СК-в> заданы противоречиво, то возвращается nil (например, если функция trans вызывается с цифровым кодом СК, равным 3, из пространства модели, а не из пространства листа).
Как было сказано выше, в качестве аргумента <точка> может фигурировать двумерная точка. В этом случае AutoCAD преобразует точку из двумерной в трехмерную, добавляя координату Z по следующему алгоритму.
Если задан аргумент <признак> и его значение отлично от nil (т. е. аргумент <точка> интерпретируется не как точка, а как вектор перемещения), то добавляемая координата Z получает значение 0. Если признак не задан или равен nil, то значение координаты Z зависит от аргумента <СК-из> и выбирается по табл. 12.1.
Таблица 12.1. Выбор функцией trans координаты Z для двумерной точки
СК-из |
Значение Z |
МСК |
0.0 |
ТСК |
Текущий уровень (значение системной переменной ELEVATION) |
СКО |
0.0 |
ЭСК |
В проекции на текущую плоскость построений (т. е. плоскость ТСК + текущий уровень) |
ЭСКЛ |
В проекции на текущую плоскость построений (т. е. плоскость ТСК + текущий уровень) |
Рассмотрим пример. Предположим, в качестве ТСК выбрана система координат, у которой оси параллельны осям МСК, а начало находится в точке X=10, Y=10, Z=10. Тогда:
(trans '(1 1 1) 0 1) — возвращает (-9.0 -9.0 -9.0);
(trans '(1 1 1) 0 1 T) — возвращает (1.0 1.0 1.0).
12.4.5. Функции, работающие со строками
К функциям, работающим со строками, относятся следующие:
Примеры:
(chr 192) — возвращает "А" (русская буква);
(chr 48) — возвращает "0";
(ascii "A") — возвращает 192 (русская буква);
(ascii "A") — возвращает 65 (латинская буква);
(ascii "ABCDEFGH") — возвращает 65 (все буквенно-цифровые знаки, кроме первого, игнорируются);
(read "B C D E F") — возвращает B;
(read "23. 55") — возвращает 23.55;
(read "10 масса") — возвращает 10;
(strcase "AbcdE FGh-38") — возвращает "ABCDE FGH-38";
(strcase "AbcdE FGh-38" Т) — возвращает "abcde fgh-38";
(strcat "Happy N" "ew " "Year") — возвращает "Happy New Year";
(strcat (itoa 12) "-я линия") — возвращает "12-я линия";
(strlen "Happy N" "ew " "Year") — возвращает 14;
(strien "Петербург") — возвращает 9;
(substr "Happy New Year" 7 3) — возвращает "New";
(substr "Happy New Year" 7) — возвращает "New Year".
Функция wcmatch проверяет строку на соответствие образцу (шаблону):
(wcmatch <строка> <образец>)
Аргументы: <строка> — строка, структуру которой нужно проверить; <образец> — особая строка, определяющая требования к проверяемой структуре. Аргумент <образец> может содержать как обычные алфавитно-цифровые знаки, так и специальные групповые знаки (wild-card-characters), рассматриваемые в табл. 12.2. Можно в строке, определенной аргументом <образец>, задать сразу несколько шаблонов, на соответствие каждому из которых аргумент <строка> будет проверен; шаблоны должны разделяться с помощью запятой.
Таблица 12.2. Специальные знаки, которые можно использовать в образце
Знак |
Назначение |
# |
Заменяет одну любую цифру |
@ |
Заменяет одну букву |
. (точка) |
Заменяет любой символ, не являющийся ни буквой, ни цифрой |
* |
Заменяет любую последовательность любых символов, даже пустую; может использоваться в любом месте аргумента <образец> |
? |
Заменяет ровно один любой символ |
~ |
Если стоит в начале образца, заданного аргументом <образец>, то означает "все, кроме определенного аргументом <образец>" |
[...] |
Заменяет любой из символов, перечисленных в скобках |
[~. . .] |
Заменяет любой символ, кроме перечисленных в скобках |
- |
Между двумя однотипными знаками указывает на диапазон |
, (запятая) |
Разделяет два шаблона |
' (обратный апостроф) |
Превращает специальный знак в обычный |
В квадратных скобках любой знак рассматривается не как специальный, поэтому его не надо предварять обратным апострофом. Символ "\" является служебным, поэтому его использование отвечает правилам, изложенным в разд. 12.2.
Возвращаемое значение: т — если аргумент <строка> соответствует аргументу <образец>, nil — если не соответствует.
Примеры:
(wcmatch "Happy New Year" "H*") — возвращает T, т. к. проверяемая строка начинается с буквы "Н";
(wcmatch "Happy New Year" "h*") — возвращает nil (при проверке внутри строк символы "Н" и "h" считаются разными);
(wcmatch "Happy New Year" "??ppy*, *New*, * * *") — возвращает Т, т. к. все три шаблона соблюдены;
(wcmatch "Happy New Year" "*#*") — возвращает nil, т. к. нет ни одной цифры;
(wcmatch "Happy New Year" "H[adgt]*") — возвращает Т;
(wcmatch "Happy***day" "Happy'*'*'**") — возвращает Т;
(wcmatch "Year 2000" "@ea@ [1-6]000") — возвращает Т.
12.4.6. Функции, работающие со списками и точечными парами
Следующая группа функций — это функции, работающие со списками и точечными парами. Точечная пара — это двухэлементный список вида (а . b), в котором в качестве особого разделителя выступает точка (точка в такой записи отделяется пробелом как от первого, так и от второго элементов). Точечные пары используются для хранения в файлах системы AutoCAD данных о графических примитивах (см. разд. 12.6). Первый элемент точечной пары обычно является целым числом и называется DXF-кодом, а второй — данными этого кода. Основной способ получения точечных пар — функция cons.
Есть отличия в работе некоторых функций со списками и с точечными парами.
Функция list — это основная функция, позволяющая создать список:
(list (<элемент1> [<элемент2> ... [<элементN>] ... ] ] )
В качестве аргументов <элементы>, из которых образуется список, могут выступать любые объекты, которыми оперирует AutoLISP. Самый распространенный список — это список из двух или трех вещественных чисел, представляющий точку.
В качестве элементов списка могут выступать другие списки или точечные пары.
Примеры:
(list 12.12 0.34 5.88) — возвращает (12.12 0.34 5.88);
(list 1 "ad2" (list 0 1 2)) — возвращает (1 "ad2" (0 1 2)) (третьим элементом нового списка является список из чисел 0, 1 и 2).
Следующие функции выполняют основные операции со списками:
Примеры:
(append '(14 16) '(12.12 0.34 5.88)) — возвращает (14 16 12.12 0.34 5.88);
(nth о '(12.12 0.34 5.88)) — возвращает 12.12;
(reverse '(12.12 0.34 5.88)) — возвращает (5.88 0.34 12.12);
(car '(12.12 0.34 5. 88)) — возвращает 12.12;
(car '(62 . 1)) — возвращает 62;
(cdr '(12.12 0.34 5.88)) — возвращает (0.34 5.88);
(cdr ' (62 . 1)) — возвращает 1.
От функции car и cdr путем их повторения в разных комбинациях от двух до четырех раз в языке AutoLISP образованы следующие функции (их двадцать восемь):
Аналогичным методом раскрываются оставшиеся функции (caadar, caaddr, cadaar, cadadr, caddar, cadddr, cdaaar, cdaadr, cdadar, cdaddr, cddaar, cdddar, cddadr, cddddr).
Функция cons добавляет к списку первый элемент или создает точечную пару:
(cons <аргумент1> <аргумент2>)
Если <аргумент2> является списком, то функция cons добавляет в него <аргумент1> в качестве нового первого элемента. Если <аргумент2> является атомом (т. е. не списком), то функция cons создает точечную пару типа (<аргумент1> . <аргумент2>).
Примеры:
(cons 67.4 '(10.3 12.9 -3.9)) — возвращает (67.4 10.3 12.9 -3.9);
(cons "Happy" '("New" "Year")) — возвращает ("Happy" "New" "Year");
(cons 2 4) — возвращает (2 . 4);
(cons 8 "0") — возвращает (8 . "0");
(cons 62 l) — возвращает (62 . l).
Функция member проверяет принадлежность элемента списку:
(member <элемент> <список>)
Если функция member обнаруживает <элемент> в аргументе <список>, то возвращает остаток списка, начиная с этого места; если <элемент> в аргументе <список> не обнаруживается, то функция возвращает nil.
Примеры:
(member 1 '(4 50 1 2)) — возвращает (1 2);
(member 4 '(4 50 1 2)) — возвращает (4 50 1 2);
(member 0 '(4 50 1 2)) — возвращает nil.
Функция assoc применяется к списку, в котором элементами являются списки или точечные пары, и выбирает из этих элементов тот, у которого первый элемент имеет заданное значение:
(assoc <код> <список>)
Если в аргументе <список> имеется несколько элементов, удовлетворяющих требуемому условию, то в качестве возвращаемого значения выбирается первый из них. Функция assoc — основной инструмент в операциях выборки из списка с характеристиками примитива AutoCAD того элемента, который содержит точечную пару с нужным DXF-кодом свойства (цвета, типа линии, веса и т. д.).
Примеры:
(assoc 8 '((8 . "Walls") (62 .4) (6 . "ACAD_IS005W100") ) ) — возвращает (8 . "walls");
(assoc 62 '((8 . "Walls") (62 .4) (6 . "ACAD IS005W100") ) ) — возвращает (62 . 4).
Следующие три функции (apply, mapcar, foreach) позволяют выполнять операции сразу над всеми элементами списка.
(apply '<функция> <список>) — применение функции, заданной аргументом <функция> поочередно ко всем элементам списка, заданного аргументом <список>.
Примеры:
(apply '* (list 2 3 5)) — равносильно (* 2 3 5), поэтому возвращает 30;
(apply 'max (list 2 3 5)) — возвращает 5.
Последний пример показывает, что функция apply удобна, когда какую-то операцию нужно выполнить над аргументами, количество которых заранее неизвестно. Поэтому следует сформировать список, к которому затем применить функцию apply с именем нужной функции в качестве первого аргумента. Читателю следует обратить внимание на употребление перед аргументом апострофа.
(mapcar ' <функция> <список1> [<список2> ... [<списокN>] ... ]) — применение функции, заданной аргументом <функция> сначала к первым элементам каждого из списков (<список1>, , <списокN>), затем — ко вторым элементам и т. д.; результаты объединяются в новый список, который является возвращаемым значением.
Пример:
(mapcar '* '(2 3) '(l 5)) возвращает (2 15), т. к. первый элемент результирующего списка вычисляется как (* 2 l), а второй — как (* 3 5).
Функция foreach позволяет организовать циклическое выполнение выражений (их может быть несколько), поочередно подставляя вместо переменной цикла элементы списка:
(foreach <имя> <список> [<выражение1> [<выражение2>... [ <выражениеN> ] ... ] ] )
Функция возвращает значение последнего вычисленного выражения. Если аргументы <выражение1> ... <выражениеN> не заданы, то функция foreach возвращает nil.
Пример:
(setq s1 0 s2 l)
(foreach p '(1 2 3 4 5) (setq s1 (+ s1 p) ) (setq s2 (* s2 p) ) ) — возвращает 120 (результат вычисления s2 — произведения чисел списка); кроме того, в переменной s1 сформируется сумма чисел списка.
Функция eval позволяет сформировать список и затем вычислить его (evaluate) как выражение. В таком списке на первом месте должно стоять имя функции.
Пример:
(setq mylist (list 'max 1.7 9.34 6.7));
(eval mylist) — возвращает 9.34 (вычисляется выражение (max l.7 9.34 6.7)).
Функция quote является обратной по отношению к функции eval и позволяет обрабатывать выражение как список, т. е. не вычисляя его. Сокращенным вариантом имени функции quote является одинарная кавычка.
Примеры:
(quote б 7 8 2 l) — возвращает (67821);
'(16 23 90) — возвращает (16 23 90).
Функция acad_strlsort позволяет отсортировать по возрастанию список, элементами которого являются строки.
Пример:
(acad_strlsort '("Пн" "Вт" "Ср" "Чт" "Пт" "Сб" "Bс"))— возвращает ("Вс" "Вт" "Пн" "Пт" "Сб" "Ср" "Чт").
12.4.7. Функции ввода данных и указания объектов
Эти функции дают возможность пользователю вводить данные в интерактивном режиме. К основным функциям ввода данных и указания объектов относятся следующие:
Вызов функции initget должен предшествовать в программе обращению к функциям getint, getreal, getdist, getangle, getorient, getpoint, getcorner, getkword, entsel, nentsel и nentselp, поскольку устанавливает
в этих функциях ограничения на ввод пользователем данных. В частности, initget может сохранять за пользователем возможность нажатия клавиши <Enter> вместо ввода данных (например, для выбора значения по умолчанию), а может и не сохранять. Аргумент <флаг> функции initget — это битовый флаг, который должен быть целым числом (от 0 до 255), являющимся суммой битов с соответствующими каждому из них весовыми значениями (нумеруются по степеням числа 2). Биты описываются в табл. 12.3. Если бит не установлен, то его значение в битовом флаге считается равным нулю.
Таблица 12.3. Значения битов функции initget
Бит |
Назначение |
1 |
Не разрешает пустой ввод (пустой ввод— это нажатие только клавиши <Enter>) |
2 |
Не разрешает ввод нуля |
4 |
Не разрешает ввод отрицательных чисел |
8 |
Разрешает указание точек за пределами лимитов, несмотря на значение системной переменной LIMCHECK |
16 |
Данный бит не задействован |
32 |
Заставляет AutoCAD использовать штриховую линию вместо сплошной для резиновой нити и резиновой рамки; если значение системной переменной POPUPS равно нулю, то AutoCAD игнорирует данный бит |
64 |
Запрещает ввод третьей координаты для точки в функции getdist |
128 |
Разрешает произвольный ввод в качестве ключевого слова; преобладает над другими битами и ключевыми словами; в частности, данный бит позволяет игнорировать значение бита 1 |
Примеры использования битового флага:
(initget 1) — не разрешен пустой ввод;
(initget 3) — не разрешены пустой ввод и ввод нуля (3=1+2);
(initget 7) — не разрешены пустой ввод, ввод нуля и ввод отрицательных чисел (7=1+2+4);
(initget 6) — разрешен пустой ввод, но не разрешены ввод нуля и ввод отрицательных чисел (6=2+4).
Аргумент <строка> функции initget — это строка, ограниченная двойными кавычками с двух сторон, которая задает ключевые слова, являющиеся допустимыми вариантами ввода. В аргументе <строка> различные варианты ключевых слов разделяются одним или большим количеством пробелов, например: "А Б В Г Д". Если пользователю при выборе ключевого слова разрешаются сокращенные варианты, тогда обязательная часть ключевого слова и аргументе <строка> должна быть указана в верхнем регистре, например: "ПЕРесечение". В этом случае допустимыми вариантами ввода являются "ПЕР", "пер", "ПЕРЕ", "пере", "перес", "ПЕРЕСЕЧЕН", "ПЕРЕСЕЧЕНИЕ" (после трех обязательных букв может идти любое количество необязательных букв, вплоть до набора полного ключевого слова; при вводе букв верхний и нижний регистры равноправны); недопустимыми вариантами будут "П", "ПЕ", "п", "пе".
Возможны случаи, когда вариант сокращенного ввода начинается не с первой буквы ключевого слова, например:
(initget "выход") — здесь допустимыми вариантами ввода будут "х", "хо", "ход" или "выход", "вых", "выхо".
Если ключевое слово в аргументе <строка> указано только в верхнем или только в нижнем регистре, то рядом через запятую можно указать сокращенный вариант: "КРАСНЫЙ, КР" — равносильно записи "красный".
В локализованных версиях системы AutoCAD в аргументе <строка> могут присутствовать варианты ввода для локализованной и английской версий. Тогда в начале должны идти локализованные ключевые слова, а затем их английские аналоги, причем первый английский аналог должен начинаться с символа подчеркивания. Количество локализованных ключевых слов должно равняться количеству английских ключевых слов. Например: "прямой криволинейный _straight curved". В этом случае даже при вводе русского варианта будет возвращаться английское значение ("Straight" или "Curved").
Сама функция initget всегда возвращает nil. Однако за ней (не обязательно в следующей строке) должна идти функция, для которой функция initget установила ограничения ввода. В качестве возвращаемого значения эта функция (например, getreal) выдает допустимое значение, указанное пользователем, а в случае ввода сокращенного варианта ключевого слова — полный вариант ключевого слова.
Листинг 12.9. Пример 1 использования функции getint
(initget 6) (setq numb (getint "Введите номер участка (<1>): "))
В этом примере сначала устанавливаются ограничения на будущий ввод целого числа (не допускаются ноль и отрицательные числа). Допускается пустой ввод. Функция getint выводит на экран запрос "Введите номер участка (<1>) : ". Ее возвращаемым значением будет nil, если пользователь ответит простым нажатием клавиши <Enter>, или введенное пользователем допустимое целое число (например, 17). В случае ввода пользователем недопустимого целого числа (например, 0 или —2) функция getint выведет сообщение об ошибочном значении и будет ожидать допустимого варианта ввода.
В программе, приведенной в листинге 12.9, далее должна быть предусмотрена обработка пустого ввода для того, чтобы в этом случае присвоить переменной numb значение по умолчанию (1):
(if (not numb) (setq numb 1))
Листинг 12.10. Пример 2 использования функции getint
(initget 6 "Левый Правый Нижний Верхний")
(setq numb (getint "Введите номер участка (<1>) или [Левый/Правый/Нижний/ Верхний]: "))
В этом примере устанавливаются ограничения на будущий ввод целого числа (не допускаются ноль и отрицательные числа), однако допускаются пустой ввод и ввод одного из четырех ключевых слов. Далее в программе должен идти анализ значения переменной numb на nil, на положительное целое число и на ключевые слова ("левый", "правый", "нижний", "верхний").
Листинг 12.11. Пример использования функции getkword
(initget 1 "Да Нет") (setq reply (getkword "Продолжить цикл [Да/Нет]: "))
В этом примере переменной reply присваивается значение "да" или "нет", пустой ввод не допускается (хотя можно было бы разрешить пустой ввод и далее использовать его для выбора значения по умолчанию).
Поскольку не все биты функции initget используются со всеми функциями ввода данных, то в табл. 12.4 разбираются варианты применения (в случае возможности использования ставится плюс).
Функция |
Бит1 |
Бит 2 |
Бит 4 |
Битв |
Бит 32 |
Бит 64 |
Бит 128 |
getint |
+ |
+ |
+ |
|
|
|
+ |
getreal |
+ |
+ |
+ |
|
|
|
+ |
getdist |
+ |
+ |
+ |
|
+ |
+ |
+ |
getangle |
+ |
+ |
|
|
+ |
|
+ |
getorient |
+ |
+ |
|
|
+ |
|
+ |
getpoint |
+ |
|
|
+ |
+ |
|
+ |
getcorner |
+ |
|
|
+ |
+ |
|
+ |
getkword |
+ |
|
|
|
|
|
+ |
Функции entsel, nentsel и nentselp не используют биты функции initget и управляются только ключевыми словами.
Функция getstring не использует ни биты, ни ключевые слова функции initget.
Функции getvar и getenv позволяют прочитать значения, соответственно, системных переменных AutoCAD и переменных окружения.
Примеры:
(getvar "CECOLOR") — возвращает значение системной переменной "CECOLOR";
(getvar "PDMODE") — возвращает значение системной переменной "PDMODE";
(getenv "CONFIG") — возвращает значение переменной окружения "CONFIG".
Функция getfiled позволяет вызвать диалоговое окно выбора файла в удобном виде.
Пример:
(getfiled "Выберите файл данных для расчета"
"с: \\Ritm\\Genhull\\9ll" "dat" l6) — вызывает окно, приведенное на рис. 12.4:
Рис. 12.4. Диалоговое окно, вызываемое функцией getfiled
Второй аргумент функции getfiled, в зависимости от значения четвертого аргумента (<флаг>), может задавать либо имя файла по умолчанию, либо папку, с которой предлагается начать поиск. Битовый флаг, задаваемый аргументом <флаг> получается как сумма тех битов, нумеруемых степенями числа 2, которые будут установлены. Описание битов приведено в табл. 12.5.
Таблица 12.5. Значения битов функции getfiled
Бит |
Значение |
1 |
Подсказка имени при создании нового файла (нельзя использовать для выбора существующего файла) |
2 |
Не используется |
4 |
Разрешает вводить имя файла с любым расширением (или без расширения) |
8 |
Если бит 1 не установлен, то система AutoCAD выполняет поиск файла, имя которого задано в аргументе <имя> по списку путей, заданных в настройке, причем getfiled возвращает имя файла без пути; в противном случае возвращается полное имя файла (с путем) |
16 |
Аргумент <имя> трактуется как имя папки, в которой нужно искать файл; при этом в поле Имя файла: (File name:) ничего не вписывается по умолчанию (см. рис. 12.4); аналогичный эффект достигается и в том случае, если аргумент <имя> заканчивается обратной косой чертой |
32 |
Если установлен бит 1, то не будет выводиться предупреждающее сообщение при записи поверх существующего файла |
64 |
Не передает файл, если в качестве имени задан URL-адрес |
128 |
Не допускается использование URL-адресов |
С функциями getvar и getenv тесно связаны функции, выполняющие обратные операции:
В обеих функциях аргументы <имя> и <значение> должны быть текстовыми строками.
Функция setcfg является обратной по отношению к функции getcfg:
(setcfg <имя> <значение>) — запись данных приложения в раздел AppData файла acad.cfg; аргумент <имя> должен иметь вил "AppData/ имя_приложения/имя_раздела/ ... /имя_параметра" (до 496 символов длиной); возвращаемое значение функции setcfg — аргумент <значение> или nil (в случае ошибки).
12.4.8. Функции доступа к файлам
В данном разделе рассматриваются функции, выполняющие операции с файлами (поиск, открытие и закрытие), операции чтения и записи строк или символов в файл, а также вывода на экран различных сообщений:
Приведенные выше функции AutoLISP дают пользователю возможность работы только с файлами последовательного доступа. Указателем позиции в файле, с которой далее будет происходить чтение (запись), является переменная, в которой хранится дескриптор этого файла. Механизм работы с файлами виден на следующем примере.
Листинг 12.12. Пример использования функций работы с файлами
; Операции записи
(setq ff (open "с:\\test.txt" "w"))
(write-line "Первая строка" ff)
(write-char 65 ff) (write-char 67 ff)
(write-line "Вторая строка" ff)
(close ff)
; Операции чтения
(setq ff (open "c:\\test.txt" "r"))
(setq s1 (read-char ff))
(setq s2 (read-char ff))
(setq strl (read-line ff))
(setq str2 (read-line ff))
(setq str3 (read-line ff))
(close ff)
(setq ff nil)
Первое выражение — (setq ff (open "c: \\test.txt" "w")) — создает переменную ff, в которую заносится дескриптор файла c:\test.txt, открываемого для записи (в первом аргументе функции open обратная косая черта удваивается). Затем выражение (write-line "первая строка" ff) заносит в открытый файл первую запись (текст "Первая строка" из 13 символов) и символы с кодами 13 и 10, которые в файлах последовательного доступа разделяют записи.
Замечание
В операционной системе UNIX признаком конца записи является один символ с кодом 10.
После этого указатель файла показывает на начало второй записи, куда поочередно — с помощью двукратного применения функции write-char — заносятся символы с кодами 65 (буква "А") и 67 ("С"). Затем в продолжение второй записи файла с помощью функции write-line заносится текст "вторая строка" (13 символов) и добавляются символы с кодами 13 и 10, означающие конец второй записи файла. Вслед за этим файл с дескриптором ff закрывается функцией close.
Вторая часть листинга начинается функцией открытия того же файла, но уже в режиме чтения. Затем с помощью двукратного применения функций read-char читаются два первых символа первой записи, которые запоминаются в переменных s1 и s2. После этого в переменную strl читается остаток первой записи. Далее в переменную str2 читается вторая запись файла. Третья операция чтения с помощью функции read-line приносит nil, поскольку в файле c:\test.txt третьей записи нет. По окончании выполнения рассмотренной программы переменная ff очищается (в ней хранился дескриптор файла). Другие переменные получат такие значения:
s1 = 207 (код символа "П");
s2 = 229 (код символа "е");
strl = "рвая строка";
str2 =: "АСВторая строка";
str3 = nil.
Функции princ, prini, print и prompt чаще всего используются для вывода сообщений в командную строку системы AutoCAD, причем prini и print выводят сообщения в кавычках (другие особенности описаны выше). Вместо функции write-line для записи в файл можно пользоваться функцией princ, но заносить признак конца записи, который выглядит как "\n", пользователь должен сам. Например:
(princ "Первая запись\n" ff)
12.4.9. Функции доступа к примитивам
Рисунок в системе AutoCAD имеет организацию, аналогичную организации базы данных, в которой элементы (графические примитивы и неграфические объекты) имеют списковую структуру. Каждый примитив имеет свой тип. Перечислим все эти типы примитивов в алфавитном порядке: 3DFACE, 3DSOLID, ACAD_PROXY_ENTITY, ARC, ARCALIGNEDTEXT, ATTDEF, ATTRIB, BODY, CIRCLE, DIMENSION, ELLIPSE, HATCH, IMAGE, INSERT, LEADER, LINE, LWPOLYLINE, MLINE, MTEXT, OLEFRAME, OLE2FRAME, POINT, POLYLINE, RAY, REGION, RTEXT, SEQEND, SHAPE, SOLID, SPLINE, TEXT, TOLERANCE, TRACE, VERTEX, VIEWPORT, WIPEOUT, XLINE. Как правило, наименование типа совпадает с английским именем команды системы AutoCAD, которая создает графический объект. Поэтому полезно познакомиться с приложением 1, в табл. П 1.2 которого перечисляются английские наименования команд и соответствующие им русские наименования, а также с командами меню Express {приложение 5).
Функции, рассматриваемые в данном разделе, работают с примитивами рисунка, обращаясь непосредственно к их внутренней структуре. AutoLISP имеет средства выбора графических объектов, как по их порядковому номеру, так и по определенным признакам (цвету, слою и т. п.).
Функция entlast извлекает последний неудаленный основной примитив рисунка:
(entlast)
Функция возвращает nil, если в рисунке нет неудаленных примитивов (например, когда вы только что создали новый рисунок); иначе возвращается имя последнего основного примитива в следующем виде:
<Имя объекта: 14а4158>
(< Entity name: 14а4158>)
Полученное имя следует сохранить в переменной, например:
(setq eela (entlast))
Функция entnext позволяет перейти в рисунке к следующему примитиву (подпримитиву):
(entnext [<примитив>])
В качестве единственного аргумента функции entnext может выступать ранее полученное имя примитива текущего рисунка. Если функция вызывается без аргумента, то она возвращает имя первого неудаленного примитива в базе рисунка. При наличии аргумента функция возвращает имя следующего примитива, либо nil, если база графических объектов рисунка исчерпана.
Пример:
(setq el (entnext)) — возвращает имя первого неудаленного примитива;
(setq e2 (entnext е2)) — возвращает имя примитива, следующего за e1;
(setq еЗ (entnext еЗ)) — возвращает имя примитива, следующего за e2.
Функция entlast возвращает имя последнего основного примитива. Это означает, что если последним созданным графическим объектом рисунка является сложный объект (например, полилиния типа POLYLINE), то вслед за ним в базе рисунка следуют подпримитивы, т. е. вершины (имя примитива — VERTEX), а завершается перечисление подпримитивов полилинии примитивом SEQEND.
Пример (в предположении, что объект типа POLYLINE является последним основным примитивом):
(setq eela (entlast)) — возвращает имя основного примитива последней полилинии;
(setq v1 (entnext eela)) — возвращает имя примитива, являющегося первой вершиной полилинии;
(setq v2 (entnext v1)) — возвращает имя примитива, являющегося второй вершиной полилинии.
Замечание
В легкой полилинии (примитиве типа LWPOLYLINE) нет подпримитивов и информация обо всех вершинах может быть извлечена из основного примитива.
Функция entsel предлагает пользователю указать один объект, выдавая соответствующий запрос:
(entsel [запрос] )
Здесь аргумент <запрос> — любая строка текста. Функция возвращает список, состоящий из двух элементов: имени выбранного примитива и точки, которой пользователь указал объект (такая точка, как правило, оказывается вне самого примитива, поскольку точность указания мышью зависит от величины прицела). Можно указать объект вводом с клавиатуры ключевого слова Последний (Last) — тогда в возвращаемом списке координаты точки будут нулевыми.
Пример:
(setq esl (entsel "Выберите объект: ")) — возвращает (<Имя объекта: 14а9960> (301.791 138.438 0.0)).
Имя указанного пользователем объекта может быть извлечено из такого списка, например, с помощью функции car.
Функция entdel позволяет удалять неудаленные основные примитивы и восстанавливать примитивы, ранее удаленные в данном сеансе редактирования:
(entdel <примитив>)
Функция возвращает имя удаляемого (восстанавливаемого) примитива. Следует иметь в виду, что при операции сохранения рисунка все примитивы, помеченные как удаленные, из рисунка стираются и уже больше не могут быть восстановлены.
Функция entget является основным инструментом извлечения информации о примитиве, т. к. получает список с его характеристиками:
(entget <примитив> [<приложения>])
Здесь аргумент <примитив> — это имя примитива для получения его данных, аргумент <приложения> — это список с именами приложений, с помощью которых к примитиву привязаны расширенные данные (о расширенных данных см. разд. 12.4.11).
Предположим, в рисунке первым объектом является отрезок (тип примитива — LINE). Тогда выражение (setq le (entget (entnext) ) ) должно вернуть примерно такой список:
((-1 . <Имя объекта: 14а4158>) (0 . "LINE") (330 . <Имя объекта: 14a40f8>) (5 . "2В") (100 . "AcDbEntity") (67 . 0) (410 . "Model") (8 . "0") (100 . "AcDbLine") (10 201.536 140.622 0.0) (11 285.148 96.5053 0.0) (210 0.0 0.0 1.0))
В этом списке элементами являются точечные пары и списки, причем и в тех, и в других первыми элементами- выступают целые числа, называемые DXF-кодами (см. также разд. 12.4.6). Под соответствующим кодом в точечных парах и списках находятся данные определенного типа: код -1 указывает имя примитива (<имя объекта: l4a4l58>), код 0 — тип примитива ("LINE"), код 5 — метку (внутренний номер примитива в рисунке), код 410 — имя вкладки пространства модели или листа, код 8 — имя слоя ("0"), код 10 - координаты начальной точки (201.536 140.622 0.0), код 11 — координаты конечной точки (285.148 96.5053 0.0), код 210— направление вектора нормали к плоскости, в которой описан примитив. Остальные коды, не имеющие принципиального значения, здесь не рассматриваются. С помощью функции assoc можно из списка с характеристиками объекта извлечь нужную точечную пару, а затем, применив функцию cdr, получить данные необходимого DXF-кода.
Продолжим пример с отрезком:
(cdr (assoc о le)) — возвращает "LINE" (тип примитива);
(cdr (assoc 8 le)) — возвращает "0" (имя слоя);
(cdr (assoc 10 le)) — возвращает (201.536 140.622 0.0);
(cdr (assoc 11 le)) — возвращает (285.148 96.5053 0.0).
Кроме того, по коду 62 можно было бы извлечь номер цвета примитива, по коду 6 — имя типа линии, по коду 48 — собственный масштаб типа линии, по коду 311 — вес линии (умноженный на 100). В рассмотренном выше списке le точечных пар с такими DXF-кодами нет, поэтому для них действуют значения по умолчанию: ПОСЛОЮ (BYLAYER) или 1.
Рассмотрим, какой примерный список можно было бы получить для окружности (объекта типа CIRCLE):
((-1 . <Имя объекта: 14а4160>) (0 . "CIRCLE") (330 . <Имя объекта: 14a40f8>) (5 . "2С") (100 . "AcDbEntity") (67 . 0) (410 . "Model") (8 . "0") (100 . "AcDbCircle") (10 387.691 142.198 0.0) (40 . 27.8941) (210 0.0 0.0 1.0))
Для окружности DXF-коды -l, 0, 410, 8, 210 имеют тот же смысл, что и для всех примитивов (в том числе и для примитива типа "LINE"). Под кодом 40 находится радиус окружности, а точечная пара с DXF-кодом 10 хранит в себе данные о центре.
Поэтому для рассматриваемой окружности:
(cdr (assoc 40 le)) — возвращает 27.8941 (радиус);
(cdr (assoc 10 le) ) — возвращает (387.691 142.198 0.0).
Сравнивая справочную информацию, выдаваемую командой СПИСОК (LIST), и список, получаемый с помощью функции entget, можно получить геометрический смысл DXF-кодов для примитивов других типов.
Если построить в программе список, описывающий примитив (кроме точечной пары с флагом -1), то можно создать в рисунке такой примитив с помощью следующих функций:
Пример:
(entmake '((0 . "CIRCLE") (10 500.0 0.0 0.0) (40 . 50.0)))
создает новый примитив — окружность с центром в точке (500 0 0) и радиусом 50; остальные свойства (слой, цвет и т. д.), данные о которых отсутствуют в списке, берутся по умолчанию.
Функция entmod похожа на функцию entmake, но получает в качестве аргумента список, который содержит точечную пару с DXF-кодом -1 (т. е. имя существующего в рисунке примитива) и модифицирует примитив в соответствии с новым списком (в списке могут изменяться любые данные, кроме имени примитива, типа примитива и метки):
(entmake <список>)
Функция entmod изменяет примитив в базе рисунка и возвращает аргумент <список> при успешном завершении или nil — при невозможности выполнить изменение. Для перерисовки примитива на экране следует воспользоваться функцией entupd:
(entupd <примитив>)
Здесь аргумент <примитив> — это имя примитива в том виде, в котором оно выводится, например, функцией entlast.
Другие функции доступа к примитивам:
Функция nentsel работает с блоками (точнее, с примитивами типа INSERT) следующим образом. В случае, если пользователь указал атрибут блока, то функция возвращает список из двух элементов: первым является имя объекта-атрибута, а вторым — точка указания атрибута. В случае, если пользователь указал не атрибут, а объект, принадлежащий вхождению блока, то возвращается список из четырех элементов: первым является имя примитива, с помощью которого был указан блок, вторым — точка указания, третьим — матрица размером 4х3 для преобразования точек из системы координат объекта (СКО) в МСК, четвертым — список с именем блока, содержащим указанный примитив (если примитив входит в блок, который вложен в другой блок. то список содержит все имена вкладываемых блоков, начиная от самого внутреннего и кончая самым внешним).
Матрица размером 4х 3, которая выдается в качестве третьего элемента возвращаемого функцией nentsel значения, имеет вид: ((m00 m01 m02) (m10 m11 m12) (m20 m21 m22) (m30 m31 m32)). Тогда преобразование точек из СКО в МСК идет по системе уравнений:
X' = Х*m00 + Y*m10 + Z*m20 + m30
Y' = Х*m01 + Y*m11 + Z*m21 + m31
Z' = X*m02 + Y*m12 + Z*m22 + m32
Здесь (X Y Z) — координаты точки до преобразования, (X' Y' Z') — координаты точки после преобразования.
Функция nentselp работает аналогично функции nentsel, но в случае, если пользователь указал объект, принадлежащий вхождению блока, то тоже возвращается список из четырех элементов, но третьим элементом является матрица размером 4х4, которая служит для преобразования точек из системы координат объекта (СКО) в МСК. Матрица имеет вид: ((n00 n01 n02 n03) (n10 n11 n12 n13) (n20 n21 n22 n23) (0.0 0.0 0.0 1.0)). Преобразование точек из СКО в МСК идет по такой системе уравнений:
X' = Х*n00 + Y*n01 + Z*n02 + n03
Y' = X*n10 + Y*n11 + Z*n12 + n13
Z' = X*n20 + Y*n21 + Z*n22 + n33
Смысл списков (X Y Z) и (X' Y' Z') тот же, что и для функции nentsel.
Следующая группа функций — это функции, работающие с наборами выбора. Набор — это временное множество, в которое входят имена основных примитивов рисунка (как правило, они собираются по какому-то признаку-фильтру). Неосновные примитивы (составные части блока или вершины полилинии) не могут входить в набор. Имя набора обычно сохраняется в какой-нибудь переменной AutoLISP, чтобы затем можно было извлекать из набора нужные примитивы.
Замечание
При необходимости сохранять наборы примитивов в рисунке следует оформлять их в виде групп с помощью команды ГРУППА (GROUP). Группы относятся к неграфическим объектам, существующим внутри рисунка.
Функция ssget формирует набор по запросу или признаку:
(ssget [<метод>] [<точка1> [<точка2>]] [<список>] [<фильтр>])
Аргумент <метод> — это текстовая строка, принимающая одно из следующих значений (в локализованной версии системы AutoCAD все перечисляемые строки должны спереди дополняться символом "_"), характеризующих метод выбора примитивов:
Аргумент <точка1> при отсутствии аргумента <точка2> определяет дополнительное условие для включения в набор: включаемый примитив должен проходить через точку, заданную аргументом <точка1>. Если заданы оба аргумента <точка1> и <точка2>, то заданные ими точки являются углами простой или секущей рамки (когда в качестве метода выбора применяются рамки, т. е. "W" или "С").
Аргумент <список> представляет собой список, элементами которого являются точки, используемые в методах "F", "WP", "СР".
Аргумент <фильтр> — это список со структурой, аналогичной структуре списка, возвращаемого функцией entget, что позволяет оставить только те примитивы, которые удовлетворяют дополнительным признакам (например, определенному цвету, слою, весу и т. д.).
Возвращаемое значение функции ssget — имя созданного набора (или nil, если создать набор невозможно).
Примеры:
(ssget) — выдает стандартный запрос:
Выберите объекты:
(Select objects:)
и далее создает набор в соответствии с дальнейшими указаниями пользователя;
(ssget '(15.78 320.1)) — создает набор из примитивов, проходящих через точку (15.78 320.1);
(ssget "_с" '(1.57 -40.4) '(15.78 320.1)) — создает набор из примитивов, выбираемых секущей рамкой с углами в точках (1.57 -40.4) и (15.78 320.1);
(ssget "_x" '((0 . "POLYLINE") (8 . "WALLS"))) — создает набор из всех примитивов рисунка, являющихся полилиниями типа POLYLINE и лежащими на слое WALLS;
(ssget " А") — создает набор из всех примитивов рисунка, аналогично методу "_Х" с пустым фильтром, т. е. (ssget "_Х" '()) или (ssget "_X" nil).
В рисунке одновременно может быть открыто не более 128 наборов выбора. При достижении такого предела функция ssget отказывается создавать следующие наборы и возвращает nil. Чтобы удалить ненужные наборы, следует присвоить значение nil переменным, хранящим имена этих наборов.
Замечание
В предыдущих версиях системы AutoCAD количество наборов, которые могут быть одновременно открыты, было еще меньше (например, в 10-й версии оно равнялось всего шести).
Для манипуляций с наборами в языке AutoLISP предусмотрены следующие функции:
Функция ssnamex возвращает список, в котором элементами являются описания способа выбора примитивов, попавших в набор. Если аргумент <номер> не задан, то длина возвращаемого списка равна количеству элементов, определяемых аргументом <набор>. Если аргумент <номер> задан и задан корректно, то возвращаемым значением будет список из одного элемента — описания способа выбора примитива с порядковым номером, равным значению аргумента <номер>. Если же аргумент <номер> задан неправильно, то функция ssnamex вернет nil.
Описание способа выбора примитива — это список из трех элементов: (<метод> <примитив> <данные>). Параметр <метод> — целое число, которое соответствует методу выбора функции ssget и может принимать такие значения:
Если на месте параметра <метод> стоит отрицательное число, то оно определяет многоугольник выбора (см. описание многоугольника ниже).
Параметр <примитив> описания способа выбора примитива является его именем в том виде, в котором оно выводится функцией entiast. Параметр <данные> может быть либо только числом 0, либо числом 0 и списком, уточняющим выбор, либо числом 0 и номером многоугольника. В уточняющем списке может быть точка, с помощью которой выбран объект. Если использован номер многоугольника (а он должен быть отрицательным: —1 , —2 и т. п.), то далее будет следовать описание многоугольника с соответствующим номером.
Описание многоугольника— это список такого вида: (<номер_многоуголь-ника> <точка1> ... <точкаN>). Нумерация многоугольников начинается с —1 и наращивается путем добавления -1 к очередному номеру.
Описание точки (<точка1> и т. п.) — это список из трех (или двух) элементов: (<код> <база> [<вектор>] ). Описание точки зависит от вида, на котором она выбиралась, и фактически представляет собой описание прямой, луча или отрезка. Параметр <код> является кодом описания точки (0 — прямая, 1 — луч, 2 — отрезок), параметр <база> представляет собой начальную точку описания, а необязательный параметр <вектор> — это либо направление, в котором продолжается бесконечная линия (прямая или луч), либо смещение в сторону, характерное для отрезка.
Пример:
Если в набор nab5 включен один объект, который был указан точкой (7.51 16.02) на обычном виде сверху в МСК, то выражение
(ssnamex nab5 0) возвращает примерно следующее: ((1 <имя объекта: 14аа560> 0 (0 (7.51 16.02 0.0)))).
В этом примере возвращенный функцией ssnamex список состоит из одного элемента (списка), в котором присутствуют следующие составные части:
Другие примеры значений, возвращаемых функцией ssnamex:
((0 <Имя объекта: 14ае578> 0)) — один примитив, выбранный неин-терактивным методом (например, "L");
((3 <Имя объекта: 14ае568> О -1) (3 <Имя объекта: 14ае578> 0 -1) (-1 (0 (-147.787 129.133 0.0)) (0 (-64.2728 129.133 0.0)) (0 (- 64.2728 22.3376 0.0)) (0 (-147.787 22.3376 0.0)))) — два примитива, выбранных секущим прямоугольником с номером —1 (для прямоугольника заданы четыре точки);
((1 <Имя объекта: 14ае560> 0 (0 (74.2672 64.8218 0.0))) (2 <Имя объекта: 14ае570> 0 -1) (2 <Имя объекта: 14ае568> 0 -1) (-1 (0 (-177.446 158.755 0.0)) (0 (40.3151 158.755 0.0)) (0 (40.3151 83.1407 0.0)) (0 (-177.446 83.1407 0.0)))) — три примитива, первый из которых выбран указанием с помощью точки (74.2672 64.8218 0.0), а второй и третий выбраны рамкой, которая фигурирует как многоугольник с номером -1.
В этих примерах параметр <вектор> описания точек нигде не использован, т. е. объекты выбирались на виде сверху в МСК.
Наборы — удобный инструмент для выбора нужных примитивов рисунка с заданными свойствами. После формирования набора затем из него с помощью функции ssname (или ssnamex) извлекаются нужные объекты, с которыми далее выполняются необходимые операции (например: удаление, модификация и т. д.).
12.4.10. Функции доступа к табличным данным и неграфическим объектам
Помимо примитивов в рисунке всегда присутствует определенный объем неграфической информации: описания блоков, таблицы слоев, таблицы видовых экранов, таблицы типов линий, размерные стили и т. п. Все они хранятся в базе рисунка примерно в том же виде, что и примитивы.
Для доступа к табличным объектам в языке AutoLISP имеются такие функции:
В качестве допустимых значений параметра <таблица> во всех трех функциях могут фигурировать только следующие текстовые строки:
Схема использования рассмотренных функций будет ясна из примеров:
(5etq lay1 (tbinext "LAYER" T)) — возвращает список с данными первого слоя рисунка (первым слоем должен быть слой с именем "0"), например: ((0 . "LAYER") (2 . "0") (70 . 0) (62 . 7) (6 . "Continuous")); в этом списке в точечной паре с DXF-кодом 0 находится имя таблицы, в которой ищется неграфический объект, с кодом 2 — имя слоя, с кодом б — тип линий примитивов слоя по умолчанию (т. е. каким типом отображать объекты с типом линии ПОСЛОЮ (BYLAYER)), с кодом 62 — цвет слоя по умолчанию, 70 — состояние слоя по отношению к операциям блокирования, замораживания; нетрудно заметить, что аналогичные DXF-коды используются в списке, возвращаемом функцией entget для примитивов;
(setq lay2 (tbinext "LAYER")) — возвращает список с данными второго слоя рисунка, например: ((0 . "LAYER") (2 . "стена1") (70 . 4) (62 . 40) (6 . "ограждение1"));
(setq lay2name (tbiobjname "LAYER" "Стена1")) — возвращает <Имя объекта: 14аа570>; теперь с помощью функции entget можно получить тот же список, что и lay2 в предыдущем примере.
С помощью этих трех функций можно проанализировать наличие в рисунке таких элементов, как описания блоков, пользовательские системы координат или текстовые стили с определенными именами и выбрать соответствующий вариант работы программы.
Свойствами, аналогичными таблицам, обладают в рисунке словари (dictionaries). К ним относятся, например, словари стилей мультилиний, групп, растровых изображений, листов чертежа. Пользовательские приложения могут создавать свои словари. Следующие функции выполняют операции над словарями и их элементами:
12.4.11. Функции, работающие с расширенными данными
К любому примитиву рисунка можно добавлять расширенные данные, т. е. текстовые или числовые данные, которые желательно хранить вместе с геометрией (например, марки материалов, наименования узлов, секций и т. п.). Обычно наличие такой информации позволяет разрабатывать программы связи с базами данных (рассмотрение таких программ не является предметом настоящей книги). Расширенные данные связываются с именем определенного приложения. У одного графического примитива могут быть данные нескольких приложений, которые располагаются последовательно в определенной структуре.
Если расширенные данные присоединены к примитиву, то в базе рисунка они следуют за обычными данными (DXF-коды этих данных находятся в диапазоне от —2 до 239). Извлечь расширенные данные можно с помощью функции entget, при обращении к которой нужно указывать имя приложения, с которым они связаны (см. описание функции entget в разд. 12.4.9). В списке, возвращаемом функцией entget, расширенные данные находятся в подсписке, начинающемся с DXF-кода —3 (признак наличия расширенных данных), в котором далее располагаются точечная пара с кодом 1001 (начало расширенных данных и имя приложения, с которым они связываются) и точечные пары с DXF-кодами 1000, 1002—1071. Назначение DXF-кодов расширенных данных описывается в табл. 12.6.
Таблица 12.6. Назначение DXF-КОДОВ расширенных данных
Код |
Назначение |
1000 |
Строковая константа длиной не более 255 знаков (256-й знак зарезервирован под символ с кодом 0) |
1001 |
Строковая константа, используемая только для указания имени приложения, длиной не более 31 знака (если код 1001 попадает внутрь списка, организуемого с помощью DXF-КОДОВ 1002, то он интерпретируется как обычная константа, а не имя приложения; см. далее описание DXF-кода 1002). Имя приложения должно состоять только из цифр, латинских букв и символов |
1003 |
Имя слоя, с которым связываются расширенные данные |
1005 |
Метка объекта в базе чертежа |
1010 |
Трехмерная точка (три вещественных числа) |
1040 |
Вещественное число |
1070 |
16-битовое целое число (со знаком или без знака) |
1071 |
32-битовое целое число со знаком (длинное целое). Хотя система AutoCAD хранит данные группы с кодом 1071 как длинное целое, AutoLISP возвращает их как вещественные числа. Функциям entmake и entmod в точечной паре с кодом 1071 можно подавать либо целые числа, либо вещественные. ObjectARX трактует данные группы 1071 как длинные целые |
1002 |
Строка, принимающая значения "{" или "}" и имеющая тот же смысл, что и левая и правая круглые скобки в обозначении списка. С помощью группы с кодом 1002 можно формировать в расширенных данных списковые структуры (они должны начинаться открывающей скобкой с кодом 1002, затем должны идти данные, включаемые в список, а закрывающая скобка с кодом 1002 завершает оформление списка). См. также замечание об интерпретации данных группы 1001 в таком списке |
1004 |
Двоичные данные длиной не более 127 байт. Обрабатываются только в ObjectARX |
1011 |
Трехмерная точка, интерпретируемая как положение в МСК. Такая точка перемещается, масштабируется, поворачивается и симметрируется одновременно с родительским примитивом рисунка. Если точка попадает в рамку выбора команды РАСТЯНУТЬ (STRETCH), то она подвергается операции растягивания вместе с родительским примитивом |
1012 |
Трехмерная точка, интерпретируемая как перемещение в МСК. Такая точка не перемещается и не растягивается одновременно с основным примитивом, но участвует вместе с ним в операциях масштабирования, поворота и симметрирования |
1013 |
Трехмерная точка, интерпретируемая как направление в МСК. Такая точка не масштабируется, не перемещается и не растягивается одновременно с основным примитивом, но участвует вместе с ним в операциях поворота и симметрирования. Вектор с координатами, взятыми из группы с кодом 1013, является нормализованным, т. е. единичной длины |
1041 |
Вещественное число, интерпретируемое как расстояние. Оно масштабируется одновременно с родительским примитивом |
1042 |
Вещественное число, интерпретируемое как масштабный коэффициент. Масштабируется одновременно с родительским примитивом |
Как было сказано выше, расширенные данные связываются с именем конкретного приложения. Имена приложений хранятся в рисунке в таблице символов "APPID" (см. разд. 12.4.10). Чтобы занести имя приложения в эту таблицу, следует пользоваться функцией rеgарр:
(rеgарр <приложение>)
Параметр <приложение> должен быть текстовой строкой (по возможности нужно выбирать имя приложения таким, чтобы оно не совпадало с именами приложений других разработчиков — иначе это приведет к неправильной работе с расширенными данными). Функция rеgарр возвращает nil в случае ошибки (nil возвращается также, если приложение с таким именем уже регистрировалось).
Функция entmake (описание см. в разд. 12.4.9) может создать примитив и сразу присоединить к нему расширенные данные. С помощью функции entmod можно добавить расширенные данные к существующему графическому объекту.
Предположим в качестве примера, что к последнему существующему примитиву рисунка (пусть им является отрезок) нужно добавить расширенные данные, связанные с приложением "GENHULL":
(setq le (entget (entlast) ) ) — сохраняет в переменной le список последнего примитива, у которого еще нет расширенных данных: ((-1 . <Имя объекта: 14аа578>) (0 . "LINE") (330 . <Имя объекта: 14aa4f8>) (5 . "2F") (100 . "AcDbEntity") (67 . 0) (410 . "Model") (8 . "0") (62 . 1) (100 . "AcDbLine") (10 85.4655 223.335 0.0) (11 187.712 167.209 0.0) (210 0.0 0.0 1.0));
(setq edata '((-3 ("GENHULL" (1000 . "Пластмасса") (1070 . 57))))) — сохраняет в переменной edata список с расширенными данными, которые будут добавлены к списку le (добавляются текстовая строка с DXF-кодом 1000 и целое число с DXF-кодом 1070);
(setq le2 (append le edata)) — объединяет списки le и edata и формирует новый список le2: ((-1 . <Имя объекта: 14аа578>) (0 . "LINE") (330 . <Имя объекта: 14aa4f8>) (5 . "2F") (100 . "AcDbEntity") (67 . 0) (410 . "Model") (8 . "0") (62 . 1) (100 . "AcDbLine") (10 85.4655 223.335 0.0) (11 187.712 167.209 0.0) (210 0.0 0.0 1.0) (-3 ("GENHULL" (1000 . "Пластмасса") (1070 . 57))));
(entmod le2) — модифицирует примитив по новому списку и возвращает то же значение, что и предыдущее выражение;
(entget (entlast) '("GENHULL")) — получает список нового последнего примитива вместе с расширенными данными, относящимися к приложению "GENHULL": ((-1 . <Имя объекта: 14аа578>) (0 . "LINE") (330 . <Имя объекта: 14aa4f8>) (5 . "2F") (100 . "AcDbEntity") (67 . 0) (410 . "Model") (8 . "0") (62 . 1) (100 . "AcDbLine") (10 85.4655 223.335 0.0) (11 187.712 167.209 0.0) (210 0.0 0.0 1.0) (-3 ("GENHULL" (1000 . "Пластмасса") (1070 . 57)))).
Размер расширенных данных любого примитива не должен превышать 16 килобайт (16 383 байта). Поэтому по мере добавления таких данных их размер нужно контролировать. Этой цели служат две функции:
Перед записью в зону примитива новой порции расширенных данных нужно проверить, достаточно ли для них места.
Коротко рассмотрим другие функции языка AutoLISP.
Функция alert позволяет вывести на экран диалоговое окно с сообщением системы AutoCAD. Синтаксис функции:
(alert <сообщение>)
Функция возвращает nil. Максимальная длина строки <сообщение> зависит от многих вещей (в частности, от платформы) и может быть вычислена пользователем самостоятельно. В случае превышения допустимой длины AutoCAD обрезает сообщение. При необходимости вывести сообщение в несколько строк нужно использовать знаки конца строки ("\n").
Пример:
(setq lines (strcat "Я вас любил: любовь еще, быть может,\n"
"В душе моей угасла не совсем;\nНо пусть она вас больше не тревожит;\n"
"Я не хочу печалить вас ничем."))
(alert lines) выводит на экран окно, изображенное на рис. 12.5.
Рис. 12.5. Диалоговое окно, вызываемое функцией alert
В данном примере выведены четыре строки, поскольку трижды был использован знак конца строки. Выход из диалогового окна, изображенного на рис. 12.5, — щелчок по кнопке ОК.
Несколько полезных геометрических функций:
Примеры:
(inters '(2.25 4.50) '(-2.25 6.335) '(1.27 0.50) '(-8.65 -2.30)) — возвращает nil;
(inters '(2.25 4.50) '(-2.25 6.335) '(1.27 0.50) '(-8.65 -2.30) nil) — возвращает (7.64593 2.29966);
(polar '(16.32 4.782) 0.345 4.79) — возвращает (20.8278 6.40196);
(osnap '(16.32 4.782) "_end,_mid") — возвращает координаты точки, уточненной с помощью применяемых одновременно функций объектной привязки к конечной и средней точкам.
К полезным функциям, кроме того, относятся функции работы с элементами меню:
Подробности описания этих функций в настоящей книге не приводятся (их можно найти в справочной системе AutoCAD).
В языке AutoLISP есть некоторое количество функций для работы с диалоговыми окнами (они тоже из-за недостатка места здесь не рассматриваются):
К рассматриваемой версии языка AutoLISP в качестве расширения могут быть добавлены более ста функций, реализующих технологию ActiveX и дополняющих возможности пользователя по созданию эффективных программных продуктов на базе системы AutoCAD 2000. Все эти функции начинаются с префиксов vir-, via- и vlax-, и перед первым обращением к любой из них надо выполнить функцию загрузки (без этого они не будут доступны):
(vl-load-com)
Технология ActiveX облегчает
и ускоряет доступ к примитивам и их характеристикам. К примитивам рисунка можно
прикрепить другие объекты, называемые реакторами. Реакторы отслеживают
определенные будущие события (например, перемещение или удаление примитива,
открытие, сохранение или закрытие рисунка, вставку блока или внешней ссылки).
Как только проверяемое событие происходит, реакторы с помощью специальных средств
выполняют запрограммированные операции.