Опубликован: 21.02.2012 | Доступ: свободный | Студентов: 2061 / 499 | Оценка: 4.20 / 4.40 | Длительность: 19:35:00
Специальности: Разработчик аппаратуры
Лекция 3:

AutoLISP в среде Visual LISP

Функции доступа к файлам

В данном разделе рассматриваются функции, выполняющие операции с файлами (поиск, открытие и закрытие), операции чтения и записи строк или символов в файл, а также вывода на экран различных сообщений:

  • (findfile <файл>) - поиск файла по имени (краткому или полному) или поиск папки по полному имени; если аргумент не содержит полного пути, то поиск выполняется по стандартным путям поиска файлов для системы AutoCAD; возвращается полное имя файла (папки), если он найден, или nil в противном случае;
  • (open <файл> <режим>) - открытие файла с именем, заданным аргументом <файл>; аргумент <режим> - это строка из одного символа, определяющего режим, в котором открывается файл ("w" или "W" - запись, "а" или "А" - дополнение, "г" или "R" - чтение); в предыдущих версиях AutoLISP не допускалось указание режима в верхнем регистре; если аргумент <файл> указывает на несуществующий файл в режимах "w", "W", "а" и "А", то он создается; возвращаемое значение - дескриптор файла (при успешном открытии) или nil (при неудаче);
  • (close <дескриптор>) - закрытие файла; единственным аргументом является <дескриптор> - переменная, создаваемая функцией open и управляющая процессами записи и чтения: функция close возвращает nil, если указан действительный дескриптор, или сообщение об ошибке;
  • (read-line [<дескриптор>]) - чтение строки из файла с заданным дескриптором; если аргумент <дескриптор> опущен, то выполняется чтение строки с клавиатуры (строка должна закончиться нажатием клавиши <Enter>);
  • (write-line <строка> [<дескриптор>]) - запись строки, заданной аргументом <строка> в файл с заданным дескриптором; если аргумент <дескриптор> опущен или равен nil, то система AutoCAD выводит строку на экран; при записи строка, заданная аргументом <строка>, заносится в файл (или на экран) без ограничивающих ее двойных кавычек;
  • (princ [<аргумент> [<дескриптор>]]) - запись аргумента, который может быть любым выражением AutoLISP (а также числом, списком, строкой, именем примитива и т. д.), в файл с заданным дескриптором. Если аргумент <дескриптор> опущен или равен nil, то <аргумент> выводится на экран. Если <аргумент> - строка, то при записи она заносится в файл (или на экран) без ограничивающих ее двойных кавычек. Если опущены оба аргумента, то функция осуществляет так называемый тихий выход, т. е. не возвращает никакого значения, в том числе nil;
  • (prinl<аргумент> [<дескриптор>] ]) - функция аналогична princ, но если <аргумент> - строка, то при записи она заносится в файл (или на экран) с ограничивающими двойными кавычками; специальные символы (обратная косая черта, кавычки, конец строки и т. п.) предваряются обратной косой чертой; если опущены оба аргумента, то функция prinl тоже осуществляет тихий выход;
  • (print [<аргумент> [<дескриптор>]]) - функция аналогична prini, но при выводе добавляет перед аргументом символ конца строки предыдущей записи, а после аргумента - пробел; тоже может осуществлять тихий выход;
  • (read-char [<дескриптор>]) - чтение символа из файла. Если аргумент не задан, то производится чтение символа из буфера клавиатуры. Возвращает код прочитанного символа (аналогично функции ascii); нажатие на клавишу <Enter> возвращает код 10;
  • (write-char <код> [<дескриптор>]) - запись символа с заданным кодом в файл; если аргумент не задан, то вывод символа на экран; возвращает код символа; невозможно с помощью данной функции записать символ с кодом 0;
  • (prompt <сообщение>) - вывод сообщения на экран (если используется установка системы AutoCAD на двухэкранный вычислительный комплекс, то на оба экрана; чтобы избежать этого, можно пользоваться для вывода сообщений функцией princ).

Приведенные выше функции AutoLISP дают пользователю возможность работы только с файлами последовательного доступа. Указателем позиции в файле, с которой далее будет происходить чтение (запись), является переменная, в которой хранится дескриптор этого файла. Механизм работы с файлами виден на следующем примере.

Пример использования функций работы с файлами

 
; Операции записи 
(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)

Функции доступа к примитивам

Рисунок в системе 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, которая создает графический объект.

Функции, рассматриваемые в данном разделе, работают с примитивами рисунка, обращаясь непосредственно к их внутренней структуре. AutoLISP имеет средства выбора графических объектов, как по их порядковому номеру, так и по определенным признакам (цвету, слою и т. п.).

Функция entlast извлекает последний неудаленный основной примитив рисунка:

(entlast) 

Функция возвращает nil, если в рисунке нет неудаленных примитивов (например, когда вы только что создали новый рисунок); иначе возвращается имя последнего основного примитива в следующем виде:

(<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 "Выберите объект: ")) - возвращает (<Entityname: 14а9960> (301.791 138.438 0.0)). 

Имя указанного пользователем объекта может быть извлечено из такого списка, например, с помощью функции car.

Функция entdel позволяет удалять неудаленные основные примитивы и восстанавливать примитивы, ранее удаленные в данном сеансе редактирования:

(entdel <примитив>) 

Функция возвращает имя удаляемого (восстанавливаемого) примитива. Следует иметь в виду, что при операции сохранения рисунка все примитивы, помеченные как удаленные, из рисунка стираются и уже больше не могут быть восстановлены.

Функция entget является основным инструментом извлечения информации о примитиве, т. к. получает список с его характеристиками:

(entget <примитив> [<приложения>]) 

Здесь аргумент <примитив> - это имя примитива для получения его данных, аргумент <приложения> - это список с именами приложений, с помощью которых к примитиву привязаны расширенные данные (о расширенных данных см. ниже).

Предположим, в рисунке первым объектом является отрезок (тип примитива - 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-кодами. Под соответствующим кодом в точечных парах и списках находятся данные определенного типа:

  • код -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 0 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 <список>) - создает новый примитив по списку, структура которого аналогична структуре списка, возвращаемого функцией entget; функция entmake не может создать примитив типа VIEWPORT; возвращаемое значение - аргумент <список> или nil, если создание объекта невозможно;
  • (entmakex <список>) - создает новый примитив или неграфический объект по списку, аналогично функции entmake; возвращаемое значение - имя нового примитива или nil, если создание объекта невозможно.

Пример:

(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.

Другие функции доступа к примитивам:

  • (handent <метка>) - возвращает имя примитива или неграфического объекта по его метке; аргумент <метка> - это текстовая строка с шестнадцатеричной меткой в том виде, в каком она возвращается функцией entget;
  • (nentsel [запрос] ) - запрашивает объект и для простого примитива возвращает такой же список из имени примитива и точки указания, как и функция entsel. Если указанный пользователем примитив является трехмерной полилинией (POLYLINE), то первым элементом возвращаемого списка будет имя подпримитива начальной вершины (VERTEX) участка, на котором указывалась полилиния. Если указанный пользователем объект является вхождением блока (INSERT), то возвращается список из двух или четырех элементов. Если аргумент запроса не задан, то в качестве подсказки выводится запрос: Select object:
  • (nentselp [запрос] [<точка>]) - аналогична функции nentsel, но если указанным примитивом является вхождение блока (INSERT), то возвращает список, в котором третьим элементом является матрица преобразования размером не 4х3, а 4х4 (см. ниже); если задан аргумент <точка>, то запрос не выдается и аргумент выступает в качестве точки указания.

Функция 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.

Алексей Тимонин
Алексей Тимонин
Алексей Потапкин
Алексей Потапкин

Здравствуйте.

Подскажите, пожалуйста, каким образом можно передать параметры в макрос написанный в Autocad на VBA? Например, есть процедура, которая отрисовывает заштрихованный прямоугольник (см. ниже). Как её изменить, чтобы на входе от пользователя требовалось ввести также в качестве параметров координаты углов прямоугольника?

Public Sub DrawHatchedBox()

...

End Sub