![]()
![]() ![]()
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
Примеры использования битового флага: (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 разбираются варианты применения (в случае возможности использования ставится плюс).
Функции 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
С функциями 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, которые в файлах последовательного доступа разделяют записи.
После этого указатель файла показывает на начало второй записи, куда поочередно - с помощью двукратного применения функции 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)) - возвращает имя примитива, являющегося второй вершиной полилинии.
Функция 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, чтобы затем можно было извлекать из набора нужные примитивы.
Функция 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 переменным, хранящим имена этих наборов.
Для манипуляций с наборами в языке 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-КОДОВ расширенных данных
Как было сказано выше, расширенные данные связываются с именем конкретного приложения. Имена приложений хранятся в рисунке в таблице символов "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 облегчает
и ускоряет доступ к примитивам и их характеристикам. К примитивам рисунка можно
прикрепить другие объекты, называемые реакторами. Реакторы отслеживают
определенные будущие события (например, перемещение или удаление примитива,
открытие, сохранение или закрытие рисунка, вставку блока или внешней ссылки).
Как только проверяемое событие происходит, реакторы с помощью специальных средств
выполняют запрограммированные операции.
![]() ![]()
электронной версии ©2002 ЦИТ СГГА - издатель Все права защищены. |