AutoCAD. Visual LISP, AutoLISP и DXF: Учебное пособие по Visual LISP [Коллектив авторов] (doc) читать онлайн

-  AutoCAD. Visual LISP, AutoLISP и DXF: Учебное пособие по Visual LISP  182 Кб скачать: (doc) - (doc+fbd)  читать: (полностью) - (постранично) - Коллектив авторов

Книга в формате doc! Изображения и текст могут не отображаться!


 [Настройки текста]  [Cбросить фильтры]




AutoCAD


Visual LISP, AutoLISP и DXF

Учебное пособие по Visual LISP

AutoCAD 2002 русский
В данном учебном пособии продемонстрировано несколько мощных возможностей среды программирования Visual LISPTM для AutoCAD® и представлены некоторые новые функции языка AutoLISP®.
Здесь на примере построения парковой дорожки с помощью автоматизированных средств рисования, экономящих время разработчика, показаны преимущества параметрического программирования. В данном учебном пособии также описано создание процедур, которые позволяют пользователю автоматизировать процесс рисования сложных форм, а не начинать каждый раз построения с нуля.


ОГЛАВЛЕНИЕ
Введение 4
Знакомая парковая дорожка: Работа в среде Visual LISP 4
Обзор учебного пособия 4
Постановка задачи и начало программирования 5
Постановка задачи 5
Начало работы в Visual LISP 7
Знакомство с форматированием кода в Visual LISP 8
Анализ кода 8
Создание фиктивных функций в программе 9
Проверка кода с помощью Visual LISP 10
Выполнение программы в Visual LISP 11
Итоги занятия 1 11
Средства отладки Visual LISP 11
Различия между локальными и глобальными переменными 12
Применение локальных переменных в программе 13
Изучение функции gp:getPointInput 14
Объединение данных в ассоциативные списки 15
Применение ассоциативных списков 15
Сохранение результата функции gp:getPointInput в переменной 16
Контроль значений переменных программы 17
Переработка кода программы 18
Комментарии в тексте программы 20
Точки останова и несколько контрольных значений 21
Панель инструментов «Отладка» 21
Пошаговое выполнение кода 23
Контроль значений переменных в ходе пошагового выполнения программы 24
Шаг с выходом из функции gp:getPointInput и переход к C:Gpmain 26
Итоги занятия 2 26
Построение границы дорожки 27
Использование служебных функций 27
Перевод градусов в радианы 27
Преобразование 3М точек в 2М 28
Создание графических объектов в AutoCAD 30
Создание объектов с помощью функций ActiveX 30
Создание объектов с помощью entmake 30
Создание объектов из командной строки AutoCAD 30
Создание функции построения границы дорожки 30
Передача параметров функциям 31
Работа с ассоциативным списком 32
Использование углов и задание точек 33

Введение
Знакомая парковая дорожка: Работа в среде Visual LISP
Данное учебное пособие предназначено для опытных пользователей AutoCAD и предполагает, что они знакомы с языком LISP или AutoLISP. Предполагается также, что пользователь может выполнять основные операции управления файлами в системе Windows® — например, создавать папки, копировать файлы и перемещаться по файловой структуре на жестком диске и в сети.
Пользователям, уже знакомым с примером построения парковой дорожки с помощью AutoLISP, который описывался в предыдущих пособиях, следует обратить внимание на ряд отличий:
• Введена визуальная среда разработки Visual LISP (VLISPTM). Она позволяет редактировать и отлаживать код, а также предоставляет пользователю ряд других специальных средств разработки приложений AutoLISP. Если раньше в учебном пособии по построению парковой дорожки основное внимание уделялось концепциям языка AutoLISP, то теперь в него включено и описание инструментов разработки VLISP.
• Продемонстрированы новые функции ActiveX™ и реакторов, а также некоторых других расширений языка AutoLISP, имеющихся в VLISP.
• Учебное пособие было тщательно переработано. По сравнению с предыдущей версией исходный код практически полностью изменен, а учебный материал значительно расширен.
• Имеется два возможных способа выполнения учебного пособия по построению парковой дорожки. Приложение можно выполнить по частям путем интерпретации LISP-файлов, загружаемых в один документ. Можно также скомпилировать программный код в VLX-приложение (исполняемый файл с расширением .vlx). VLX обладает независимым пространством имен и может взаимодействовать с документом, из которого загружается приложение.
Обзор учебного пособия
Целью данного учебного пособия является создание новой команды AutoCAD, которая строит на экране парковую дорожку, выложенную круглой плиткой. Учебное пособие состоит из семи занятий. По мере их прохождения инструкции по выполнению отдельных задач постепенно становятся менее подробными. При возникновении вопросов следует обращаться к электронной справочной системе по VLISP.
Занятия 4 и 5 предназначены для пользователей среднего уровня и выходят за рамки базовых концепций AutoLISP. Занятия 6 и 7 посвящены достаточно сложным задачам программирования и предназначены для опытных разработчиков AutoLISP.
Файлы с исходным кодом для каждого этапа разработки имеются на установочном компакт-диске AutoCAD. Они копируются в систему только при полной установке, или если в ходе выборочной установки выбран компонент «Примеры». Если при последней установке AutoCAD файлы учебного пособия не были скопированы, необходимо снова запустить программу установки, выбрать вариант «Добавить» и установить компонент «Примеры».
Файлы в папке организованы в структуру в соответствии с занятиями учебного пособия:
<Папка AutoCAD>\Tutorial\VisualLISP\Lesson1
<Папка AutoCAD>\Tutorial\VisualLISP\Lesson2
и т.д.
Перед началом выполнения учебного пособия рекомендуется создать рабочую папку и скопировать в нее все файлы исходного кода. Это позволит при возникновении трудностей в ходе изучения учебного пособия восстановить файлы с правильным кодом. В тексте учебного пособия рабочая папка обозначается следующим образом:
<Папка AutoCAD>\Tutorial\VisualLISP\MyPath
Вместо этой строки в каждом конкретном случае следует вводить путь, по которому реально находится рабочая папка.
И, наконец, рекомендуется прочитать раздел «Getting Started» электронного документа Visual LISP Developer's Guide. В нем кратко описаны многие понятия, которые необходимо знать для выполнения данного учебного пособия.
Постановка задачи и начало программирования
На первом занятии определяется, какие задачи должно выполнять приложение. С помощью среды разработки Visual LISP (VLISP) создается LISP-файл и начинается разработка кода программы на AutoLISP. По мере выполнения упражнений также объясняется, каким образом VLISP упрощает разработку приложений.
Постановка задачи
Разработка приложения AutoLISP является следствием осознанной необходимости автоматизировать выполнение какой-либо задачи в AutoCAD. Часто возникает желание ускорить выполнение однотипных повторяющихся построений или объединить сложную последовательность операций в одну. Пусть необходимо построить парковую дорожку, которая имеет сложную конфигурацию с переменным числом компонентов, задаваемых пользователем. Дорожка должна выглядеть следующим образом:

Для построения дорожки программа на AutoLISP должна:
• Нарисовать прямоугольную границу по начальной точке, конечной точке и ширине. Граница может быть расположена на плоскости произвольным образом и обладать произвольными размерами.
• Запросить у пользователя размер плитки и расстояние между плитками. Плитки представляют собой круги; они должны заполнять дорожку, не перекрываясь и не пересекая границу.
• Расположить плитки на дорожке в шахматном порядке.
Чтобы посмотреть, как программа должна работать в конечном итоге, можно запустить готовую версию приложения, поставляемую с AutoCAD.
Для запуска имеющегося примера
1. Из меню AutoCAD «Сервис» выберите «Приложения».
2. Выберите файл gardenpath.vlx из папки Tutorial\VisualLISP и нажмите кнопку «Загрузить».
3. Нажмите «Закрыть».
4. В командной строке введите gpath.
5. В ответ на первые два запроса укажите начальную и конечную точки в области рисования AutoCAD.
6. В ответ на запрос «Half Width of Path» (половина ширины дорожки) введите 50 (если работа ведется в Британской системе единиц, следует ввести 2).
7. В диалоговом окне «Garden Path Tile Specifications» нажмите «OK».
Начало работы в Visual LISP
Посмотрев, как должно работать приложение, можно приступать к его разработке в среде VLISP. Но вначале следует уяснить, что происходит, когда среда VLISP ожидает передачи управления из AutoCAD. Возможно, при запуске примера уже возникала подобная ситуация.
Для знакомства с поведением Visual LISP при ожидании передачи управления из AutoCAD
1. В командной строке AutoCAD введите vlisp для запуска Visual LISP.
2. Переключитесь обратно в окно AutoCAD (для этого можно выбрать соответствующий значок на панели задач или требуемое количество раз нажать ALT+TAB) и введите в командной строке gpath.
3. Перед тем, как отвечать на подсказки команды gpath, переключитесь в окно VLISP.
В окне VLISP указатель мыши заменяется специальным курсором VLISP, обозначающим, что ввод команд или текста в окне VLISP запрещен. Он напоминает, что для возобновления работы VLISP необходимо завершить выполняющуюся в AutoCAD программу AutoLISP.
4. Вернитесь в окно AutoCAD и ответьте на все запросы команды gpath.
Теперь можно приступить к созданию приложения для построения парковой дорожки.
Для начала разработки приложения в Visual LISP
1. Из меню «Файл» среды VLISP выберите «Новый файл».
2. В текстовом окне редактора, озаглавленном «Без имени-0», введите следующий текст (комментарии можно опустить):
;;; Function C:GPath is the main program function and defines the
;;; AutoCAD GPATH command.
(defun C:GPath ()
;; Ask the user for input: first for path location and
;; direction, then for path parameters. Continue only if you have
;; valid input.
(if (gp:getPointInput) ;
(if (gp:getDialogInput)
(progn
;; At this point, you have valid input from the user.
;; Draw the outline, storing the resulting polyline
;; "pointer" in the variable called PolylineName.
(setq PolylineName (gp:drawOutline))
(princ "\nThe gp:drawOutline function returned <")
(princ PolylineName)
(princ ">")
(Alert "Congratulations - your program is complete!")
)
(princ "\nFunction cancelled.")
)
(princ "\nIncomplete information to draw a boundary.")
)
(princ) ; exit quietly
)
;;; Display a message to let the user know the command name.
(princ "\nType gpath to draw a garden path.")
(princ)
3. Выберите «Файл» >> «Сохранить как» и сохраните код в новом файле <папка AutoCAD>\Tutorial\VisualLISP\MyPath\gpmain.lsp.
4. Проверьте свою работу.
Знакомство с форматированием кода в Visual LISP
VLISP распознает синтаксические единицы (символы и слова) текста программы AutoLISP и выделяет их различными цветами. Это упрощает и ускоряет процесс поиска ошибок. Например, если пропущена закрывающая кавычка, текст продолжает набираться фиолетовым, т.е. цветом, обозначающим заключенное внутри кавычек содержимое. Текст, идущий после закрывающей кавычки, окрашивается в зависимости от его назначения.
VLISP форматирует текст программы по мере его ввода, добавляя необходимые отступы и пробелы. Для того, чтобы отформатировать в VLISP код, скопированный из другого файла, следует выбрать из меню VLISP «Сервис» >> «Форматировать код в редакторе».
Анализ кода
Выражение defun описывает новую функцию. Основная функция в данном случае называется C:GPath. Приставка C: обозначает, что к функции можно обращаться из командной строки AutoCAD — точно так же, как к любой команде. Для вызова приложения в ответ на подсказку «Команда:» необходимо ввести Gpath. Для запроса у пользователя ввода информации используются функции gp:getPointInput и gp:getDialogInput. Функция gp:drawOutline строит границу дорожки. Эти функции имеют префикс gp:, обозначающий, что они используются только в приложении построения парковой дорожки. Подобные префиксы рекомендуется (хотя и не обязательно) использовать для того, чтобы отличать функции, используемые исключительно в рамках одного приложения, от совместно используемых функций и утилит.
С помощью выражений princ, находящихся в тексте главной функции, на экран выводится результат, если программа была выполнена успешно, или предупреждающее сообщение, если возникла непредвиденная ситуация. Например, как будет показано на втором занятии, если пользователь нажмет клавишу ENTER вместо указания точки на экране, выполнение функции gp:getPointInput прервется, а в главную функцию будет передано значение nil. В результате программа выдает сообщение о том, что введено недостаточно информации для построения границы.
Вызов функции princ ближе к концу служит для вывода подсказки. После загрузки приложения в командной строке выдается подсказка, в которой говорится какое имя команды надо ввести для построения парковой дорожки. Последняя функция princ, не имеющая строковых аргументов, служит для мягкого выхода из программы (то есть без возвращения главной функцией результата). При отсутствии последнего выражения princ подсказка выдавалась бы дважды.
Создание фиктивных функций в программе
Для того, чтобы программа выполнялась правильно, требуется еще три функции. Главная функция программы содержит вызовы трех пользовательских функций:
• gp:getPointInput
• gp:getUserInput
• gp:drawOutline
Теперь необходимо создать три описания фиктивных функций. Фиктивные функции пока будут заменять полные функции, к разработке которых мы еще вернемся. Их наличие позволяет проверять работу программы до того, как в нее будут добавлены все элементы, необходимые для выполнения приложения.
Для описания фиктивной функции в приложении
1. Поместите курсор в окне редактора в самое начало кода программы и нажмите пару раз ENTER для ввода пустых строк.
2. Введите перед главной функцией следующее:
;;; Function gp:getPointInput will get path location and size
(defun gp:getPointInput ()
(alert
"Function gp:getPointInput will get user drawing input"
)
;; For now, return T, as if the function worked correctly.
T
)
;;; Function gp:getDialogInput will get path parameters
(defun gp:getDialogInput ()
(alert
"Function gp:getDialogInput will get user choices via a dialog"
)
;;For now, return T, as if the function worked correctly.
T
)
;;; Function gp:drawOutline will draw the path boundary
(defun gp:drawOutline ()
(alert
(strcat "This function will draw the outline of the polyline"
"\nand return a polyline entity name/pointer."
)
)
;; For now, simply return a quoted symbol. Eventually, this
;; function will return an entity name or pointer.
'SomeEname
)
Каждая функция содержит в конце строку, состоящую из одного символа «T». Она используется в качестве возвращаемого значения. Каждая функция AutoLISP должна возвращать в вызвавшую ее функцию какое-либо значение. Буква «T» используется в AutoLISP для значения «true» (истина); именно оно будет передано в вызывающую функцию. Файл gpmain.lsp организован таким образом, что для перехода к следующему этапу выполнения программы каждая вызываемая функция должна возвращать значение, отличное от nil (что обозначает «нет значения»).
По умолчанию функции AutoLISP всегда возвращают значение последнего вычисленного внутри нее выражения. В фиктивных функциях единственным выражением является обращение к функции alert. Однако функция alert всегда возвращает значение nil. Таким образом, будучи вызванной последней в gp:getPointInput, она возвратит nil и переход через if к функции gp:getDialogInput станет невозможным.
По аналогичной причине функция gp:DrawOutline возвращает в качестве фиктивного значение ('SomeEname). Конструкции LISP, предваренные апострофом, не вычисляются. Для более подробного изучения языка LISP в конце данного учебного пособия приведен список рекомендуемых книг.
Проверка кода с помощью Visual LISP
VLISP обладает мощными инструментами проверки кода на синтаксические ошибки. Следует пользоваться ими перед выполнением программы. С помощью средств проверки можно выявить такие распространенные ошибки, как пропуск скобок или кавычек, а также другие случаи неверного синтаксиса.
Для синтаксической проверки кода
1. Убедитесь, что окно текстового редактора, содержащее gpmain.lsp, является активным. Для активизации окна следует щелкнуть на его заголовке.
2. Из меню VLISP выберите «Сервис» >> «Проверить текст в редакторе».
3. Результаты синтаксической проверки выводятся в окне «Сообщения сборки». Если ошибок не обнаружено, окно содержит приблизительно следующее сообщение:
[CHECKING TEXT GPMAIN.LSP загружается...]
......
; Проверка завершена.
При возникновении затруднений см. главу «Developing Programs with Visual LISP» электронного документа Visual LISP Developer's Guide. Сначала следует попытаться установить причину ошибки самостоятельно. Если на поиск ошибки уходит слишком много времени, для продолжения работы над учебным пособием можно воспользоваться файлом примера gpmain.lsp, находящимся в папке lesson1.
Для использования файла примера gpmain.lsp (при необходимости)
1. Закройте окно текстового редактора, содержащее файл gpmain.lsp с неработающим кодом.
2. Из меню VLISP выберите «Файл» >> «Открыть файл» и откройте файл gpmain.lsp, расположенный в папке \Tutorial\VisualLISP\lesson1.
3. Выберите «Файл» >> «Сохранить как» и сохраните файл в рабочей папке \Tutorial\VisualLISP\MyPath под именем gpmain.lsp, заменив им неверную копию.
Выполнение программы в Visual LISP
В VLISP имеются специальные средства отладки и выявления ошибок, возникающих при выполнении приложения AutoLISP.
Для загрузки и выполнения программы
1. Установив активным текстовый редактор, из меню VLISP выберите «Сервис» >> «Загрузка текста в редактор».
2. В ответ на подсказку «_$» в окне консоли VLISP введите (C:GPath).
Окно «Консоль Visual LISP» воспринимает ввод команд в формате AutoLISP, поэтому все имена функций должны заключаться в скобки.
3. Нажимайте ENTER или щелкайте на кнопке «OK» в ответ на каждое выдаваемое сообщение. Последним должно появиться сообщение: «Congratulations - your program is complete!»
Замечание: Если окно AutoCAD было свернуто, сообщения не будут видны. Поэтому перед вызовом функции gpath свернутое окно AutoCAD следует развернуть (с помощью панели задач или ALT+TAB).
Итоги занятия 1
На этом занятии были рассмотрены:
• Постановка задачи.
• Значения фиктивных функций.
• Именование внутренних функций приложений, в отличие от совместно используемых функций.
• Проверка кода с помощью VLISP.
• Загрузка и выполнение программ в VLISP.
Занятие окончено. Сохраните программу, чтобы не потерять последние изменения в ней.
Средства отладки Visual LISP
Это занятие посвящено технологии использования средств отладки VLISP, позволяющих ускорить разработку программ на AutoLISP. Кроме того, здесь объясняется разница между локальными и глобальными переменными и особенностями их применения. По окончании данного занятия программа построения дорожки становится более интерактивной, так как в нее добавляется возможность запроса информации у пользователя. Результаты вычислений сохраняются в виде списка. Ознакомившиеся с данной главой начинают понимать преимущества списков при программировании на AutoLISP. Ведь именно благодаря возможностям обработки списков язык LISP получил свое имя (LISt Processing).
Различия между локальными и глобальными переменными
На этом занятии мы сравним локальные и глобальные переменные. Глобальные переменные доступны для всех функций, загруженных в документ (рисунок AutoCAD). Эти переменные могут сохранять свое значение после завершения работы программы, в которой они были описаны. Этим свойством глобальных переменных можно при необходимости воспользоваться. Пример этого будет рассмотрен немного позднее.
Локальные переменные хранят свои значения, только пока выполняется функция, в которой они были определены. После завершения выполнения функции память автоматически освобождается от значений локальных переменных. Это называется автоматической очисткой памяти от мусора и является общим свойством большинства сред разработки на языке LISP, в том числе и VLISP. Локальные переменные используют память более эффективно, чем глобальные.

Другим большим преимуществом локальных переменных является то, что они упрощают отладку и усовершенствование приложений. Так, например, никогда нельзя быть точно уверенным, когда и какая именно функция изменила значение глобальной переменной. Проследить же изменение локальной переменной значительно проще. Таким образом, локальные переменные позволяют сократить количество побочных эффектов (выражающихся в том, что одна часть программы влияет на ее другую часть).
Из-за перечисленных выше преимуществ мы практически везде в данном пособии будем использовать локальные переменные.
Замечание: Возможно, что пользователи, уже работавшие с AutoLISP, привыкли использовать глобальные переменные для проверки работы программы в ходе ее создания. Теперь это уже не нужно, так как в VLISP имеются другие мощные средства отладки.
Применение локальных переменных в программе
Вернемся к функции gp:getPointInput, созданной на первом занятии.
(defun gp:getPointInput ()
(alert
"Function gp:getPointInput will get user drawing input"
)
;; For now, return T, as if the function worked correctly.
T
)
В том виде, как она есть на данный момент, функция не выполняет практически никаких действий. Нам нужно расширить ее, добавив функции ввода пользовательских данных (начальной и конечной точек, а также ширины дорожки).
При создании программ на AutoLISP желательно представлять себе и учитывать поведение AutoCAD. Исходя из этих соображений лучше написать программу так, чтобы она запрашивала не ширину дорожки, а половину ширины, так как в последнем случае появляется возможность задать ее указанием точки на рисунке относительно осевой линии.
После завершения функции gp:getPointInput ее переменные, как и присвоенные им значения, становятся не нужны. Поэтому будем хранить значения, введенные пользователем, в локальных переменных. Функция должна выглядеть следующим образом:
(defun gp:getPointInput (/ StartPt EndPt HalfWidth)
(if (setq StartPt (getpoint "\nStart point of path: "))
(if (setq EndPt (getpoint StartPt "\nEndpoint of path: "))
(if (setq HalfWidth (getdist EndPt "\nHalf width of path: "))
T
)
)
)
)
Локальные переменные объявляются после косой черты в выражении defun, с которого начинается описание функции. Сначала, при первом вызове getpoint, запрашивается начальная точка. Затем относительно нее задается конечная точка. При этом из начальной точки исходит динамическая, растягивающаяся при перемещении курсора линия (резиновая нить). Аналогично этому, при задании половины ширины резиновая нить исходит из конечной точки.
Для проверки работы gp:getPointInput
1. Введите в окне консоли VLISP код функции gp:getPointInput.
2. Поместив курсор в окне консоли за последней скобкой блока кода (или на следующей строке), нажмите ENTER для замены предыдущей загруженной версии функции gp:getPointInput.
3. Вызовите функцию из окна консоли. Для этого в ответ на подсказку введите (gp:getPointInput).
4. Укажите точки, запрашиваемые программой, и задайте половину ширины.
Изучение функции gp:getPointInput
При вызове функции gp:getPointInput управление автоматически передается из VLISP в AutoCAD. После ответа на все три запроса управление передается из AutoCAD в VLISP, а в окне консоли выводится символ «T».
Внутри программы происходит следующее:
1. VLISP ожидает указания первой точки.
2. После того, как первая точка указана, программа запоминает ее значение (список из трех координат X, Y и Z) в переменной StartPt.
3. В первой функции if проверяется, было или не было введено правильное значение. После задания начальной точки управление передается следующей функции getpoint.
4. После указания конечной точки значения ее координат сохраняются в переменной Endpt.
5. Результат этого выражения проверяется в следующем выражении if, после чего управление передается функции getdist.
6. Функция getdist позволяет как указывать точку на экране, так и вводить численное значение. Результат выполнения функции getdist хранится в переменной HalfWidth.
7. После этого программа встречает значение «T», находящееся внутри функции. Так как далее не следует никаких других функций, данная функция завершается и возвращает значение «T». Именно оно и выводится в окне консоли.
Данные необходимо каким-то образом передать из одной функции в другую. Для этого можно, например, создать список значений, полученных функцией gp:getPointInput, как описано ниже:
(defun gp:getPointInput ( / StartPt EndPt HalfWidth )
(if (setq StartPt (getpoint "\nStart point of path: "))
(if (setq EndPt (getpoint StartPt "\nEndpoint of path: "))
(if (setq HalfWidth (getdist EndPt "\nHalf width of path: "))
(list StartPt EndPt HalfWidth)
)
)
)
)
Скопируйте этот вариант gp:getPointInput в окно консоли и нажмите ENTER. Теперь можно воспользоваться еще одной служебной функцией окна консоли.
Для выполнения gp:getPointInput с помощью протокола окна консоли
1. Нажмите TAB. Консоль переходит в режим протокола, и в ней происходит циклический перебор ранее введенных команд. Для перебора команд в обратном порядке служит комбинация SHIFT+TAB.
2. Когда в окне консоли появится подсказка (gp:getPointInput), нажмите ENTER для повторного выполнения функции.
3. Ответьте на запросы, как и в предыдущий раз.
Функция возвращает список, содержащий в себе два вложенных списка и одно вещественное число (с плавающей точкой). Возвращаемые значения выглядят приблизительно следующим образом:
((4.46207 4.62318 0.0) (7.66688 4.62318 0.0) 0.509124)
Значения соответствуют переменным StartPt, EndPt и HalfWidth.
Объединение данных в ассоциативные списки
Хотя предыдущий пример работает, того же самого результат можно добиться другим, более удобным, способом. В следующем упражнении создается ассоциативный список (обрабатываемый LISP-функцией assoc). В ассоциативном списке с каждым элементом-значением связывается свой ключевой код. Ниже приведен пример ассоциативного списка:
((10 4.46207 4.62318 0.0) (11 7.66688 4.62318 0.0) (40 . 1.018248))
В этом списке ключевыми кодами являются числа 10, 11 и 40. Эти ключевые коды служат уникальным индексом списка. С помощью данного механизма AutoCAD возвращает в AutoLISP информацию об объектах, полученную при выполнении программы. Код 10 обозначает начальную точку, код 11 обычно используется для конечной точки.
В чем преимущество использования ассоциативного списка? В отличие от обычного списка, порядок значений в нем не играет никакой роли. Еще раз рассмотрим первый список:
((4.46207 4.62318 0.0) (7.66688 4.62318 0.0) 0.509124)
Присмотримся к возвращенным значениям. Непонятно, какой из вложенных списков соответствует начальной, а какой — конечной точке. Более того, внесение изменений в такую функцию может нежелательным образом повлиять на другие функции, использующие возвращаемые ею данные.
В ассоциативном списке порядок следования значений не важен. Даже при изменении порядка элементы ассоциативного списка всегда можно идентифицировать. Например, код 11 обозначает конечную точку вне зависимости от того, где она располагается в общем списке.
((11 7.66688 4.62318 0.0) ; order of list
(40 . 1.018248) ; has been
(10 4.46207 4.62318 0.0)) ; modified
Применение ассоциативных списков
Значения ключевых кодов для ассоциативных списков необходимо документировать. Так, для парковой дорожки коды 10, 11, 40, 41 и 50 используются следующим образом:
• 10 обозначает 3М координаты начальной точки дорожки.
• 11 обозначает 3М координаты конечной точки дорожки.
• 40 обозначает ширину (но не половину ширины) дорожки.
• 41 обозначает длину дорожки от начала до конца.
• 50 обозначает угол поворота дорожки.
Ниже приведен обновленный вариант функции gp:getPointInput. Здесь используется функция AutoLISP cons, которая создает кодированные вложенные списки, объединяемые затем в общий ассоциативный список. Теперь скопируйте этот вариант функции в окно консоли, нажмите ENTER и запустите функцию (gp:getPointInput) снова.
(defun gp:getPointInput (/ StartPt EndPt HalfWidth)
(if (setq StartPt (getpoint "\nStart point of path: "))
(if (setq EndPt (getpoint StartPt "\nEndpoint of path: "))
(if (setq HalfWidth (getdist EndPt "\nHalf width of path: "))
;; if you've made it this far, build the association list
;; as documented above. This will be the return value
;; from the function.
(list
(cons 10 StartPt)
(cons 11 EndPt)
(cons 40 (* HalfWidth 2.0))
(cons 50 (angle StartPt EndPt))
(cons 41 (distance StartPt EndPt))
)
)
)
)
)
Следует обратить внимание на то, что при построении списка программа умножает половину ширины, указанную пользователем, на 2, преобразуя ее таким образом в полную ширину.
Полученный результат в окне консоли выглядит примерно так:
_$ (gp:getPointInput)
((10 2.16098 1.60116 0.0) (11 12.7126 7.11963 0.0) (40 . 0.592604) (50 . 0.481876) (41
. 11.9076))
_$
Сохранение результата функции gp:getPointInput в переменной
Теперь можно продвинуться дальше. Вызвав функцию снова, сохраним полученный результат в переменной gp_PathData. Для этого в ответ на подсказку в окне консоли введите следующее:
(setq gp_PathData (gp:getPointInput))
Для просмотра содержимого только что описанной переменной введите в окне консоли:
_$ gp_PathData
VLISP возвращает данные в следующем виде:
((10 2.17742 1.15771 0.0) (11 13.2057 7.00466 0.0) (40 . 1.12747) (50 . 0.487498) (41 .
12.4824))
Контроль значений переменных программы
VLISP предлагает разработчику целый комплекс средств программирования и отладки. Одним из наиболее полезных является средство контроля значений, позволяющее отслеживать состояние переменных более подробно, чем это позволяет сделать окно консоли. Можно также контролировать значения локальных переменных в процессе выполнения функции, в которой они определены.
Для контроля значений переменных
1. Выберите «Отладка» >> «Добавить контрольное значение» из меню VLISP. Открывается диалоговое окно «Добавление контрольного значения».
Введите имя переменной для контроля ее значения. В данном случае введем переменную gp_PathData, только что описанную в окне консоли. Открывается диалоговое окно «Контрольное значение»:

VLISP выводит значение переменной в окне «Контрольное значение» на отдельной строке (см. основное окно на рисунке). Таким образом, длинные списки в окне целиком не умещаются. Окно контрольных значений можно растянуть, но можно поступить и иначе.
2. Дважды щелкните мышью на имени переменной в окне «Контрольное значение». Открывается диалоговое окно «Изучение»:

В диалоговом окне «Изучение» показаны тип данных изучаемой переменной (в данном случае LIST—список) и ее значение. Каждый элемент списка выводится в этом диалоговом окне на отдельной строке.
3. Дважды щелкните мышью на строке с кодом 11. Открывается еще одно диалоговое окно «Изучение»:

4. Просмотрев нужные переменные, закройте все диалоговые окна «Изучение», однако окно «Контрольное значение» оставьте открытым.
Переработка кода программы
Теперь, познакомившись с принципом работы ассоциативных списков в AutoLISP, воспользуемся этим типом списка для написания окончательного варианта функции gp:getPointInput. Для этого следует заменить предыдущую версию gp:getPointInput, ранее сохраненную в файле gpmain.lsp, следующим кодом.
Замечание: При наборе текста программы в файле gpmain.lsp вручную (а не копированием из имеющегося готового файла) можно сэкономить время, исключив все комментарии (строки, начинающиеся с точки запятой). Однако это не означает, что следует привыкать писать код без комментариев и в дальнейшем!
;;;--------------------------------------------------------------;
;;; Function: gp:getPointInput ;
;;;--------------------------------------------------------------;
;;; Description: This function asks the user to select three ;
;;; points in a drawing, which will determine the ;
;;; path location, direction, and size. ;
;;;--------------------------------------------------------------;
;;; If the user responds to the get functions with valid data, ;
;;; use startPt and endPt to determine the position, length, ;
;;; and angle at which the path is drawn. ;
;;;--------------------------------------------------------------;
;;; The return value of this function is a list consisting of: ;
;;; (10 . Starting Point) ;; List of 3 reals (a point) denoting ;
;;; ;; starting point of garden path. ;
;;; (11 . Ending Point) ;; List of 3 reals (a point) denoting ;
;;; ;; ending point of garden path. ;
;;; (40 . Width) ;; Real number denoting boundary ;
;;; ;; width. ;
;;; (41 . Length) ;; Real number denoting boundary ;
;;; ;; length. ;
;;; (50 . Path Angle) ;; Real number denoting the angle ;
;;; ;; of the path, in radians. ;
;;;--------------------------------------------------------------;
(defun gp:getPointInput (/ StartPt EndPt HalfWidth)
(if (setq StartPt (getpoint "\nStart point of path: "))
(if (setq EndPt (getpoint StartPt "\nEndpoint of path: "))
(if (setq HalfWidth (getdist EndPt "\nHalf width of path: "))
;; if you've made it this far, build the association list
;; as documented above. This will be the return value
;; from the function.
(list
(cons 10 StartPt)
(cons 11 EndPt)
(cons 40 (* HalfWidth 2.0))
(cons 50 (angle StartPt EndPt))
(cons 41 (distance StartPt EndPt))
) ) ) ) )
Теперь необходимо обновить основную функцию C:GPath в файле gpmain.lsp. Она должна выглядеть следующим образом:
(defun C:GPath (/ gp_PathData)
;; Ask the user for input: first for path location and
;; direction, then for path parameters. Continue only if you
;; have valid input. Store the data in gp_PathData.
(if (setq gp_PathData (gp:getPointInput))
(if (gp:getDialogInput)
(progn
;; At this point, you have valid input from the user.
;; Draw the outline, storing the resulting polyline
;; pointer in the variable called PolylineName.
(setq PolylineName (gp:drawOutline))
(princ "\nThe gp:drawOutline function returned <")
(princ PolylineName)
(princ ">")
(Alert "Congratulations - your program is complete!")
) ;_ end of progn
(princ "\nFunction cancelled.")
) ;_ end of if
(princ "\nIncomplete information to draw a boundary.")
) ;_ end of if
(princ) ; exit quietly
);_ end of defun
При копировании и вставке кода перед описанием функции C:GPath: следует добавить следующий заголовок:
;;;**************************************************************;
;;; Function: C:GPath The Main Garden Path Function ;
;;;--------------------------------------------------------------;
;;; Description: This is the main garden path function. It is a ;
;;; C: function, meaning that it is turned into an ;
;;; AutoCAD command called GPATH. This function ;
;;; determines the overall flow of the garden path ;
;;; program. ;
;;;**************************************************************;
;;; The gp_PathData variable is an association list of the form: ;
;;; (10 . Starting Point) - List of 3 reals (a point) denoting ;
;;; starting point of the garden path. ;
;;; (11 . Ending Point) - List of 3 reals (a point) denoting ;
;;; endpoint of the garden path. ;
;;; (40 . Width) - Real number denoting boundary ;
;;; width. ;
;;; (41 . Length) - Real number denoting boundary ;
;;; length. ;
;;; (50 . Path Angle) - Real number denoting the angle of ;
;;; the path, in radians. ;
;;; (42 . Tile Size) - Real number denoting the size ;
;;; (radius) of the garden path tiles. ;
;;; (43 . Tile Offset) - Spacing of tiles, border to border. ;
;;; ( 3 . Object Creation Style) ;
;;; - Object creation style indicates how ;
;;; the tiles are to be drawn. The ;
;;; expected value is a string and one ;
;;; one of three values (string case ;
;;; is unimportant): ;
;;; "ActiveX" ;
;;; "Entmake" ;
;;; "Command" ;
;;; ( 4 . Polyline Border Style) ;
;;; - Polyline border style determines ;
;;; the polyline type to be used for ;
;;; path boundary. The expected value ;
;;; one of the following (string case is;
;;; unimportant): ;
;;; "Pline" ;
;;; "Light" ;
;;;**************************************************************;
Для проверки переработанного кода
1. Сохраните обновленный файл.
2. Воспользуйтесь функцией «Проверить текст в редакторе» для проверки текста на наличие синтаксических ошибок.
3. Отформатируйте текст программы для повышения читаемости кода.
4. Загрузите код, чтобы переопределить предыдущие версии функций.
5. Для выполнения программы в ответ на подсказку в окне консоли введите (c:gpath).
Если при выполнении программы возникли ошибки, следует попытаться их исправить и запустить программу снова. Если успешного выполнения программы не удается добиться даже после нескольких попыток, можно скопировать правильный код из папки Tutorial\VisualLISP\Lesson2.
Комментарии в тексте программы
Все выражения в программе на языке AutoLISP, начинающиеся с точки с запятой, считаются комментариями. В предыдущих двух примерах кода содержится множество таких комментариев. Комментарии в AutoLISP предназначены для самого разработчика, и не учитываются программой. Включение в текст программы комментариев является одним из признаков хорошего стиля программирования, к которому следует стремиться. Для чего же нужны комментарии?
• Для того, чтобы код был понятен самому разработчику при редактировании программы и добавлении в нее новых возможностей месяцы спустя. Человеческой памяти свойственно быстро забывать подробности реализации алгоритмов, и даже самая очевидная последовательность функций через некоторое время может восприниматься как непонятный набор скобок.
• Для того, чтобы код был понятен другим людям, несущим ответственность за дальнейшее сопровождение программы. Читать код, написанный другим человеком, утомительно и сложно — особенно если в нем практически не содержится комментариев.
В VLISP включены средства, помогающие комментировать код. Следует обратить внимание на то, что некоторые комментарии в примерах начинаются с трех точек с запятой (;;;), некоторые с двух (;;), а некоторые только с одной (;). О том, как VLISP обрабатывает различные комментарии, см. раздел «Applying Visual LISP Comment Styles» в электронном документе Visual LISP Developer's Guide.
В целях экономии места в оставшихся исходных файлах примеров кода данного учебного пособия комментариев не содержится. Предполагается, что разработчик уже имеет хорошую привычку вставлять в текст программы подробные комментарии, и делает это без посторонней подсказки.
Точки останова и несколько контрольных значений
Точка останова — это символ (точка), который помещается в исходный код для обозначения места, в котором выполнение программы временно приостанавливается. VLISP выполняет код до тех пор, пока не встретит точку останова. Обнаружив ее, VLISP переходит в режим ожидания указаний от программиста. Следует обратить внимание, что окончательного прекращения работы программы в этот момент не происходит.
В режиме останова можно
• Выполнять код последовательно: функцию за функцией, выражение за выражением.
• Возобновлять выполнение программы из любой точки.
• Динамически изменять значения переменных и результаты выполнения программы.
• Добавлять переменные для контроля их значений.
Панель инструментов «Отладка»
В данном разделе используются несколько инструментов, имеющихся на панели «Отладка». По умолчанию эта панель расположена в верхней части окна VLISP, в одном ряду с панелями «Вид» и «Сервис».

Панель «Отладка» располагается левее всех. Большинство кнопок панели активизируются только после запуска программы в отладочном режиме (т.е. если заданы одна или несколько точек останова).
Для удобства использования панель «Отладка» можно открепить из ее исходного положения. Для этого нужно установить указатель мыши на две вертикальные линии в левой части панели и перетащить панель. Точно также можно открепить и установить в удобное для работы место любую другую панель VLISP.
Панель «Отладка» разделена на три основные группы, в каждой из которых имеется по три кнопки. При выполнении программы в режиме отладки панель выглядит следующим образом:

• Первые три кнопки позволяют выполнять код программы по шагам.
• Следующие три кнопки определяют поведение VLISP после обнаружения точки останова или ошибки.
• Следующие три кнопки служат для задания или удаления точек останова, добавления переменных для контроля их значений и перехода к месту последнего останова в исходном коде.
Последняя кнопка на панели «Отладка» является индикатором положения курсора. Она не выполняет никаких функций, а только наглядно показывает положение курсора при пошаговой отладке кода. При выполнении программы в обычном режиме (не в режиме отладки) на кнопке ничего не изображается.
Для задания точки останова
1. В окне редактора VLISP, содержащем файл gpmain.lsp, установите указатель мыши перед открывающей скобкой функции setq в следующей строке кода внутри функции gp:getPointInput:
(setq HalfWidth (getdist EndPt "\nHalf width of path: "))
2. Щелкните один раз мышью. Курсор устанавливается в указанную позицию, как видно на следующей иллюстрации:

3. Установив курсор в нужное положение, нажмите кнопку «Точка останова Вкл/Откл» на панели инструментов «Отладка».
Кнопка «Точка останова Вкл/Откл» действует как переключатель между состояниями «Вкл» и «Откл». Если в положении курсора нет точки останова, то она ставится, если уже имеется — удаляется.
4. Нажмите кнопку «Загрузка активного окна редактирования» на панели «Инструменты», чтобы загрузить файл.
5. Запустите функцию (C:GPath) из окна консоли VLISP.
VLISP выполняет программу обычным образом вплоть до точки останова. В данном случае программа запрашивает ввести первые две точки дорожки (начальную и конечную).
6. Укажите в ответ на запросы начальную и конечную точки.
После этого VLISP приостанавливает выполнение программы и переключается в окно текстового редактора. Строка, на которой произошел останов, выделяется.

Необходимо обратить внимание на следующее:
• Курсор располагается прямо в точке останова. Это может оказаться не очевидным, поэтому VLISP предлагает еще одну подсказку.
• На панели «Отладка» индикатор положения курсора принимает вид красного курсора перед парой скобок. Это означает, что выполнение программы было приостановлено перед выражением.
Пошаговое выполнение кода
Для пошагового выполнения кода программы используются три крайние слева кнопки панели «Отладка». Они служат для выполнения следующих действий:
• Шаг с заходом в выделенное выражение.
• Переход к концу выделенного выражения (т.е. шаг с обходом).
• Переход к концу текущей функции, внутри которой произошел останов (т.е. шаг с выходом).
Перед тем, как сделать выбор, следует еще раз проверить статус выделенного выражения и положение курсора. В данном случае выделенное выражение включает в себя вложенную в setq функцию getdist, а курсор располагается в самом начале выделенного блока.
Для пошагового выполнения кода от точки останова
1. Нажмите кнопку «Шаг с обходом».
После этого управление передается AutoCAD, и запрашивается ширина дорожки.
2. Ответьте на запрос.
После того, как ширина задана, управление снова передается в VLISP. Следует обратить внимание на положение курсора на индикаторе.
VLISP вычисляет выделенное выражение целиком и останавливается в конце выражения.
3. Снова нажмите кнопку «Шаг с обходом». VLISP переходит к началу следующего блока кода и выделяет блок целиком.
4. Теперь нажмите другую кнопку — «Шаг с заходом».
Замечание: Если в ходе выполнения упражнения был сделан неправильный выбор или пропущены какие-либо шаги, его можно легко начать сначала. Для этого сначала следует нажать кнопку «Сброс» на панели «Отладка». Выполнение кода VLISP прекращается, и система VLISP возвращается на верхний уровень. Теперь можно начать все с пункта 1.
Выделенной становится первая функция cons, а VLISP останавливается непосредственно перед функцией (обратите внимание на индикатор положения курсора).
Контроль значений переменных в ходе пошагового выполнения программы
При пошаговом выполнении программы можно добавлять переменные в окно контрольных значений и изменять их значения.
Если окно контрольных значений не видно на экране, для его вывода достаточно нажать на панели кнопку «Окно контрольных значений».
Если в окне контрольных значений все еще содержится переменная gp_PathData, следует нажать кнопку «Очистить окно», расположенную в верхней части окна.
Для добавления переменной в окно контрольных значений
1. Дважды щелкните мышью на любом вхождении StartPt в окне текстового редактора VLISP. Это имя переменной, изменения значения которой необходимо проследить.
2. Нажмите кнопку «Добавить контрольное значение» в окне контрольных значений, или щелкните правой кнопкой мыши и выберите «Добавить контрольное значение» из контекстного меню.
3. Повторите ту же процедуру для переменных EndPt и HalfWidth. Окно контрольных значений должно выглядеть приблизительно так:

Использование точек останова в сочетании с контролем значений при отладке неверно работающей программы позволяет проверить, принимаютли переменные нужные значения в процессе выполнения программы.
Кроме того, пользователь может изменить значение переменной и проследить, как это повлияет на выполнение программы. Пусть, например, переменная halfwidth должна принимать только целые значения. Однако в результате неточного ввода точек ее значение может оказаться равным 1.94818. Изменяя значение переменной вручную, можно выяснить, как это скажется на поведении программы.
Для изменения значения переменной в ходе выполнения программы
1. В ответ на подсказку в окне консоли введите следующее:
(setq halfwidth 2.0)
Следует обратить внимание на то, что значение в окне «Контрольное значение» изменилось. Но можно ли при этом быть уверенным, что именно это новое значение используется при создании ассоциативного списка (вложенный список с кодом 40)? Для проверки этого добавим еще одно выражение в окно контрольных значений.
2. Выберите «Отладка» >> «Результат последнего вычисления» из меню VLISP.
В окно «Контрольное значение» добавляется переменная с именем *Last-Value*. *Last-Value* — это глобальная переменная, в которой VLISP автоматически сохраняет значение последнего вычисленного выражения.
3. Выполняйте программу пошагово (с помощью кнопок «Шаг с заходом» и «Шаг с обходом») до тех пор, пока не будет вычислено выражение, ответственное за построение вложенного списка для ширины. Это выражение представляет собой следующее:
(cons 40 (* HalfWidth 2.0))
Если значение переменной HalfWidth было установлено равным 2, то в результате вычислений выражение должно возвращать в окне контрольных значений (40 . 4.0).
Шаг с выходом из функции gp:getPointInput и переход к C:Gpmain
Необходимо пояснить еще один момент: что происходит со значениями локальных переменных при выходе из функции gp:getPointInput.
Для выхода из функции gp:getPointInput и перехода к c:gpath
1. Нажмите кнопку «Шаг с выходом».
VLISP переходит к самому концу функции gp:getPointInput и останавливается непосредственно перед выходом.
2. Нажмите кнопку «Шаг с заходом».
Управление передается функции c:gpmain, из которой функция gp:getPointInput была вызвана перед этим.
Проверим значения переменных в окне контрольных значений. Переменные endpt и StartPt являются локальными для функции gp:getPointInput и получают значение nil. VLISP автоматически освобождает память, занимаемую этими переменными. Третья локальная переменная HalfWidth также должна была бы принять значение nil, однако в ходе отладки ее значение было глобально заменено из окна консоли, поэтому в окне «Контрольное значение» для этой переменной остается значение 2.0. Переменная *Last-Value* выводит ассоциативный список, созданный функцией gp:getPointInput.
Наш первый сеанс отладки завершен. При этом не следует забывать, что программа все еще находится в состоянии останова.
Для завершения занятия
1. Нажмите кнопку «Продолжить» на панели «Отладка». Ответьте на запросы. Это приводит к завершению работы программы.
2. Выберите «Отладка» >> «Удалить все точки останова» из меню VLISP. Ответьте «да» на запрос подтверждения. Все точки останова удаляются из текста программы.
Необходимо запомнить, что для удаления отдельной точки останова следует поместить курсор в ее позицию в тексте программы и нажать кнопку «Точка останова Вкл/Откл».
Итоги занятия 2
На этом занятии были рассмотрены:
• Локальные и глобальные переменные.
• Задание и удаление точек останова.
• Пошаговое выполнение программы.
• Контроль и динамическое изменение значений переменных в процессе выполнения программы.
• Сброс значений локальных переменных до nil после завершения выполнения функции, в которой они определены.
Программисты, планирующие разрабатывать приложения на AutoLISP, смогут убедиться, что инструменты, описанные на этом занятии, незаменимы в ежедневной работе.
Построение границы дорожки
На этом занятии программа обретает возможность выполнять несложные построения в AutoCAD, заключающиеся в создании границы парковой дорожки в виде полилинии. Для построения границы необходимо предварительно написать несколько служебных функций, которые в дальнейшем могут быть применены и в других приложениях. Здесь также рассмотрено создание функций, ожидающих передачи данных извне, а также объяснено, почему использование аргументов является важным принципом программирования. К концу занятия программа начинает параметрически (т.е. автоматически на основе уникальных параметров, заданных пользователем) строить объекты AutoCAD.
Использование служебных функций
Служебные функции выполняют задачи, общие для различных создаваемых программистом приложений. Такие функции служат своеобразной библиотекой и могут использоваться снова и снова.
Служебные функции необходимо тщательно документировать. В комментариях следует также указывать, каким образом можно усовершенствовать функцию в будущем, если позволит время.
Перевод градусов в радианы
Создадим функцию, которая в дальнейшем позволит не набирать многократно одну и ту же формулу. Она выглядит следующим образом:
(defun Degrees->Radians (numberOfDegrees)
(* pi (/ numberOfDegrees 180.0)))
Функция называется Degrees->Radians и служит для перевода углов, выраженных в градусах, в радианы.
Для чего необходимо переводить угловые величины в радианы? Дело в том, что AutoCAD при работе с углами оперирует радианами, а большинство людей привыкли измерять углы в градусах. Благодаря этой функции пользователь может вводить значения углов в градусах, а AutoLISP сам преобразует их в радианы.
Для проверки работы служебной функции
1. В ответ на подсказку в окне консоли VLISP введите следующее:
(defun Degrees->Radians (numberOfDegrees)
(* pi (/ numberOfDegrees 180.0)))
2. В ответ на подсказку в окне консоли VLISP введите следующее:
(degrees->radians 180)
Функция возвращает число 3.14159. Таким образом 180 градусов преобразуются в 3.14159 радиан. Не правда ли, это число что-то напоминает? Ну, конечно же — это число «пи».
Для того, чтобы использовать функцию в программе, следует просто скопировать ее из окна консоли в файл gpmain.lsp. Функцию можно вставить в любое место файла; но, естественно, не в код какой-либо уже имеющейся функции.
Правильно отформатировать вставленный текст можно с помощью кнопки панели VLISP «Форматирование выделенного фрагмента».
Теперь добавим комментарии, описывающие функцию. После документирования код функции должен выглядеть примерно так:
;;;--------------------------------------------------------------;
;;; Function: Degrees->Radians ;
;;;--------------------------------------------------------------;
;;; Description: This function converts a number representing an ;
;;; angular measurement in degrees, into its radian ;
;;; equivalent. There is no error checking on the ;
;;; numberOfDegrees parameter -- it is always ;
;;; expected to be a valid number. ;
;;;--------------------------------------------------------------;
(defun Degrees->Radians (numberOfDegrees)
(* pi (/ numberOfDegrees 180.0))
)
Преобразование 3М точек в 2М
Другая полезная функция, которая потребуется в программе построения парковой дорожки, должна преобразовывать 3М точки в 2М точки. AutoCAD обычно работает с 3М координатами, однако некоторые объекты (например, компактные полилинии) по определению являются двумерными. Функция getpoint возвращает 3М точки, поэтому для их преобразования требуется создать специальную функцию.
Для преобразования 3М точек в 2М
1. В ответ на подсказку в окне консоли введите следующее:
(defun 3dPoint->2dPoint (3dpt)(list (car 3dpt) (cadr 3dpt)))
2. Выполним функцию. Для этого в ответ на подсказку в окне консоли введите:
(3dpoint->2dpoint (list 10 20 0))
Функция работает; однако, для создания приложения, строящего парковую дорожку, следует учитывать еще одно обстоятельство. Для функций LISP чаще всего безразлично, является ли аргумент целым или вещественным числом. Но совсем по другому обстоит дело с функциями ActiveX, которые будут использованы позднее на этом занятии. Функции ActiveX работают с вещественными числами. Нашу функцию можно легко модифицировать так, чтобы она всегда возвращала вещественные числа, а не целые.
3. В ответ на подсказку в окне консоли введите следующее:
(defun 3dPoint->2dPoint (3dpt)(list (float(car 3dpt))
(float(cadr 3dpt))))
4. Снова запустите функцию:
(3dpoint->2dpoint (list 10 20 0))
Следует обратить внимание на то, что возвращаемые значения теперь являются вещественными числами (т.е. записываются с десятичной точкой).
5. Снова проверим работу функции, на этот раз с помощью функции getpoint. В ответ на подсказку в окне консоли введите следующее:
(setq myPoint(getpoint))
6. Укажите точку в графическом окне AutoCAD.
Функция getpoint возвращает 3М точку.
7. В ответ на подсказку в окне консоли введите следующее:
(3dPoint->2Dpoint myPoint)
На этот раз возвращается 2М точка.
Теперь добавим функцию в файл gpmain.lsp (точно так же, как это было сделано с функцией Degrees->Radians). Новый код должен выглядеть следующим образом:
;;;--------------------------------------------------------------;
;;; Function: 3dPoint->2dPoint ;
;;;--------------------------------------------------------------;
;;; Description: This function takes one parameter representing a;
;;; 3D point (list of three integers or reals), and ;
;;; converts it into a 2D point (list of two reals).;
;;; There is no error checking on the 3D point ;
;;; parameter -- it is assumed to be a valid point. ;
;;;--------------------------------------------------------------;
;;; To do: Add some kind of parameter checking so that this ;
;;; function won't crash a program if it is passed a ;
;;; null value, or some other kind of data type than a ;
;;; 3D point. ;
;;;--------------------------------------------------------------;
(defun 3dPoint->2dPoint (3dpt)
(list (float(car 3dpt)) (float(cadr 3dpt)))
)
Обратите внимание, что заголовок содержит указания о дальнейшей доработке данной функции. Для того чтобы приобрести дополнительный опыт, можно подумать о том как усовершенствовать функцию, чтобы защитить функцию от аварийного завершения из-за неверного ввода данных.
Совет: используйте функции numberp и listp...
(listp '(1 1 0)) => T
(numberp 3.4) => T
Создание графических объектов в AutoCAD
В большинстве программ на AutoLISP для создания объектов используются один из следующих методов:
• Функции ActiveX
• Функция entmake
• Функция command
На этом занятии объясняется построение объектов с помощью ActiveX. На пятом занятии будут использоваться функция entmake и команды AutoCAD.
Создание объектов с помощью функций ActiveX
Создание объектов в VLISP с помощью функций ActiveX является самым современным из имеющихся методов. ActiveX обладает рядом преимуществ по сравнению с использованием функций entmake и command.
• Функции ActiveX работают быстрее.
• Имена функций ActiveX обозначают действие, которые они выполняют, что обеспечивает более удобное чтение, обновление и исправление программы.
Пример функции ActiveX приводится ниже в тексте этого же занятия.
Создание объектов с помощью entmake
Функция entmake собирает в ассоциативный список информацию о координатах и ориентации, слое и цвете объекта, а затем передает все это в AutoCAD для построения. Ассоциативный список функции entmake очень похож на список, получаемый функцией entget. Различие заключается в том, что entget возвращает информацию об объекте, а entmake по имеющимся данным строит новый объект.
Создание объектов из командной строки AutoCAD
Когда AutoLISP впервые был встроен в AutoCAD, единственным средством для создания объектов была функция command. Она позволяет программисту вызывать из программы на AutoLISP практически любую команду, которая может быть выполнена из командной строки AutoCAD. Это—надежный способ, но он не настолько быстр, как функции ActiveX, и не обеспечивает такой же гибкости, как entmake.
Создание функции построения границы дорожки
После выполнения упражнений предыдущих занятий функция gp:drawOutline выглядела следующим образом:
;;;--------------------------------------------------------------;
;;; Function: gp:drawOutline ;
;;;--------------------------------------------------------------;
;;; Description: This function draws the outline of the ;
;;; garden path. ;
;;;--------------------------------------------------------------;
(defun gp:drawOutline ()
(alert
(strcat "This function will draw the outline of the polyline "
"\nand return a polyline entity name/pointer."
)
)
;; For now, simply return a quoted symbol. Eventually, this
;; function will return an entity name or pointer.
'SomeEname
)
В том виде, как она есть, функция не выполняет практически никаких действий. Однако, ассоциативный список, хранящийся в переменной gp_PathData, позволяет рассчитать все характерные точки, необходимые для построения контура дорожки. Теперь определим, каким образом информация из этой переменной будет передаваться в функцию gp:drawOutline.
Следует помнить, что переменная gp_PathData является локальной и определена внутри функции C:GPath. В AutoLISP локальные переменные, описанные в одной функции, доступны всем функциям, вызываемым из нее (подробнее см. раздел «Различия между локальными и глобальными переменными» ). Функция gp:drawOutline вызывается из C:GPath. Поэтому переменную gp-PathData можно использовать и для функции gp:drawOutline; однако это не является наилучшим решением.
Почему так? Если одна и та же переменная используется только двумя функциями, описанными в одном файле (как в приведенном примере), установить, где была описана и для чего используется переменная, нетрудно. Однако, если таких функций много и они располагаются в различных файлах (как часто случается), потребуется много усилий, чтобы выяснить, что представляет собой переменная gp_PathData.
Передача параметров функциям
Лучший способ передать информацию из одной функции в другую — это передать вызываемой функции параметры (аргументы). Разработаем функцию таким образом, чтобы она ожидала передачи аргументов. Вспомним функцию Degrees->Radians. Ей передавался параметр numberOfDegrees:
(defun Degrees->Radians (numberOfDegrees)
(* pi (/ numberOfDegrees 180.0)))
При вызове функции необходимо передать ей число. Число внутри функции Degrees->Radians объявляется параметром, называемым numberOfDegrees. Например:
_$ (degrees->radians 90)
1.5708
В данном случае параметру numberOfDegrees присваивается число 90.
Можно также передать функции аргумент, являющийся переменной. Например, пусть имеется переменная aDegreeValue. Следующие функции присваивают ей значение 90 и передают его функции Degrees->Radians:
_$ (setq aDegreeValue 90)
90
_$ (degrees->radians aDegreeValue)
1.5708
Работа с ассоциативным списком
Ассоциативный список, содержащийся в переменной gp_PathData, можно передать функции gp:drawOutline следующим образом:
(gp:drawOutline gp_PathData)
Это достаточно просто. Однако в тексте функции необходимо указать, каким образом информация из ассоциативного списка должна обрабатываться. Выяснить структуру списка можно с помощью средства VLISP «Изучить».
Для анализа ассоциативного списка с помощью средства VLISP «Изучить»
1. Загрузите код из окна редактора.
2. В ответ на подсказку в окне консоли введите следующее выражение:
(setq BoundaryData (gp:getPointInput))
VLISP будет записывать информацию в переменную BoundaryData.
3. Ответьте на запросы о начальной и конечной точках и полуширине.
4. Выберите имя переменной BoundaryData в окне консоли, дважды щелкнув на нем мышью.
5. Выберите «Вид» >> «Изучить» из меню VLISP.
VLISP выводит следующее окно:

В окне «Изучение» выводится каждый вложенный список переменной BoundaryData.
6. В ответ на подсказку в окне консоли VLISP введите следующее:
(assoc 50 BoundaryData)
Функция assoc возвращает элемент ассоциативного списка, обозначенный определенным кодом. В данном примере указан код 50, соответствующий углу наклона парковой дорожки (см. раздел «Применение ассоциативных списков» ).
7. В ответ на подсказку в окне консоли VLISP введите следующее:
(cdr(assoc 50 BoundaryData))
Функция cdr возвращает второй и все следующие за ним элементы списка. В данном примере cdr находит значение угла, которое является вторым и последним элементом значения, возвращаемого функцией assoc.
После этих объяснений при понимании следующего фрагмента кода затруднения возникать не должны:
(setq PathAngle (cdr (assoc 50 BoundaryData))
Width (cdr (assoc 40 BoundaryData))
HalfWidth (/ Width 2.00)
StartPt (cdr (assoc 10 BoundaryData))
PathLength (cdr (assoc 41 BoundaryData))
Использование углов и задание точек
Есть еще пара вопросов, которые требуется рассмотреть. Во-первых, необходимо установить, каким образом строится дорожка под заданным пользователем углом. Из функции gp:getPointInput можно легко получить исходный угол наклона дорожки. Для построений нужно рассчитать два вектора, перпендикулярных этому углу.

Здесь пригодится функция Degrees->Radians. В следующем фрагменте кода выполняется расчет двух перпендикулярных векторов; при этом переменная PathAngle передается в качестве аргумента функции Degrees->Radians:
(setq angp90 (+ PathAngle (Degrees->Radians 90))
angm90 (- PathAngle (Degrees->Radians 90)))
Имеющиеся данные позволяют найти с помощью функции polar четыре угловые точки дорожки.

(setq p1 (polar StartPt angm90 HalfWidth)
p2 (polar p1 PathAngle PathLength)
p3 (polar p2 angp90 Width)
p4 (polar p3 (+ PathAngle (Degrees->Radians 180))
Функция polar возвращает 3М точку, расположенную под заданным углом и на заданном расстоянии от исходной точки. Например, точку p2 функция polar находит, откладывая от точки p1 расстояние, равное PathLength, вдоль вектора, повернутого на угол PathAngle против часовой стрелки относительно оси X.