Введение в язык АвтоЛИСП. Лекции

         

Введение в язык АвтоЛИСП. Лекции


ЛЕКЦИЯ 1

ВВЕДЕНИЕ В ЯЗЫК  АВТОЛИСП

 

 

1.1. Назначение и возможности языка Автолисп

Графический язык программирования Автолисп (AutoLisp) является расширением языка программирования Лисп (LISP). Лисп - это язык высокого уровня, ориентированный на обработку списков, который выбран в качестве базового потому, что графические примитивы (начиная с точки), блоки, наборы примитивов и блоков представляются в Автокаде в виде списков.

В составе системы Автокад поставляется интерпретатор языка Автолисп. Он загружается в оперативную память вместе с загрузкой Автокада и доступен в течение всего сеанса редактирования. Таким образом, графический редактор Автокада и интерпретатор Автолиспа представляют собой единую систему.  Любая функция Автолиспа может быть вызвана из графического редактора и любая команда редактора может быть использована в программе на Автолиспе.

Наиболее характерные классы применений Автолиспа:

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

2)   Создание и ведение графических баз данных из приложений, написанных на Автолиспе. Программы на Автолиспе, в сочетании с пользовательскими меню, могут организовывать просмотр, поиск, выбор и вставку необходимых чертежей.

3)   Анализ и (или) автоматическое преобразование графической базы  данных (БД) Автокада.

Программа на Автолиспе может решать такие задачи как:

-    обнаружение пересечений электрических и других магистралей в производстве;

-    подсчет суммарной длины трасс;

-    расчет площадей сложных областей, центра масс и моментов инерции и другие.


Программа также может быстро осуществить преобразование чертежа, на которое при работе "врукопашную" пришлось бы затратить значительное время, например:
*                    заменить блоки чертежа на другие,
*                    перенести выделенные объекты со слоя на слой;


*                    отобрать объекты определенного типа и модифицировать их.
4)   Расширение системы команд графического редактора Автокад и построение на базе Автокад специализированных САПР. Автокад является открытой и развивающейся системой.
В языке Автолисп определены более 150 различных операций, которые называются встроенными функциями.
По назначению их можно разделить на функции:
- для работы с числовыми данными и переменными, реализующие арифметические, логические, а также наиболее часто используемые математические функции;
- для проверки выполнения различных условий и организации ветвлений программ;
- для работы со строками текстов: формирование, сцепление, сравнение строк, выделение подстрок и т.п.;
- для ввода с клавиатуры устройств указания, и вывода на экран и принтер;
- для работы с текстовыми файлами на МД;
- для работы с примитивами, блоками, системными переменными, таблицами в БД.
1.2. Использование программ на языке Автолисп  в системе Автокад
 
Рассмотрим возможные способы использования программ на Автолиспе в зависимости от того, куда помещена разработанная программа.
1. Непосредственный ввод с клавиатуры.
В ответ на приглашение
command:
(команда:)
набираются команды языка и последовательно выполняются.
Однако такой способ неудобен, так как при каждом повторном выполнении программу нужно вновь полностью набирать.


Может использоваться для отладки фрагментов программ.
2. Запись программы в виде текстового файла с расширением .LSP с последующей загрузкой в Автокад. Загрузка файла программы выполняется с помощью команды Автокада LOAD.
Функция имеет вид:
              (LOAD < имя файла >)
Имя файла в команде Load задается в кавычках, но без расширения.
Любая функция загруженной программы вызывается путем указания ее имени и, возможно, параметров функции, заключив их в скобки.
              (myprog "Привет")
3. Оформление программы как готовой команды ACAD.
Программу можно оформить так, чтобы после загрузки файла с этой программой, ее можно было вызывать по имени точно так же, как вызываются команды Автокада (т.е. без скобок).
4. Автоматическая загрузка программ.
Программы на Автолисп будут загружаться автоматически при загрузке Автокада, если их поместить в файл под названием ACAD.LSP.
5. Автоматический запуск программы на Автолиспе.
Можно оформить программу на Автолиспе так, чтобы она автоматически выполнялась после входа в Автокад. Для этого ее нужно включить в файл ACAD.LSP под именем S::STARTUP.
1.3. Основные понятия языка Автолисп
Введем некоторые простые правила описания выражений языка Автолисп и правила использования специальных символов описания.
Символы
              : : =  означают "это есть"
<объект> - означает, что на данное место можно подставить любой объект указанного класса.
[(объект)] - означает, что объект может присутствовать, а может и не присутствовать в данном выражении.
<объект>... - означает, что объектов данного класса может быть несколько.
Программа на языке Автолисп есть множество функций
т.е.
<Программа>: : = <функция>...,
где <функция>: : = (<имя функции> [<аргумент>...] )
Каждая функция имеет свое, связанное с ней имя. В программе используются функции Автолиспа и функции, определенные самим программистом.
1.4. Понятие функций
В Автолиспе используется более 150 встроенных функций


              <функция>: : = (<имя функции> [<аргумент>...])
<имя функции> - это символическое имя, определенное в Автолиспе.
Примеры функций:
(+ А В) - сложение
(prompt  "функция печати")
Аргументы функций - это выражения, построенные по правилам Автолиспа, в том числе и другие вложенные функции. Для каждой функции характерны определенные количество, типы и порядок аргументов. Описание новой функции предшествует обращению к этой функции.
              <описание функции>: : = (DEFUN <имя функции>
              ([<аргумент>...] [/<локальная переменная>...]
              )<выражение>...
              )
Здесь <имя функции> - имя начинающееся с буквы, список аргументов - это список имен, обозначающих данные, которые будут посланы в функцию перед ее выполнением.
Локальные переменные определены только внутри функции и теряют свои значения после выхода из функции.
Выражения показывают, какие действия совершаются в функции. Каждое выражение возвращает значение выражению, в которое оно вложено. Если выражение не является вложенным, Автолисп передает значение Автокаду.
Все выражения и функции в Автолиспе заключаются в скобки. Количество левых скобок в выражении, функции должно быть равно количеству правых скобок. Как аргументы, так и локальные переменные могут отсутствовать, но скобки ( ) после имени функции обязательны.
Напишем нашу первую программу на Автолиспе. Для этого в любом текстовом редакторе создадим текстовый файл такого содержания:
              (defun myprog 1 ( ) ¿
              (prompt "My first program") ¿
              (prompt "-Моя первая рограмма") ¿
              ) ¿
Сохраним файл под именем TEST1.LSP.
Для выполнения программы в Автокаде необходимо ввести:
              команда: (load "Test1")
На экране в зоне командной строки и сообщений появляется сообщение:
              My first program - Моя первая программа.


Поздравляю, вы написали первую программу на Автолиспе! Для того, чтобы перевод был написан на другой строчке, нужно было ввести функцию (terpri) вслед за первым (prompt...).
Помимо функций в программе могут быть комментарии. Комментарий - это любой текст, начинающийся со знака "; ". Комментарий не обрабатывается интерпретатором, а служит для лучшего понимания текста программы.
Например:
              ; это комментарий
              (prompt "печать") ; prompt - функция
              ; вывода строки на экран
1.5. Типы данных, с которыми работает Автолисп
 
Автолисп поддерживает следующие типы данных:
-    символы (переменные);
-    списки;
-    строковые константы;
-    целые числа;
-    вещественные числа;
-    дескрипторы файлов;
-    "имена" примитивов Автокада;
-    наборы выбора Автокада;
-    встроенные и внешние функции.
1.6. Используемые переменные
Для обозначения переменных в Автолиспе используется множество символов латинского алфавита A - Z и цифры 0 - 9. Имя переменной должно начинаться с символа. Регистр символов несущественен,
т.е.      АВС : : = аВс

Правильные имена переменных
Неправильные имена переменных
                A
              37
                arc1
              1point
                point1
              pnt*3
                d3
              a$

Значениями переменных может быть:
nil  -  нет  значения
Целое число                     (Infeger)                  ± 10 ÷ 32768 (2 147 483 647)
                                                                                0 - 65535
Действительное число    (Real)                            1.625;  1.625 · Е2
Строка                              (String)                          (не > 100 символов) - текст константы


                                                                            \n - перевод строки      \t - табуляция
Список                              (List)                         (A  (ABC))
Переменные могут быть локальными и глобальными. Локальные имеют определенные значения внутри функций, в которых они определены, и теряют свои значения при выходе из этих функций. В разных функциях могут быть определены локальные переменные с одинаковыми именами, они могут обозначать разные вещи.
Глобальные переменные определены в пределах всей программы. Их значения могут меняться различными функциями.
Конкретные значения переменные приобретают во время выполнения программы.
<список> : :  = (<элемент>... [<список>]...)
Элементы в списках могут быть разных типов (константы и переменные разных типов).
Простейший тип списка - точка (X, Y) или (X, Y, Z).
Пример списков
(7   9)
(3   5 9 7 2)
(3.2039   6.9029   8.2039)
Более сложными списками в базе данных Автокада представлены примитивы, такие как LINE (ОТРЕЗОК), CIRCLE (ОКРУЖНОСТЬ).
Пример описание примитива LINE в базе данных:
((- 1. <Enfity name: 6000014>) (0. "LINE) (
(8. "LAYER1") (10. 563. 000000 484. 000000)
(11.   1622 . 000000   745 . 000000))
Присвоение типа переменной производится автоматически. Для присваивания используется внутренняя функция Автолиспа  Setq.
(Setq <имя переменной> <значение>...)
Setq присваивает заданное значение переменной с указанным именем и возвращает заданное значение в качестве результата функции. Если функция вызывается из командной строки Автокада, то возвращаемое значение выводится на экран.
Пример:
              (Setq k 3)
              (Setq x 3.875)
              (Setq layname "РАДИАТОРЫ")
Эти выражения присваивают значения целой, вещественной и строковой переменной. Точки задаются более сложным образом, так как они содержат компоненты X, Y и возможно Z. Точки представляют собой списки из 2-х или 3-х чисел, заключенных в круглые скобки:


(3.857   1.23) - двумерная точка
(88.0   16.0   32.0) - трехмерная точка
Для формирования списка можно воспользоваться функцией  list.
              (list   3.857   1.23)
              (Setq pt (list   3.857   1.23))
Непосредственно обращаться к координатам X, Y и Z точки можно с помощью трех встроенных функций car, cadr, caddr.
(car pt) - возвращает координату X точки pt
(cadr pt) - взвращает координату Y
(caddr pt) - возвращает координату Z
(setq pt3 (list (car pt1) (cadr pt2)))
pnt3                                pnt2


pnt1
  Рис. 1.1. Прямоугольник
ЛЕКЦИЯ 9
9.1.Обмен данными Автокада с другими системами
Автокад может использоваться не только сам по себе как самостоятельная полноценная многофункциональная графическая  система, но в сочетании с другими программными системами. Это означает, что с одной стороны, с помощью Автокада можно создавать файлы рисунков, предназначенные для анализа и использования другими системами,  а с другой стороны, использовать, просматривать, изменять или выводить на внешние устройства (плоттеры) рисунки, созданные не средствами Автокада.
Например, средствами Автокада можно получить описание структур для проведения структурного анализа методом конечных  элементов на более мощном компьютере. Вычисленные таким образом внутренние напряжения и смещения могут быть затем использованы в качестве исходных данных для отображения деформированной структуры средствами Автокада.
Используя язык программирования Автолисп или систему разработки приложений на языке C, пользователь может предусмотреть вывод необходимых данных в требуемом для внешних программ и САПР формате.
Схема такого взаимодействия представлена на рис. 9.1.
                                Автокад


                                   Формат
                                        БД
                      БД                     Программный          Требуемый          Внешнее


                Автокада                 интерфейс на                                      приложение
                                                AutoLisp или С            формат            или САПР
              Рис. 9.1. Схема обмена данными
Такая схема выстраивается для передачи некоторого заданного набора данных.
Однако передача всех данных из базы данных чертежа Автокада или их части может происходить по другим схемам (рис. 9.2, 9,3).
                                                  Стандартный формат
                                                        обменных файлов                            Внешнее 
              Автокад                                                                                  приложение
                                                             DXF, IGES                                или САПР
              Рис. 9.2. Экспорт, импорт данных


                                     Выходной            Внешний            Требуемый         Внешнее
       Автокад                 формат           программный         формат            приложение
                                        .dwg                     интерфейс                                   или САПР
                                        .dxf
                                        .dxb
              Рис. 9.3. Обмен через внешний программный интерфейс
База данных рисунка Автокада (файл с расширением .dwg) хранится в очень сжатом формате, который подвергается значительным изменениям по мере добавления в Автокад новых функциональных возможностей. Документация на этот формат не публикуется.
Для обеспечения совместимости и обмена рисунками между Автокадом и другими пакетами программ создан формат файла обмена рисунками (DXF). Данный формат воспринимается всеми версиями Автокада, и, кроме того, он может быть преобразован во внутреннее представление файла  рисунка (и наоборот).


Автокад поддерживает также формат стандартного графического обмена (Initial Graphics Exchange  Specification - IGES).
Рисунки, созданные в Автокаде, могут выводиться в виде файлов в формате IGES, а последние в свою очередь могут считываться и преобразовываться во внутренний формат Автокада.
В этой лекции мы рассмотрим основы формата обмена рисунками Автокада DXF, а также команды, предназначенные для считывания и записи этих файлов.
DXF-файлы являются обычными текстовыми файлами в коде ASCII. Они могут легко преобразовываться в форматы других САПР или передаваться в другие программы для специальной обработки. Автокад также способен генерировать и считывать DXF-файлы в двоичном формате.
Для вывода DXF-файла служит команда DXFOUT (ЭКСПОРТА). Для ввода DXF-файла в Автокад служит команда DXFIN (ИМПОРТА).
Команда DXFOUT (ЭКСПОРТА) запрашивает имя файла. По умолчанию именем файла является имя текущего рисунка, но с типом файла .dxf. После ввода имени файла следует запрос:
Введите число знаков после запятой (от 0 до 16) / Объекты / Двоичный <6>:
Если в качестве ответа ввести Объекты (или просто 0), то команда выдает запрос на выбор объектов, которые требуется записать в DXF-файл. В файл вывода будут включены только объекты, которые выбраны из исходного файла. Как только нужные объекты выбраны, Автокад повторно выдает запрос о точности.
9.2.Формат DXF-файла. Общая структура файла
DFX-файл организован следующим образом:
-    раздел HEADER (заголовок) - содержит общую информацию о рисунке. Каждый параметр имеет имя и  соответствующее значение.
-    раздел TABLES (таблицы) содержит определения поименованных элементов.
*      таблица типов линий (LTYPE);
*      таблица слоев (LBYER);
*      таблица гарнитур (STYLE);
*      таблица видов (VIEW);
*      таблица пользовательских систем координат (UCS);


*      таблица конфигураций видовых экранов;
*      таблица размерных стилей (DIMSTYLE).
-    раздел BLOCKS (блоки). Содержит графические примитивы рисунка, включая любые вхождения блоков.
-    раздел ENTITIES (примитивы). Содержит графические примитивы рисунка, включая любые вхождения блоков.
-    END OF FILE (конец файла).
Если используется опция "объекты" команды DXFOUT (ЭКСПОРТА), то полученный DXF-файл содержит только раздел ENTITIES и END OF FILE.
DXF-файл состоит из множества групп, каждая из которых занимает две строки. В первой строке  размещается код группы. Вторая строка содержит значение группы, формат которой зависит от типа группы, задаваемого ее кодом.
ЛЕКЦИЯ 2
2.1. Функции доступа к командам Автокада
Любая команда Автокада может быть включена в программу на Автолиспе, что позволяет строить изображение из программы автоматически или с участием пользователя. Это делается с помощью функции command, имеющей вид:
(command"<имя команды>" [<аргумент>...])
где <имя команды> - имя требуемой команды, определенной в данной версии Автокад. Если командой предусмотрены аргументы, то они приводятся. Аргументы перечисляются в той последовательности, в которой вы задавали бы их, формируя команду для Автокада в режиме диалога. Аргументами являются точки, опции, числа, строки символов и др.
Пустая строка (" ") эквивалентна вводу пробела или нажатию клавиши Enter.
Примеры:
              (command "circle" "0, 0" "3, 3")
              (setq p1 '(1.0 1.0 3.0))
              (setq rad 4.5)
                        (command "circle" p1 rad)
Ввод (command) равносилен нажатию клавиши Enter.
Ключевое слово pause в качестве аргумента обозначает переключение в режим графического диалога и ожидание аргумента с клавиатуры или от мыши.
Пример:
              (command "circle" pause "5,5")


- определяет круг с центром, заданным пользователем и проходящий через точку с координатами (5, 5).
              (command "line" "0, 0" "100, 100"
               "200, 100" "200, 0" "с""") - рисует прямоугольник с координатами точек (0, 0), (100, 100), (200, 100), (0, 0).
Передача точек указания командам Автокада:
Некоторые команды Автокада (такие как ОБРЕЖЬ [TRIM], УДЛИНИ [EXTEND], СОПРЯГИ [FILLET]) требуют от пользователя указать не только примитив, но и точку на нем. Для того чтобы передать точку в функцию "command", не используя PAUSE, следует сначала получить эти значения и записать их в переменные, которые можно передать в функцию с command.
Пример:
              (command "circle" "5, 5" "2")                                                                  (5, 7)
                        (command "line" "4, 6" "6, 6" " ")
              (setq et (entlast))                                                                        (4, 6)                        (6, 6)
              (setq pt '(5, 7))                                                                       X                          R2            
              (command "TRIM" et " " pt " ")                                                             5, 5                   
- обрезается верхняя часть окружности.                                                                                   
                                                                                                                                           
                                                                                                                                                          Y
                                                                                                            Рис. 2.1.


Окружность
2.2. Арифметические, математические и тригонометрические функции
2.2.1. Арифметические функции (сложение, вычитание, умножение, деление)
(+ <число> <число> ...) - возвращает сумму всех чисел. Может использоваться как с целыми, так и с действительными числами. Если все числа целые, результат будет целым; если хотя бы одно число действительно, результат будет действительным. Возвращается сумма всех чисел.
 
Примеры:
              (setq a (+ 2 5) - возвращает 7
              (setq  a (+ 1 2 3 4.0) - возвращает 10.0
(+ а 5) не изменяет значения а. Если а было равно 3, возвращает 8. Чтобы изменить а, нужно ввести: (setq a (+ а 5). Возвращает 8.
(- <число> [<число>]...)
Данная функция вычитает второе число из первого и возвращает разность. Если задано более двух чисел, то из первого вычитается сумма остальных. Если задано только одно число, оно вычитается из 0 и возвращается разность.
Может использоваться с целыми и действительными числами.
Примеры:
              (- 30 20) - возвращает 10
              (- 50 40.2) - возвращает 8.8
              (- 8) - возвращает -8
Функции умножения и деления имеют аналогичный вид:
(* <число> [<число>]...)
(/ <число> [<число>]...)
Функция умножения возвращает произведение всех чисел.
Функция деления делит первое число на второе и возвращает частное. Если задано более двух чисел, то первое число делится на произведение остальных и возвращается частное.
Примеры:
              (* 2 3) - возвращает 6
              (*  2 3 4.0) - возвращает 24.0
              (* 3) - возвращает 3
              (* 3 -4) - возвращает  -12
              (/ 100 2) - возвращает 50
              (/ 100 2.0) - возвращает 50.0
              (/ 100 20 2.0) - возвращает 2.5
              (/ 135  360) - возвращает 0
Существует еще ряд встроенных арифметических функций, например: увеличения и уменьшения числа на 1 (1 + <число>), (1 - <число>), нахождения минимума и максимума из заданных чисел (min <число> <число>...), (max <число> <число>...), нахождения абсолютного значения числа (abs <число>).


Функция без скобок pi возвращает значение константы pi=3.1415526.
2.2.2. Математические и тригонометрические функции
Среди математических функций наиболее применяемые следующие:
(sqrt <число>) - извлекает корень квадратный из числа. Результат является вещественным числом.
Функции (fix <число>), (float <число>) преобразуют число к целому и к вещественному представлению.
Среди множества функций есть тригонометрические:
(sin <угол>),
(cos <угол>)
Угол здесь должен выражаться в радианах. Как задать угол в радианах?
Определим функции для преобразования градусов в радианы dtr и  радианов в градусы rtd.
; функция преобразования градусов в радианы
(defun dtr (a)
  (* pi (/ a 180.0))
  )
; функция преобразования радианов в градусы
(defun rtd (a)
  (/ (* a 180.0) pi)
)
В дальнейшем мы будем использовать эти функции.
Пример:
              (setq a 30)
              (setq S (sin (dtr a)))
              ! S
              0.5 - возвращает значение sin (30°)
Для случая частного использования функций (dtr) и (rtd) следует поместить их описание в файл acad.lsp, который загружается Автокадом автоматически.
Другие тригонометрические и математические функции:
(atan <число> [<число>]) - вычисляет арктангенс числа, результат получается в радианах. Если задано второе <число>, вычисляется арктангенс <число1>/<число2>;
(exp <число>) - вычисляет Е в степени <число>;
(expt <число> <степень>) - возвращает <число>, возведенное в степень;
(log <число>) - вычисляет натуральный логарифм <числа>.
2.3. Ввод данных с клавиатуры
Функция (getreal) вводит действительное число с клавиатуры. Описание функции: (getreal [<подсказка, сообщение>]).
Пример:
              (setq a (getreal "Введите число:") ¿
Сообщение:
              Введите число: 75.0 ¿
              ! а ¿
              75.0                 Возвращает введенное число.


Аналогично работает функция (getint [<подсказка>]), которая вводит целое число и возвращает его.
Функция (getpoint [<точка>] [<подсказка>] позволяет ввести точку и возвращает ее как список. При наличии аргумента <точка> Автокад рисует "резиновую линию" и ждет ввода второй точки.
Функция (getdist [<точка>] [<подсказка>]) обеспечивает ввод расстояния. Для задания расстояния можно ввести число с клавиатуры или указать две точки на экране. Автокад рисует "резиновую линию" от первой точки до текущего положения курсора для того, чтобы визуализировать расстояние. Расстояние возвращается как действительное число.
Пример:
              (setq a (getdist "Укажите расстояние")¿
Следует указать точку. Далее Автокад пригласит Вас к вводу второй точки. Расстояние между точками выдается в текущих единицах, установленных командой UNITS [ЕДИНИЦЫ].
Первую точку для (getdist) можно задать программно.
Например:
              (setq pnt1 (getpoint "Укажите точку"))
              (setq a (getdist pnt1 "Введите высоту:  "))
В данном случае первая точка pnt1 уже указана, необходимо тянуть "резиновую нить" до второй точки для задания высоты. При задании второй точки pnt2, расстояние между pnt1, pnt2 будет определено, как действительное число и будет назначено в переменную a.
Функция (getstring [флаг-пробела] [подсказка]) - запрашивает у пользователя строковую константу и возвращает ее. Если строка длиннее 132  символа, возвращается только первые 132 символа.
Аргумент флаг-пробела допускает наличие пробелов в вводимой строке, если он не равен nil.
Примеры:
              (setq s (getstring "Введите Ваше имя:  "))
После ввода пробела за словом "Василий _", ввод будет прерван и функция возвратит "Иван".
              (setq s (getstring T "Введите имя и фамилию:"))
После ввода "Василий _
Пупкин" и нажатия клавиши "Enter", строка будет введена и назначена в s.


Далее, используя ранее рассмотренные функции, напишем программу построения прямоугольника.
Текст программы
(defun rectan (/ pnt1 pnt2 pnt3 pnt4)
  (setq pnt1 (getpoint "Первый угол прямоугольника:  "))
  (terpri)
  (setq pnt2 (getpoint "Второй угол прямоугольника:  "))
  (terpri)
  (setq pnt3 (list (car pnt1) (cadr pnt2)))
  (setq pnt4 (list (car pnt2) (cadr pnt1)))
  (command "line" pnt1 pnt3 pnt2 pnt4 "")
)
Сохраним программу в файле ТEST2.LSP.
После загрузки файла: (load "TEST2") ¿ и вызова функции (rectan) строится прямоугольник по двум заданным пользователем угловым точкам (pnt1, pnt3). Две другие точки определяются автоматически.
pnt3                                             pnt2
 
pnt1                                             pnt4
 
 Рис. 2.2. Прямоугольник
Перед построением прямоугольника есть смысл перевести дисплей в графический режим (в случае одноэкранной конфигурации Автокада). Это делается с помощью команды (graphscr). Обратное переключение делается с помощью функции (textscr).
Написанная нами функция (rectan) не позволяет при указании второй точки наблюдать на экране "резиновые линии" прямоугольника. Этот недостаток можно исправить, если вместо (getpoint) использовать функцию (getcorner) при вводе второй угловой точки.
Внесем изменения в программу:
(defun rectan (/ pnt1 pnt2 pnt3 pnt4)
(graphscr)
(setq pnt1 (getpoint "Первый угол прямоугольника:  "))
(terpri)
(setq pnt2 (getcorner pnt1 "Второй угол прямоугольника:  "))
(terpri)
(setq pnt3 (list (car pnt1) (cadr pnt2)))
(setq pnt4 (list (car pnt2) (cadr pnt1)))
(command "line" pnt1 pnt3 pnt2 pnt4  " ")
)
После ввода первой угловой точки и перемещении курсора, мы наблюдаем "резиновые линии" прямоугольника, второй диагональный угол которого связан с курсором. Программы вычерчивает прямоугольник после указания второй точки.


                                                        pnt2
 
             pnt1
                                                                                             
            Рис. 2.3. Отрисовка прямоугольника
ЛЕКЦИЯ 3
ОРГАНИЗАЦИЯ ИНТЕРАКТИВНОЙ РАБОТЫ. ИЗМЕРЕНИЕ  И ПРИВЯЗКА.
 УПРАВЛЕНИЕ ПРОГРАММОЙ
3.1. Функции организации интерактивной работы (продолжение)
Часть функций организации интерактивной работы было рассмотрено в предыдущей лекции. Среди них, функции ввода данных (getint), (getreal), (getpoint), (getdist), (getkorner), функции переключения экрана из текстового в графический режим (graphscr) и, наоборот, (textskr), функции вывода (terpri) и (prompt).
Кроме ввода точек, целых, действительных чисел и строк в Автокаде часто необходим ввод или указание углов.
Функция (getangle [<точка>] [<сообщение>]) обеспечивает ввод углов, как с клавиатуры,  так и с помощью устройств указания. Возвращает угол в радианах. Можно также показать угол путем указания двух точек на экране. Угол измеряется против часовой стрелки. Автокад рисует "резиновую линию от первой точки к текущему положению курсора, для того, чтобы показать угол. Аргумент [<точка>], если он существует, определяет первую точку, от которой измеряется угол. (getangle) измеряет углы от направления в нуль радиан, установленного системной переменной ANGBASE.
Функция (getorient [<точка>] [<сообщение>]) определяет значение угла, не зависящее от системных переменных Автокада ANGBASE и ANGDIR. Угол отсчитывается от горизонтального направления, против часовой стрелки.
Пример:
              (setq a (getorient "Введите угол:  "))
P2  .                                       angdir      
               .  Р1                                                                                      a
                         a


                                                             X                                                  Р1                          X
 
                          a)                                                                               б)           Р2
Рис. 3.1. Различный порядок ввода точек P1 и P2                                           
Как мы видим на рис. 3.1. а, б  введенный угол будет зависеть от порядка ввода точек.
 
3.2. Функции измерения и привязки
Функции измерения и привязки используются для определения расстояний, углов, точек по известным точкам.
Функция (distance <точка1> <точка2>) определяет расстояние между двумя точками (трехмерными). Если одна или обе вводимые точки являются двумерными, то координаты Z точек игнорируются и определяется расстояние между точками, спроецированное на текущую плоскость построений.
Пример:
              (distance  ¢(100, 100) ¢(200, 100))
              возвращает 100
Функция (angle <точка1> <точка2>) определяет угол в радианах между двумя лучами, направленными из 2-мерной точки <точка1>. Первый луч направлен в положительную сторону оси Х, второй - в сторону  <точки2>. Угол измеряется против часовой стрелки (в радианах). Если точки - трехмерные, то они проецируются на текущую плоскость построений.
Пример:
              (setq pnt1 (getpoint "\ n Точка 1:  "))
              (setq pnt2 (getpoint "\ n Точка 2:  "))
              (setq a (angle pnt1 pnt2))
              (setq a1 (angle pnt2 pnt1))
(setq a (rtd a))
(setq a1 (rtd a1))
Возвращаемые значения а и а1 будут различными, так как  поменялся порядок ввода точек.
Следующая функция (polar <точка> <угол> <расстояние>) определяет точку по углу и расстоянию от другой <точки>. <Угол> должен быть выражен в радианах.
Пример:
              (setq pnt2 (polar pnt1 a dist))
                                            .  pnt2                  


                                 dist                                     
                                      a                                    
                          .                                               X
                       pnt1
                                   Рис. 3.2. Определение точки с помощью функции polar
Рассмотрим, как использовать (polar) для построения параллельных линий.
Возьмем значения pnt1 и pnt2 из предыдущего примера. Соединим их отрезком:
              (command "line" pnt1  pnt2)
Измерим угол по двум точкам:
              (setq ang1 (angle pnt1 pnt2))
Определим первую (начальную) точку параллельной линии на заданном расстоянии от исходной:
              (setq d (getreal "Введите смещение")
              (setq pnt3 (polar pnt1 (+ ang1 (dtr 90) d))
Определим вторую (конечную) точку параллельной линии:
              (setq pnt4 (polar pnt2 (+ ang 1 (dtr 90)) d))
Нарисуем параллельную линию:
              (command "line" pnt3 pnt4 "")
Если угол изменить на -90, то параллельная линия будет с другой стороны от исходной.
                                                         .  pnt4
               pnt3
                .                                                            
                                                             .  pnt2         
         d            90                                                     
                    .                                                           
                pnt1                                        .         
                                                                              
                      .                           
Рис. 3.3. Рисование параллельной линии            
Следующая функция (inters <точка1> <точка2> <точка3> <точка4> [<в пределах>]) проверяет два отрезка на пересечение и возвращает точку пересечения или nil, если они не пересекаются.


Здесь <точка1> <точка2> - крайние точки первого отрезка, <точка3> <точка4> - крайние точки второго отрезка.
Все точки выражаются в координатах текущей ПСК. Если все четыре точки - трехмерные, то (inters) контролирует пересечение в трехмерном пространстве. В противном случае (inters) проецирует отрезки на текущую плоскость построений и проверяет пересечение только на плоскости.
Аргумент <в пределах> является необязательным. Если он существует и равен nil, то отрезки будут восприниматься, как бесконечные и inters будет возвращать точку их пересечения, даже если она находится за пределами крайних точек одного или обоих отрезков. Если аргумент <в пределах> отсутствует или не равен nil, то точка пересечения должна находиться в пределах обоих отрезков, или  (inters) возвратит nil.
Рассмотрим еще одну функцию с целью ее использования в последующем примере.
Функция (osnap <точка> <режим>) возвращает точку, которая является результатом применения объектной привязки, задаваемой в <режиме> для точки <точка>. Аргумент <режим> - строковая константа, состоящая из одного или более идентификаторов объектной привязки, для англоязычной версии - на английском языке, для русскоязычной - на русском.
Функция возвращает 2-х или трехмерную точку, в зависимости от того 2, или 3-мерной является <точка>. Если не найдено ни одной точки, соответствующей заданному <режиму> объектной привязки, то возвращается nil.
Пример:
Иллюстрация использования функций (getorient), (polar), (inters) и других, ранее рассмотренных. Программа строит отрезок  в указанном направлении (направление и длина задается указанием двух точек), затем строится отрезок в другом направлении (длина и угол задаются с клавиатуры).
              (defun angelin (/ pnt1 pnt2 pnt3_a_a1_d)
              (graphscr)
              (setq pnt1 (getpoint "Введите начальную точку")) (terpri)
              (setq a (getorient "Укажите направление"))


              (setq d (getdist "\n Задайте длину отрезка"))
              (terpri)
              (setq pnt2 (polar pnt1 a d))
              (command "line" pnt1 pnt2"")
              (setq d (getreal "Введите длину с клавиатуры"))
              (terpri)
              (setq a1 (getreal "Введите угол"))
              (terpri)
              (setq a (dtr a1))
              (setq pnt3 (polar pnt2 a d))
              (command "line" pnt2 pnt3 " ")
              )
                                                                                                                                            
         .   pnt1                       pnt3                                                                                         
                   d       d     a1                                                                                                       
            pnt2   х                                                                         d                                        
                                         a                                           х                       х                                                     
                                     х                                        d- задает длину (например по существующему
                                     pnt                                           отрезку)
           
а - содержит угол, задающий направление прямой (в радианах).
Рис. 3.4. Построение отрезков
 
3.3. Функции проверки условий и сравнения
Используются в функциях управления порядком вычислений.
(= < элемент > <элемент>...) - сравнение на равенство. Если все элементы равны, то возвращается Т (True), если хотя бы один отличается от других, возвращается nil. В качестве аргументов могут использоваться числа и строковые константы.
              (= 4  4.0) ® Т
              (= 20 30) ® nil


              (= "Я" "Я") ® Т
(< <элемент> <элемент>...) - сравнение на "меньше чем". Если каждый последующий <элемент> меньше предыдущего, возвращается Т, иначе nil.
              (< 3 2 1) ®
T
              (< 2 3 4) ®
nil
Аналогично определяются функции (<= <элемент> <элемент>...), (>= <элемент> <элемент>...),
(> <элемент> <элемент>...).
Функция (eq <выражение1> <выражение2>) определяет идентичность двух выражений, т.е. относятся ли они к одному объекту, определенному, например,  с помощью setq. Если оба выражения идентичны, возвращается Т, в противном случае - nil. Обычно функция применяется для определения идентичности двух списков.
Пример:
Пусть    (setq f1 ¢(a b c))
              (setq f2 ¢(a b c))
              (setq f3 f2)
тогда     (eq f1 f3)        возвращает nil
              (eq f2 f3)        возвращает Т
Функция (equal <выражение1> <выражение2> [<точность>]) определяет, равны ли <выражение1> и <выражение2>, т.е. равны ли их значения. Необязательный параметр <точность> задает точность сравнения действительных чисел.
Предыдущий пример для функции (equal):
Пусть    (setq f1 ¢(a b c))
              (setq f2 ¢(a b c))
              (setq f3 f2)
Тогда     (equal f1 f3)    возвращает Т
              (equal f3 f2)    возвращает Т
Два списка могут быть равны по (equal), но не идентичны по (eq).
 
3.4. Функции управления порядком выполнения программы
Для управления порядком выполнения программы, в Автолиспе есть ряд функций, похожих на те, что используются в языках программирования высокого уровня (типа if, w hill) и другие, специфические по своей форме.
Начнем с традиционных.
(if <тест-выражение> <выражение-тогда> [<выражение-иначе>])
Функция выполняет выражение по условию. Если <тест-выражение> не nil, то выполняется <выражение-тогда>, в противном случае используется <выражение-иначе>, причем последнее не обязательно.


Пример:
              (if (= 1 3) "Да!!"  "Нет")  -  возвращает "Нет"
              (if (= 2 (+ 1 1) "Да!!"  "Нет")  -  возвращает "Да!!"
ЛЕКЦИЯ 4
 
УПРАВЛЕНИЕ ПОРЯДКОМ ВЫЧИСЛЕНИЙ.  ФУНКЦИИ РАБОТЫ СО СПИСКАМИ.
                                      ФУНКЦИИ ОБРАБОТКИ СТРОК
4.1. Функции управления порядком вычислений
В рассмотренной в предыдущей лекции функции (if <тест-выражение> <выражение - тогда> [<выражение-иначе>]) не предполагается выполнение нескольких <выражений-тогда> и <выражений-иначе>. При необходимости выполнения нескольких выражений, там, где предусмотрено только одно, можно воспользоваться функцией (progn <выражение>...), которая последовательно вычисляет каждое <выражение> и возвращает значение последнего выражения.
Например:
(if (= a b)
     (progn
     (setq с "Выражение 1")
     (princ c)
     (setq d "Выражение 2")
     (princ d)
     )
)
Другой пример:
(if (= a b)
     (progn
              (setq a (+ a 10))
              (setq b (-  b 10))
               )
     (progn
                        (setq a (- a 10)
                        (setq b (+ b 10)
     )
)
Функция (while <тест-выражение> <выражение>...) вычисляет <тест-выражение> и, если оно не является nil, вычисляет другие выражения, затем снова проверяет <тест-выражение>. Это продолжается до тех пор, пока <тест-выражение> не станет равно nil. Функция while возвращает значение последнего <выражения>.
В качестве примера рассмотрим функцию рисования n-параллельных линий:
(defun npar (/ pnt1 pnt2 pnt3 pnt4)
(setq nl (gefint "Число линий:  "))
(setq pnt1 (getpoint "Введите первую точку:  "))
(setq pnt2 (getpoint "Введите вторую точку:  "))
(setq a (angle pnt1 pnt2))
(command "line" pnt1 pnt2 "  ")
(setq n 1)
(setq d (getreal "Введите смещение"))


(while (< n nl)
(setq pnt3 (polar pnt1 (+ a (dtr 90) d))
(setq pnt4 (polar pnt2 (+ a (dtr 90) d))
(command "line" pnt3 pnt4 "  ")
(setq n (+ 1 n))
(setq pnt1 pnt3 pnt2 pnt4)
)

)
Функция npar запрашивает количество параллельных линий, точки для задания направления и длины линий, расстояние между двумя линиями и отрисовывает заданное число параллельных отрезков одинаковой длины.
Функция (cond (<тест> <выражение-результат>...)...) воспринимает любое число подсписков <тест> <выражение-результат> в качестве аргументов. Функция вычисляет по очереди первый элемент <тест> каждого подсписка до тех пор, пока не встретится элемент, отличный от nil. Для него вычисляется <выражение-результат> и возвращается вычисленное значение.
Пример:
(setq s (getstring "Быть или не быть?"))
(cond ((= s "Д") 1)
          ((= s "д") 1)
          ((= s "Н") 0)
          ((= s "н") 0)
          (t nil)
)
В данном примере, производится проверка ответа пользователя. Если следует ответ "Д" или "д", возвращается 1, если другой ответ, то 0 или nil.
Функция (repeat <число> <выражение>...) выполняет каждое <выражение> заданное <число> раз и возвращает значение последнего выражения. <Число> представляется любой положительной целой величиной.
Пример:
              (setq a 100
                                  b 150)
              (repeat 3
                         (setq a (+ a 100))
                         (setq b (+ b 100))
              )
a! ® 400
b! ® 450
Пример использования функций управления ходом вычислений:
Программа рисования решетки заданных размеров.
(defun c :  crsgrid (/ h h1 n pnt c p1 p2 t pnte pntv)
(graphscr)
(setq h (getdist "Расстояние между линиями"))
(terpri)
(setq h1 (getdist "Размер по горизонтали"))
(terpri)
(setq pnt (getpoint "Начальная точка"))


(terpri)
(setq c 0)
(setq n (getint "Число линий"))
; вычерчивание горизонтальных отрезков
(repeat n
              (setq p1 (list (car pnt) (+ (cadr pnt) c)))
              (setq p2 (list (+ (car pnt) h1) (+ (cadr pnt) c)))
              (command "line" p1 p2  " ")
              (setq c (+ c h))
)
(setq pntv p1)
(setq pnte p2)
(setq t 1
(setq c 0)
; вычерчивание вертикальных отрезков
(while t
              (setq p1 (list (+ (car pnt) c) (cadr pnt)))
              (setq p2 (list (+ (car pntv) c) (cadr pntv)))
              (command "line" p1 p2  " ")
              (setq c (+ c h))
              (if (or
                          (= (car p1) (car pnte))
                          (> (car p1) (car pnte))
                          )
                          (setq t nil)
              )
  )
)
Вызов функции: crsgrid.
Программа отрисовывает решетку (рис. 4.1)


Рис. 4.1. Решетка
4.2. Функции для работы со списками
Следующая функция, специфическая для Лиспа и Автолиспа, циклически вычисляет выражение, подставляя в качестве аргумента элементы списка.
(foreach <имя> <список> <выражение>...) - присваивает, проходя по <списку>, его элементы каждому элементу <имя> и вычисляет каждое <выражение> для каждого элемента в списке.
Может быть задано любое число <выражений>. Функция возвращает результат последнего вычисленного <выражения>.
Пример: Распечатка женских имен.
              (setq Names (list "Мария" "Ольга" "Наташа"))
              (foreach name names (print name))
Здесь функция foreach эквивалентна
              (print "Мария")
              (print "Ольга")
              (print "Наташа")
Список имен может быть расширен.
Функция (mapcar <функция> <список1>... <список n>) возвращает результат выполнения <функции> над отдельными элементами от <cписка1> до <списка n>, вводимыми как аргументы в функцию.


Число <списков> должно соответствовать числу аргументов, требующихся для <функции>.
Пример:
              (setq a 10  b 20  c 30)
              (mapcar ’1+ (list a b c))
возвращает (11 21 31)
Другой пример:
                        (mapcar ’+  ’(10 20 30) ’(4 3 2))
возвращает (14 23 32)
Функция (apply <функция> <список>) обеспечивает выполнение <функции> над заданными аргументами. Работает как с внутренними функциями, так и с функциями, определенными пользователем.
Пример:
              (apply ’+ ’(1 2 3))  возвращает 6.
Ранее были рассмотрены функции для работы с элементами списков (car), (cdr), (list). В дополнение к ним рассмотрим следующие функции:
(cons <новый первый элемент> <список>) - добавляет <новый первый элемент> в начало списка. При этом <новый первый элемент> может быть атомом или списком.
Пример:
              (cons ’a  ’(b c d))
возвращает (a b c d)
Для сохранения нового списка нужно использовать оператор присваивания (setq).
(setq names (cons "Алена" names))  возвращает  ("Алена" "Мария" "Ольга" "Наташа")
Функция (append <список>...) берет любое число списков и сливает их в один список.
Пример:
              (append  ’(a b c)  ’(d e))
возвращает (a b c d e)
                        (append  ’((a b c))  ’(d e))
возвращает (( a b c) d e)
Функция (length <список>) возвращает целое число, равное числу элементов в <списке>.
Функция (reverse <список>) возвращает <список> с элементами, расставленными в обратном порядке.
Пример:
(reverse  ’(1 2 3 4 5))  возвращает (5 4 3 2 1)
Функция (last <список>) возвращает последний элемент <списка>. Список не должен быть равен nil.
Пример:
(last  ’(1 2 3 4 5)) возвращает 5.
Функция (nth <n> <список>) возвращает n-й элемент <списка>, где <n>-номер элемента (нуль - первый элемент).


Если n больше, чем номер последнего элемента <cписка>, возвращается nil.
Примеры:
              (nth 3  ’(a b c d e))  возвращает d.
              (nth 0  ’(a b c ) возвращает a.
Функция (assoc <элемент списка> <структурированный список>) просматривает <структурированный список> по ключу <элемент списка> и возвращает первое вхождение <элемента> <структурированного списка>. Если <элемент списка> не найден, возвращается nil.
Пример:
Пусть имеется структурированный список ls:
((Sname "Иванов") (dep 18) (tel. 31853) (proect "Rocet"))
Тогда (assoc ’Sname ls) возвратит (Sname "Ivanov")
           (assoc ’dep ls)       возвратит 18.
Удачный способ замены величины, найденной по ключу в структурированном списке, обеспечивается функцией (subst <новый элемент> <старый элемент> <список>).
Функция осуществляет поиск в списке <старых элементов> и возвращает копию <списка> с заменой каждого встреченного <старого элемента> на <новый элемент>. Если в <списке> не найден <старый элемент>, функция возвращает <список> неизменным.
Пример:
Пусть (setq sample  ’(a b (c d) b))
тогда   (subst  ’c  ’b sample)
возвратит (a c (c d) c)
В сочетании с функцией assoc, функция subst обеспечивает удобный способ замены величины, найденной по ключу, в структурированном списке.
Пример:
Возьмем знакомый список ls:
((Sname "Иванов") (dep 18) (tel 31853) (proect "rocet"))
Пусть
(setq old (assoc  ’sname ls) ® (sname "Иванов")
(setq new ’(sname "Smirnoff"))
Тогда (subst new old ls) возвращает:
((sname "Smirnoff") (dep 18) (tel 31853) (proect "rocet"))
Функция (member <выражение> <список>) осуществляет поиск в <списке> элемента <выражение> и возвращает часть <списка>, начинающуюся с первого найденного <выражения>. Если в списке нет <выражения>, возвращается nil.


Пример:
(member ’(tel 31853) ls)  возвращает (tel 31853)(proect "rocet")
 
4.3. Функции работы со строками
Обеспечивают работу со строковыми переменными, выполняя их различные преобразования.
Функция (strcase <строка> [<признак>]) возвращает копию<строки>, переведя все символы алфавита в верхний или нижний регистр в зависимости от аргумента <признак>. Если <признак> опущен или равен nil, то все символы в <строке> будут переведены в верхний регистр. Если <признак> присутствует и не nil, все символы в строке будут переведены в нижний регистр.
Пример:
(strcase  "Пример")  возвращает "ПРИМЕР"
(strcase "АБРАКАДАБРА" Т)  возвращает "абракадабра"
Функция (strcat <строка1> [<строка2>]...) возвращает строку, которая является результатом сцепления <строки1>, <строки2> и т.д.
Пример:
                        (setq S1 "АВТОКАД
_")
              (setq S2  "12")
              (setq S3 "2000")
(setq S4 (strcat S1 S2))  возвращает "АВТОКАД 12"
(setq S5 (strcat S1 S3))  возвращает "АВТОКАД 2000"
Функция (strlen [<строка>]...) возвращает длину строковых констант (в символах), как целую величину.
Пример:
              (strlen "скажи-ка - дядя...") возвращает 16.
Функция (substr <строка> <начало> [<длина>]) возвращает подстроку <строки>, начинающуюся с символа, номер которого указан в аргументе <начало> и содержащую число символов, заданное в аргументе <длина>.
Если длина не указана, то подстрока продолжается до конца <строки>. Первый символ строки имеет номер 1 (у списка первый элемент имеет номер 0).
Пример:
                        (substr "international" 6)  возвращает
"national".
ЛЕКЦИЯ 5
5.1. Функции работы с файлами
Функция (findfile <имя-файла>) позволяет отыскивать файл, указанный в <имя файла>, в каталогах, хранящих файлы Автокада, и возвращает полное имя файла  или nil, если файл не найден.


Каталоги просматриваются в следующем порядке:
             Текущий каталог.
             Каталог, в котором хранится текущий рисунок.
             Каталоги, имена которых заданы в переменной среды ACAD (если заданы).
             Каталог, в котором хранятся программные файлы Автокада.
Если <имя-файла> содержит имя дисковода и каталогов (путь доступа), Автокад просматривает только указанный каталог.
Пусть текущий каталог c:/acad и в нем хранится файл abc.lsp. Тогда (findfile "abc.lsp") возвращает "/acad/abc.lsp".
Возвращаемое значение можно передавать функции (open <имя-файла> <режим>). Данная функция открывает файл для доступа функций Ввода/Вывода Автолиспа. Функция возвращает описатель файла для использования другими функциями ввода - вывода, поэтому ее результат должен присваиваться символу с помощью функции setq.
Здесь <имя файла> - строковая константа, указывающая имя и расширение открываемого файла. <режим> - флаг чтения/записи (строковая константа, состоящая из одной буквы, набранной на нижнем регистре).
Допустимые обозначения режима:
"r"   - открыть для чтения;
"w" - открыть для записи;
"a"  - открыть для добавления.
Пример:
              (setq a (open "abc.lsp" "r")
Для закрытия файлов, после работы с ними используется функция (close <описатель файла>).
Функция закрывает файл и возвращает nil. Здесь <описатель файла> возвращается в программу функцией (open). После выполнения функции <описатель файла> остается неизменным, но файл становится недоступным.
Рассмотрим далее функции для чтения/записи данных при работе с файлами.
Функция (read-char [<описатель файла>]) считывает единичный символ из буфера ввода клавиатуры или из открытого файла, задаваемого <описателем файла>.


Возвращает ASCII- код считываемого символа. Если не задан <описатель файла> и в буфере ввода клавиатуры нет символов, (read-char) ждет от пользователя ввода с клавиатуры (заканчивающегося нажатием клавиши ввода RETURN).
Пример:
Пусть в буфере ввода находятся символы A B C. Тогда после выполнения следующей программы:
(setq  s 1
(while (/=s 10)
              (princ (read-char))
              (terpri)
)
на экран будет выведено:
65
66
67
10
Это ASCII-коды символов A B C.
Код 10 соответствует переходу на новую строку.
Функция (read-line [<описатель файла>]) считывает и возвращает строку символов с клавиатуры или из открытого файла, заданного <описателем файла>. Если чтение невозможно, то возвращается nil.
Пример:
Пусть в файле mytext.txt содержится строка "Я вам пишу...".
Тогда
              (setq f (open "mytext.txt> "r"))
задает f как <описатель файла>.
              (read-line f)
возвращает "Я вам пишу...".
Функция (write-char <число> [<описатель файла>]) записывает один символ на экран или в открытый файл, заданный <описателем файла>. <число> - это ASCII-код символа, и является значением возвращаемым функцией.
Пример:
              (write-char 65)
записывает в буфер вывода (выводит) латинскую букву А на экран.
Функция (write-line <строка> [<описатель файла>]) выводит строковую константу <строка> на экран или в открытый файл, заданный <описателем файла>. Возвращает строку, взятую в кавычки, и отпускает кавычки, когда строка записывается в файл.
Пример:
(write-line "Ну, заяц погоди!" f)
записывает строку в файл и возвращает ее же.
5.2. Доступ к графической базе данных
 
Графические примитивы  и их наборы в базе данных Автокада идентифицируются по их имени, т.е. по алфавитно-цифровому коду, присваиваемому Автокадом. Имена примитивов и наборов не являются постоянными, они назначаются Автокадом только на период текущего сеанса Редактирования.


Однако кроме имен примитивов  возможно использование постоянных меток примитивов, независимых от сеансов редактирования.
Наиболее простой способ создания набора примитивов - это использование функции ssget. С ее помощью можно создать набор одним из следующих способов:
-  однозначным указанием примитивов для выбора, используя опции "Last (Последний)", "Current (Текущий)", "Window (Рамка)", "Crossing (Секрамка)" и др. Можно также выбрать все примитивы из базы данных рисунка.
-  запросом у пользователя примитивов набора.
 Формат функции ssget:
(ssget [<режим>] [<точка1> - <точка2>...<точка n> [<фильтр-список>]])
Необязательный аргумент <режим> - это строка, которая указывает тип выбора примитивов.  В качестве аргумента <режим> устанавливается первая буква режима выбора: "W (P)", "С (С)", "L (П)", "P (Т)" и др.
Следующие аргументы <точка1>...<точка n> задают точки для соответствующих режимов (если режим не предполагает задание точек, их задавать не следует).
Аргумент <фильтр-список> может использоваться с любым режимом выбора примитивов и определяет конкретные свойства  выбранных примитивов. Используя <фильтр-список>, можно создать набор, состоящий из примитивов заданного типа, находящихся на заданном слое или заданного цвета.
Примеры:
(setq ss1 (ssget)) - запрашивает у пользователя выбор примитивов общего вида и помещает выбранные примитивы в переменную ss1.
(setq ss2 (ssget "p")) - создает набор примитивов, выбранных перед вызовом функции ssget, выбранных перед вызовом функции ssget.
 (setq ss3 (ssget "w" pt1 pt2)) - создает набор примитивов, попадающих в рамку, заданную точками pt1, pt2.
(setq ss4 (ssget "x")) - создает набор из всех примитивов базы данных рисунка.
Пример использования <фильтра-списка>:
(setq ss1 (ssget ¢((0. "TEXT")))) - запрашивает у пользователя выбор примитивов общего вида, однако только текстовые примитивы попадают в набор.


(setq ss1 (ssget "p" ¢((0. "LINE")))) - создает набор примитивов, входящих в текущий выбор и являющихся отрезками.
Здесь <фильтр-список> может содержать групповые коды:
0   - для типа примитива;
2   - имя блока;
3   - имя размерного стиля;
6   - имя типа линии;
7   - имя гарнитуры шрифта;
8   - имя слоя;
62 - цвет.
Более подробная информация по групповым кодам приведена в [1].
Следующая функция (sslength <набор>) - возвращает целую величину, представляющую собой число примитивов в <наборе>.
Функция (ssname <набор> <индекс>) - возвращает имя примитива под номером <индекс> из <набора> примитивов. Если <индекс> отрицательный или больше самого большого номера примитива в наборе, то будет возвращен nil. Первый примитив имеет <индекс> нуль.
Функция (ssmemb <имя примитива> <набор>) - проверяет, является ли примитив <имя примитива> членом <набора>. Если - да, возвращается имя примитива, если - нет, возвращается nil.
Функция (ssadd [<имя примитива> [<набор>]]) создает или расширяет набор. При вызове без аргументов  функция  создает новый набор без примитивов.   При вызове с аргументом <имя примитива>, создает новый набор, состоящий из одного примитива. При вызове с обоими аргументами, добавляет примитив <имя примитива> в <набор>. Функция возвращает новый или измененный набор.
Примеры:
(setq ss (ssadd)) - устанавливает пустой набор ss;
(ssadd e1 ss) - возвращает набор ss, в который добавлен примитив е1.
Функция (ssdel <имя примитива> <набор>) уничтожает примитив <имя примитива> из <набора>. Если примитива нет в <наборе>, возвращается nil.
ЛЕКЦИЯ 6
РАБОТА С ГРАФИЧЕСКИМИ ПРИМИТИВАМИ
6.1. Иерархия описаний примитивов
Можно привести следующую иерархию описаний графических примитивов в Автокаде по последовательности доступа к ним:
1.    Набор графических примитивов


2.    Имя примитива
3.    Список примитива
4.    Элементы списка примитива (подписки)
Способы построения и сохранения наборов примитивов рассматривались в предыдущей лекции. Набор примитивов включает примитивы, которые вы  выбрали.
Как уже говорилось, каждый примитив в наборе имеет имя. Это могут быть не такие имена как LINE [ОТРЕЗОК] или CIRCLE [КРУГ].
Например:
Именем примитива может быть:
<Entity name: 60000014>
Извлечь имена примитивов из набора можно с помощью функций (entsel), (entlast), (entnext).
Рассмотрим пример списка примитива. Такой список содержит информацию об отдельном примитиве:
((-1. <Entity name: 60000014>)
(0. "CIRCLE")
(8. "LAYER1")
(10. 563.000000 484.000000)
(40. 60.000000))
Отдельными элементами данного списка являются:
подсписки: (40. 60.000000) - подсписок, задающий радиус окружности;
                   (8. "LAYER1") - имя слоя;
                 (10. 563.000000 484.000000) групповой код 10 соответствует описанию точки центра.
Одни и те же коды групп для различных примитивов обозначают различные атрибуты примитивов. Например, для текстового примитива подсписок (40. 60.000000) соответствует описанию высоты текста. В данном примере 40 - это код высоты текста, и высота текста равна 60.000000.
6.2. Извлечение описаний примитивов и работа с ними
Извлечь списки примитивов из графической базы Автокада можно с помощью функции (entget <имя примитива>). Функция выбирает поименованный примитив <имя примитива> и возвращает в виде списка, состоящего из определяющих примитив подсписков.
Пример:
(setq i 0)
(setq a (ssget))
(setq na (ssname a i)
(setq b (entget na)) - возвращает список, описывающий первый примитив набора b.
Рассмотрим еще один пример - программу, которая позволяет изменить высоту символов ранее введенного текста.
(defun chtext (/ a ts n index b1 b c d b2)
  (setq a (ssget)); выбор объектов
  (setq ts (getreal "Введите новую высоту текста"))


  (setq n (sslength a))
  (setq index 0)
  (repeat n
              (setq b1 (entget (ssname a index)))
              (setq index (1+ index))
              (setq b (assoc 0 b1))
              (if (= "ТЕКСТ" (cdr b))
                          (progn
                                      (setq c (assoc 40 b1))
                                      (setq d (cons (car c) ts))
                                      (setq b2 (subst d c b1))
                                      (entmod b2)
                          )
              )
  )
)
Немного пояснений. В программе определены только локальные переменные. С помощью функции (ssget) пользователю предоставляется сделать выбор текста  стандартным образом - с помощью опций "WINDOW(РАМКА)" или "CROSSING (СЕКРАМКА)".
Размер задается с клавиатуры. Количество выбранных примитивов в наборе определяется с помощью функции (sslength). В выбранные примитивы могут входить не только текст, но и другие примитивы.
Далее производится цикл по всем выбранным примитивам. Извлекается очередной примитив. В переменную b записывается подсписок, включающий код группы "0" - имя примитива. Если обрабатываемый примитив - текстовый, в переменную c назначается подсписок, определяющий высоту текста (код группы - 40). С помощью функций (cons) и (car) конструируется подсписок с новой высотой текста. Следующая строка программы производит замену старого подсписка c на новый d в списке b1. Затем новое значение списка b1 присваивается b2. С помощью функции (entmod) новый список b2 записывается в графическую базу данных.
Возможна следующая модернизация программы:

вместо: (setq a (ssget))

можно вставить: (setq a (ssget "X" ¢((0. "TEXT"))))

В этом случае, программа будет изменять высоту всех текстовых примитивов.


           This is line 1                                        This is line 1
           This is line 2                                        This is line 2


           This is line 3                                        This is line 3
рис. 6.1. Иллюстрация работы программы  chtext.
Так как теперь в наборе остались одни текстовые примитивы, таким образом, нет смысла извлекать имя примитива и  делать проверку на его соответствие строке "ТЕКСТ".
(setq b (assoc 0 b1))
(if (= "TEXT" (cdr b)))
6.3. Чтение и запись системных переменных
Для определения значений системных переменных Автокада используется  функция (getvar <имя переменной>). Здесь <имя переменной> - строковая константа.
Пример:
(setq a (getvar "limmin")) - возвращает координаты левого нижнего угла области рисования, заданной по команде limits (лимиты);
(setq b (getvar "limmax")) - возвращает координаты правого верхнего угла области рисования.
Рассмотрим пример использования функции чтения системных переменных в программе очистки экрана от графических примитивов, попадающих в зону области рисования:
(defun c: erasescr (/ l u))
(graphscr)
(setq l (getvar "limmin"))
(setq u (getvar "limmax"))
(command "erase" "w" l u  " ")
)
Для модификации (записи) новых значений системных переменных используется функция (setvar <имя переменной> <значение>). Имя переменной должно быть заключено в двойные кавычки.
Рассмотрим функции извлечения имен примитивов.
(entsel [<подсказка, сообщение>]) - возвращает список, первый элемент которого есть имя выбранного примитива, а второй - координаты точки, использованной для указания примитива. Если в качестве <подсказки> указана строковая константа (строка), то эта строка будет использована для запроса примитива у пользователя, иначе в качестве <подсказки> по умолчанию будет использоваться "Select objects:" ("Выберите объекты:").
(entlast) - возвращает имя последнего не удаленного примитива. Применяется для извлечения имени нового примитива, который был только что добавлен функцией COMMAND.


(entnext [<имя примитива>]) - при вызове без аргумента эта функция возвращает имя первого не удаленного примитива, следующего за примитивом <имя примитива>. Если следующий примитив отсутствует, то будет возвращен nil.
(entdel <имя примитива>) - удаляет примитив <имя примитива> из текущего чертежа и восстанавливается на чертеже, если он был удален перед этим в сеансе редактирования.
ЛЕКЦИЯ 7
РАБОТА С ПРИМИТИВАМИ. ДОСТУП К ГРАФИЧЕСКОЙ БАЗЕ ДАННЫХ
На прошлой лекции были рассмотрены функции доступа к графическим примитивам в базе данных чертежа Автокада. С их помощью можно извлечь список примитива из базы данных и, с помощью функций работы со списками, преобразовать его. Ниже приведена программа для вывода списка примитивов на экран.
Программа вывода списка указанных примитивов SSX_Find_Entity (SSX_FE).
(defun ssx_fe (/ x  data  ent)
  (setq ent (car (entsel "\ n Выберите объект:")))
  (if ent
              (progn
                          (setq data (entget ent))
                          (princ data)
              )
  )
)
В данном случае делается выбор объекта указанием. Функция (entsel) возвращает список, первый элемент которого есть имя выбранного примитива, а второй - координаты точки, использованной для указания примитива. Имя выделяется с помощью функции (car) и передается в переменную ent. Далее, с помощью функции (entget) извлекается список примитива и выводится на экран.
Для изменения примитива, в соответствии с новым описанием, или для создания нового примитива, необходимо возвратить новое описание в базу данных. Это можно сделать с помощью двух функций: (entmake) и (entmod).
Функция (entmake [<список>]) предназначена для создания нового примитива. Функция возвращает <список> переданный в качестве аргумента, если создает примитив в соответствии с этим списком, в противном случае - nil (в том числе и при  неправильном формате <списка>). <Список> должен соответствовать списку, возвращаемому функцией entget. <Список> должен содержать все необходимое для создания примитива, не заданные элементы берутся по умолчанию, все заданные проверяются.


Перед тем, как создать новый примитив (entmake) проверяет корректность указанных имен слоя, типа линии и цвета. Если указанное в описании имя слоя отлично от имеющихся в базе данных рисунка имен, создается новый слой с этим именем. Проверяются также имена блоков, размерных стилей, гарнитур текста.
Пример:
(entmake ¢((0. "CIRCLE")         Тип примитива
                 (62.  1)                     Цвет
                 (10  4.0  4.0  0.0)     Центральная точка
                 (40.  10.0)                Радиус
                 )
)
Результатом вызова функции будет создана окружность красного цвета с центром в точке с координатами (4, 4) и радиусом 10. Необязательные параметры (слой и тип линии) были опущены, и им будет присвоено значение по умолчанию.
Функция (entmod <список>) обновляет информацию базы данных о примитиве, имя которого указано в группе "- 1." списка.
Основной механизм, посредством которого Автокад обновляет базу данных, это извлечение примитивов с  помощью (entget), изменение списка и обновление примитива в базе данных с помощью функции (entmod).
Пример:
(setq en (entnext))
(setq ed (entget en))
(setq ed
              (subst (cons 8 "0")
                         (assoc 8 ed)
                         ed
              )
  )
(entmod ed)
Здесь определяется имя первого примитива рисунка и его слой меняется на слой "0".
Далее рассмотрим две программы, имеющие практическое значение.
В процессе работы с Автокадом иногда удобно изменять свойства примитива, указанием на другой примитив.
Программа изменения свойства примитива (слой) указанием на  другой примитив.
(defun chlay ( )
  (setq a (ssget "Выберите примитивы для изменения слоя"))
  (setq lays (cdr (assoc 8
              (entget (car (entsel) "\n "Выберите примитив для задания слоя:  ")))))
  (setq n (sslenth a)
  (setq index 0)
  (repeat n
              (setq ent (entget (ssname a index)))


              (setq index (1+  index))
              (setq lay (assoc 8 ent))
              (setq l (cons (car lay) lays))
              (setq ent1 (subst l lay ent))
              (entmod ent1)
      )
  )
)
Программа для отрисовки прямоугольника вокруг выбранного текстового примитива.
Для решения задачи используется функция (textbox <список>). Данная функция измеряет заданный текстовый примитив и возвращает координаты диагонали описывающего текст прямоугольника.
Аргумент список должен описывать текстовый примитив. В случае успешного завершения функция возвращает список двух точек, в противном случае - nil. Минимальный список, который можно передать (textbox), это сама текстовая строка. Точки, возвращаемые функцией (textbox), задают описывающий текстовый примитив прямоугольник так, как если бы этот примитив был вставлен в точку с координатами    (0, 0, 0) с углом поворота, равным 0.
Независимо от ориентации строки и гарнитуры текста точки, возвращаемые функцией (textbox), вычисляются относительно точки вставки текста (код группы 10), непосредственно преобразованной в объектную систему координат. На эту точку следует ссылаться при преобразовании координат, возвращаемых функцией (textbox), для определения действительных границ текста.
Текст программы:
(defun c: win (/ textent tb ll ur ul lr)
(setq textent (car (entsel "\ n Выберите текст:  ")))
(сommand "UCS" "entity" textent)
(setq tb (textbox (list (cons - 1 textent)))
  ll (car tb)                                                                         ul                       ur
  ur (cadr tb)
  ul (list (car ll) (cadr ur))
  lr (list (car ur) (cadr ll))                                                  ll                        lr
)
(command "pline" ll lr ur ul "close")
(command "UCS" "p")
(princ)
)
ЛЕКЦИЯ 8
Еще раз об использовании разработанных программ на Автолиспе
8.1. Загрузка программ


Определения функций, программы, можно хранить в определенных файлах и загружать их с помощью функции (load) вручную.
Можно занести необходимые функции в файл acad.lsp, в этом случае Автолисп загрузит их автоматически при запуске Автокада.
При необходимости загрузить библиотеку часто используемых функций при запуске Автокада с новым рисунком, можно занести эти функции в файл acad.mnl. Обычно в таких файлах содержатся программы на Автолиспе, необходимые для выполнения соответствующих операций в файле меню (*.mnu). Файл *.mnl  должен иметь тоже имя, что файл загружаемого меню. Файл стандартного описания меню называется acad.mnu.
Однако следует включать в автоматически загружаемую библиотеку функций только часто используемые функции, чтобы уменьшить объем используемой памяти.
8.2. Выполнение программ
Загруженные вручную, или автоматически функции можно вызывать из командной строки, из меню загрузки приложений. Возможно автоматическое выполнение функций после загрузки Автокада. Для этого, функции включают в тело функции с именем S::STARTUP. Если функция S::STARTUP определена в файле acad.lsp или *.mnl, она будет автоматически вызываться на выполнение (без аргументов) в начале сеанса работы Автокада.
Автоматически выполняемые функции можно использовать, например, для переопределения команд Автокада, для загрузки пользовательских меню приложений. Автолисп позволяет пользователю определить свои собственные команды Автокада,  но с их помощью нельзя заменить встроенные команды Автокада с теми же именами. Однако, если это необходимо, можно "переопределить" встроенные команды Автокада. Для отмены встроенного определения команды используется команда НЕТКОМ (UNDEFINE). Для восстановления определения команды используется команда ДАКОМ (REDEFINE) с именем восстанавливаемой команды.
Переопределенная команда может вызываться с символом (.) - точка перед именем.
Пример использования команд ДАКОМ (REDEFINE), НЕТКОМ (UNDEFINE).


Переопределим команду ОТРЕЗОК (LINE).
Определим функцию с: LINE:
(defun c: LINE( )
  (princ " Не лучше ли использовать полилинию? \ n")
  (command ". LINE")
)
Далее откажемся от команды "LINE"
команда: UNDEFINE
Command name: LINE ¿
Теперь, при вызове команды LINE вначале будем получать сообщение: "Не лучше ли использовать полилинию?", а потом стандартные запросы команды "LINE".
Другой пример, связанный с автоматической загрузкой и выполнением программы на Автолиспе.
Пусть необходимо переопределить стандартные команды Автокада ПОКИНЬ (QUIT) и КОНЕЦ (END). Это можно сделать с помощью автоматически загружаемого файла acad.lsp, содержащего следующие записи:
(defun c: QUIT ( )
  (princ "изменения не будут сохранены!")
  (сommand ".QUIT")
)
(defun c: END ( )
              (command "ZOOM" "ALL")
  (princ "\ n До следующего сеанса, USER!")
  (сommand ".END")
)
(defun S::STURTUP ( )
  (command "UNDEFINE" "QUIT")
  (command "UNDEFINE" "END")
)
Новое определение команды "QUIT" в дополнение к  стандартным сообщениям, выводит перед выходом из Автокада сообщение о том, что изменения, сделанные в текущем сеансе редактирования, сохраняться не будут. Новое определение команды "END" перед выходом из Автокада делает масштабирование рисунка, с отображением всей области рисования,  прощается с пользователем и  сохраняет рисунок в файле с исходным именем.
 
8.3. Использование меню для загрузки и выполнения программ
Определяемые пользователем функции и программы могут загружаться и вызываться из экранного, падающего, планшетного и других меню. Для этого пользователь должен модифицировать исходный файл описания меню acad.mnu в текстовом редакторе, сохранить его под новым именем с расширением .mnu. Новое меню может загружаться пользователем вручную или автоматически из S::STURTUP - функции с помощью команды MENU (МЕНЮ).


Короткие функции Автолиспа могут включаться непосредственно в разделы описания меню.
Пример:
Приложение может использовать следующие пункты меню:
[РАЗМЕРЫ]
[ДЛИНА]^C^C (setq RLEN (getreal "\ n Введите ширину блока"))
[ШИРИНА]^C^C (setq RWID (getreal "\ n Введите длину блока"))
[РАСЧЕТЫ]
[ПЛОЩАДЬ]^C^C (SBL RLEN RWID)
[ЗАГРУЗИ]^C^C (LOAD "myprog")
Это меню позволяет определить Лисп-переменные RLEN, RWID, используемые в расчетной программе (SBL), а также загрузить функцию (myprog) c описанием функции (SBL).
Несколько слов о системе меню Автокада. Полное описание системы меню Автокада и правила его модификации приведены в "Руководстве по адаптированию системы Автокад". Исходный текстовый файл описания меню Автокада включает в себя разделы, описывающие экранное, падающее и курсорное меню, графическое меню, кнопочные меню устройств указания, планшетное меню.
Разделы файла меню идентифицируются с помощью меток. Названия некоторых меток меню приведены в таблице 8.1.
Таблица 8.1

Метка раздела
Название меню
*** BUTTONSn
Кнопочные меню устройств указания (n=1¸4)
*** AUXn
Дополнительное кнопочное меню
*** POPn
Падающие и курсорные меню (n=1¸16)
*** ICON
Графическое меню
*** TABLETn
Планшетное меню (n=1¸4)
*** SCREEN
Экранное меню

Разделы меню могут включать в себя строки названий подменю и команд.
Подменю обозначаются метками. Метка подменю означает начало подменю.
Формат метки:
              **<имя меню> <номер>
<Имя меню> может включать до 31 символа (буквы, цифры и специальные символы).
<номер> - целое число, которое задает номер начальной строки подменю.
Пример:
              ** main_menu 3
Для обращения к подменю используют конструкцию $<раздел>=<имя меню>
Здесь <раздел>:
S             - для меню SCREEN
P1 - P16 - для меню POP
I             - для меню ICON


B1 - B4  - для меню BUTTON
T1 - T4 - для меню TABLET
A1 - A4 - для меню AUX
Пример:
Обращение к подменю из предыдущего примера выполняется с помощью строки $=main_menu. Это подменю размещается, начиная с третьей строки текущего меню.
Другие примеры:
$ S = PARTS
$ T2 = SCREEN
Пример экранного меню (в стиле AutoCAD12), в котором используются возможности субменю:
** SCREEN
[Пример]
          _____________
[РИСУЙ...] $  = DRAW_ROOT
[РЕДАКТ] $ S = Edit_Root
          _____________
[ПОКА...] КОНЕЦ
          _____________
          _____________
          _____________
[-ГЛАВН-] $ S = SCREEN
** DRAW_ROOT
[Отрезок] отрезок
[Круг] круг
[Дуга] Дуга
[Кривая] (curve)
**EDIT_ROOT
[Сотри] $ S = Sel_obj сотри
[Копируй] $ S = Sel_obj копируй
[Перенеси] $ S = Obj_sel перенеси
** Obj_sel 2
** Sel_obj 2
[Последн.] последний
[Текущий] текущий
[Рамка] рамка
[Секрамка] секрамка
[-Пред-] $ S =     - вызывает предменю.
Здесь обращение к пункту меню РИСУЙ вызывает переход к подменю **DRAW_ROOT, содержащему команды рисования. Обращение к пункту меню РЕДАКТ вызывает переход к подменю **EDIT_ROOT. Пункты подменю **EDIT_ROOT сначала устанавливают опции выбора объектов на экранном меню, затем вызывают соответствующую команду.
ЛЕКЦИЯ 10
ПРИМЕР DXF-ФАЙЛА
Рассмотрим пример DXF-файла в текстовом (ASCII) формате, содержащий только описание примитивов (раздел ENTITIES). Пример включает несколько графических примитивов (рис.9.1.). Это квадрат, с размерами 100х100 мм, в центре которого находится окружность с диаметром 60 мм. Над квадратом помещена текстовая строка "Пример DXF",  справа от квадрата - полилиния, включающая линейные и дуговые сегменты.
                          Пример DXF
                                                                                     200, 200          6


                                                                                                       5    
                                                                                                                     4
 
                                                                                                                   3
                                                                                                       2


                                                                                                              1
      100, 100
Рис. 9.1. Исходное изображение.
Описание этого рисунка в DXF-файле содержит следующие коды групп.

Код группы
Тип значения
0
Устанавливает начало описания графического примитива или разделителя файла. Следующее за ним текстовое значение указывает тип начала.
1
Первичное текстовое значение для графического примитива.
2
Имя: имя атрибута, блока и т.д. Также используется для идентификации раздела DXF или  имени таблицы.
5
Метка примитива.
6
Имя типа линии.
7
Имя гарнитуры шрифта.
8
Имя слоя.
10
Первичная координата Х (начальная точка, центр)
11-18
Другие координаты Х.
20
Первичная координата Y.
21-28
Другие координаты Y.
30
Первичная координата Z.
31-37
Другие координаты Z.
39
Высота графического примитива.
40-48
Значения с плавающей точкой (высота текста, масштабные коэффициенты и др.)
50-58
Углы.
62
Номер цвета.

  0
SECTION
  2
ENTITIES
  0
TEXT
  8
0
  5
3E
  10
100.0
  20
210.0
  30
0.0
  40
15.0
  1
Пример DXF
  7
ROMAND
  0
CIRCLE
  8
0
  5
3D
  10
150.0
  20
150.0
  30
0.0
  40
30.0
  0
INSERT
  8
0
  5
46
  2
*X4
  10
0.0
  20
0.0
  30
0.0
1001
ACAD
1000
HATCH
1002
0
1070
16
1000
0


1040
5.0
1040
0.79
1070
        0
1002
}
  0
LINE
  8
0
  5
3B
  10
200.0
  20
200.0
  30
0.0
  11
200.0
  21
100.0
  31
0.0
  0
LINE
  8
0
  5
38
  10
100.0
  20
100.0
  30
0.0
  11
100.0
  21
200.0
  31
0.0
  0
LINE
  8
0
  5
3C
  10
200.0
  20
100.0
  30
0.0
  11
100.0
  21
100.0
  31
0.0
  0
LINE
  8
0
  5
39
  10
100.0
  20
200.0
  30
0.0
  11
200.0
  21
200.0
  31
0.0
  0
POLYLINE
  8
0
  5
53
  66
        1
  10
0.0
  20
0.0
  30
0.0
  0
VERTEX
  8
0
  5
54
  10
225.0
  20
100.0
  30
0.0
  0
VERTEX
  8
0
  5
55
  10
225.0
  20
135.0
  30
0.0
  42
-0.41
  0
VERTEX
  8
0
  5
56
  10
235.0
  20
145.0
  30
0.0
  42
1.0
  0
VERTEX
  8
0
  5
57
  10
235.0
  20
160.0
  30
0.0
  42
-0.41
  0
VERTEX
  8
0
  5
58
  10
225.0
  20
170.0
  30
0.0
  0
VERTEX
  8
0
  5
59
  10
225.0
  20
200.0
  30
0.0
  0
SEQEND
  8
0
  5
5A
  0
ENDSEC
  0
EOF