Некоторые приемы программирования на AutoLisp


Нет необходимости объяснять человеку, работающему с AutoCAD, преимущества программирования в его среде на Lisp [1]. Однако и здесь есть свои особенности, не всегда известные и применяемые.

Известно, что CAD - это область, требующая больших затрат ресурсов персональных компьютеров и использующая их возможности на приделе. Для того, чтобы это понять достаточно выполнить команду HIDE (удалить скрытые линии) с чертежом site3d который поставляется с AutoCAD в качестве примера. Хорошей чертой системы AutoCAD является ее инвариантности к размерам проекта. Это означает что AutoCAD написан так, что может оперировать чертежом практически любого объема, лимитируемого только возможностями аппаратных средств. Однако при возрастании размеров чертежа, растет не только память, занимаемая DWG - файлом но и время выполнения каждой операции выбора объекта Select object: - "время ответа".

Одним из факторов повышения производительности AutoCAD является обьем оперативной памяти. Разумеется должно быть 640 К, вопрос на сколько больше?

Дополнительная память AT Extended Memory может использоваться AutoCAD как внутренняя буферная область при обмене с чертежным файлом в процессе его редактирования [1]. Обеспечение доступа к ней осуществляется установкой переменных окружения:

SET  ACADXMEM = xxxx K

Таким образом AutoCAD может использовать до 16 Мбайт памяти. Написанные на AutoLISP Средства повышения производительности труда типа "АРМ - КОНСТРУКТОРА МАШИНОСТРОИТЕЛЯ" [2] как правило не могут разместится в 40 кб heap простого AutoLisp, что приводит к необходимости использования расширенного AutoLisp. В этом случае выделенный обьем памяти драматически влияет на скорость выполнения и загрузки программ автоматизированного черчения.

Из опыта можно заключить что выполнение более или менее серьезных Lisp систем ассистирующих чертежнику требует как минимум 1 Мбайт Extended Memory. Так как модуль расширенного AutoLisp extlisp.exe загружается перед загрузкой AutoCAD он должен оставить часть ExtMemory свободной для AutoCAD. Эту информацию Extlisp.exe можно передать предварительно установив переменную:

SET LISPXMEM = yyyy K

что предписывает extlisp использовать не более yyyy Kбайт памяти[3]. Иногда для ускорения работы пытаются разместить на виртуальном диске рабочий DWG-файл и временные файлы. Не стоит отдавать этому предпочтение перед памятью для буферной области, так как в последнем случае ускорение работы получается более ощутимое. И уж тем более нет смысла помещать рабочий файл на "виртуалку" когда вы используете буферную область. Внутренний буфер обеспечивает наибольшее ускорение, если же остается какая-то часть памяти свободной - расположите на ней оверлеи системы AutoCAD.

Как правило разработчики программного обеспечения как системные так и прикладные хотят защитить свои оригинальные идеи и программы. Для защиты программ на AutoLisp в 10-й версии AutoCAD применяются следующие средства [4]:

Неудобство при работе с AutoLISP вызывает неразвитость средств отладки программ. По этой причине нами был разработан ряд сервисных средств:

  1. Библиотека сервисных функций на AutoLisp для применения в прикладных программах ( исходные тексты, встраиваемые в исходную программу). Пример (упрощенное применение команды "COLOR"):

    		     (defun $c( x$y$z / )
    		     (command "COLOR" x$y$z )
    		     )
    
  2. Расширитель командных файлов DOS позволяющий автоматизировать любые низкопроизводительные операции над исходными текстами прог- рамм, к примеру - добавление типовых функций в программу.
  3. AutoLisp Help - гипертекстовый справочник по AutoLisp.
  4. Программа проверки скобок.
  5. Программа автоматической генерации списка локальных переменных.
  6. Программа автоматического включения в исходный текст функций для которых возникала ситуация null function.
  7. Программа построения списка определенных в .LSP файле функций.
  8. Препроцессор для Lisp.
  9. Отладчик для AutoLisp.

Все эти программы были разработаны авторами в процессе создания большего программного комплекса [2.], и позволили быстро и качественно выполнить большой обьем работ по программированию.

Рассмотрим несколько примеров эффективного программирова- ния на AutoLisp:

а) Защита от runtime error: Fillet radius is to large:
; установка радиуса fillet - с защитой от  Fillet  radius  is  to
;large.
(if
(<=  (*  (min ($linlen lin11) ($linlen lin1))  0.95)
	       in_rm4	 )
(command "fillet"  "r" (* (min ($linlen lin11) ($linlen lin1)) 0.9) )
(command "fillet"  "r" in_rm4)
)

т. е. в случае если fillet радиус больше по размерам одной из сопрягаемых прямых то в качестве самого fillet радиуса устанавливаем значение 0.9 меньшей из длин сопрягаемых отрезков.

б) Установка пользовательской системы координат:
(command "UCS" "3" fploc sploc "")
	(setq fploc  (trans fploc 0 1))
	(setq sploc  (trans sploc 0 1))
(command "UCS" "World")

В данном примере реализован переход в Мировую систему координат где fploc и sploc - базовые точки привязки отрисовываемой детали к плоскости чертежа. Установка пользовательской системы координат, с последующим восстановлением мировой, позволяет все построения между этими двумя командами вести путем строго "прямоугольного" вычисления координат точек вида:

(setq  ...  (list  (+ (car ...) ...) (+ (car ...) ...) ) ) 

без использования функции polar. Такой прием повышает производительность труда программиста и снижает требования к его опыту и квалификации. Однако при обращении к нетривиальным подпрограммам построения графических элементов из функции переопределившей UCS могут потребоваться значительные усилия для организации правильной работы этих подпрограмм, в этом случае целесообразно использовать функцию trans.

г) Функция построения дуги стягивающей острый угол независимо от ориентации базовых точек:
(defun atrue( bp ep cp / tp tp1)
; проводит дугу стягивающую острый угол относительно центра cp
;  bp , ep - начальная и конечная точка
(setq tp1 (polar ep (angle ep bp) (/ (distance ep bp) 2.0)  ))
(setq tp (polar cp (angle cp tp1) (distance cp bp)  ))
(command "ARC" bp tp ep )
)
д) Проблемы при программном написании текста в чертеже.

Типичная ситуация ошибок заключается в нестабильной работе команды TEXT, которая сбивается иногда на одной из своих подсказок. Секрет этого заключается в фиксации высоты текста (Height). Высота текста задается либо предварительно при установке стиля текста командой STYLE, либо при выдаче самой команды TEXT познее. Если необходимо задавать значение высоты текста в самой команде TEXT, то предварительно в команде STYLE определяется высота текста равная 0.

	(command "STYLE" "RUSLAT" "RUSLAT"
		"0.0"	       ; - высота текста
		"1.0" "0" "N" "N"    )
затем следующая команда:
	(command  "TEXT"  "S" "RUSLAT"  "C"
		pttext   ; - начальная точка
		angtext  ; - угол поворота
		textval  ; - текстовая строка
	)

будет корректна во всех случаях.

е) Параметризация симметричной детали.

Естественно стремление запрограммировать только половину детали, а вторую получить зеркально. Однако как на AutoLisp сформировать набор выбора? Эта задача может быть решена двумя способами.
Во-первых:

(setq set (ssadd))	    ; создать набор
(setq label (entlast))      ; поставить метку в DWG

    .
    .	 (  здесь операторы рисования половины детали )
    .

(while (setq label (entnext label))
	(setq set (ssadd label set)) )  ; переписать в набор
; все что было отрисовано после метки
(command "mirror" set ""  p1 p2 "N" )

Здесь set - набор выбора, куда попадают все примитивы, отрисованные после маркирования базы данных DWG-файла меткой label, p1, p2 -крайние точки осевой линии.

Во-вторых, альтернативным способом является использование операторов вида:

(command "line" a1 a2 "") (setq  set  (ssadd
(entlast))) (command "line" a4 a3 "") (setq set (ssadd  (entlast)
set)) (command "line" a3 a2 "") (setq set (ssadd (entlast) set))

После выполнения этих операторов прямые a1, a2; a4, a3; a3, a2 оказываются в наборе выбора set с которым теперь можно выполнять зеркальное отображение.

Второй способ формирования набора выбора ( selection set ) существенно выгоднее с точки зрения быстродействия lisp-программы, хотя несколько громоздок.

е) Функция определения длинны текста.

Каждый кто знаком с системой AutoCAD знает, что введенная в чертеж текстовая строка рассматривается системой как единое целое, что не всегда удобно. Например, невозможно при черчении таблицы перенести один столбец правее командой MOVE. В такой ситуации может помочь программа разбиения текста на слова:

; функция определения длины текста
(defun    txtlen   (   EN   /	E1   ED   LENGTH   PT1   PT2  )
; (0562)46-33-48  Dniepropetrovsk city , Tkachenko V.B.
	(setq	ED   ( entget EN )  ) ;    entity info
		(if
			(= "TEXT" (cdr (assoc 0 ED) ) )
		(progn  ;if entity is text...
		(setq E1 ED) ;save a copy
		(setq ED (subst (cons 72 2) (assoc 72 ED) ED)	)
		;modify text to rt. justified
		(entmod ED)
		(setq ED (entget EN))
		;update entity details
		(setq PT1 (cdr (assoc 10 ED) ) )
		;get left endpt of modified text
		(setq PT2 (cdr (assoc 11 ED) ) )
		;get right endpt of modified text
		(setq LENGTH (distance PT1 PT2) )
		;calculate length of string
		(setq ED E1)
		;restore string justification
		(entmod ED)
		) ; progn
		) ; if
	(if (/= "TEXT" (cdr (assoc 0 ED) ) ) (setq LENGTH nil)  )
		(setq LENGTH LENGTH)
) ; end defun txtlen

(defun C:EXPLTXT(  /
	i   oldb lvar
	lname_nab  glk
	n_nab
	nab
	name_nab   )
(setq oldb (getvar "BLIPMODE"))
(setvar "BLIPMODE" 0)
(setq lvar (getvar "CMDECHO"))
(setvar "CMDECHO" 0)
(terpri)
(prompt "Program for exploding text string.")
(terpri)
(prompt "	 Tkachenko V.B., Dniepropetrovsk city.")
(prompt "	 tel: (0562) 46-33-48 ")(terpri)
(prompt " Press any key ... ")(read-char)
;  функция разбиения произвольной строки текста на слова
(setq nab (ssget)) ; получить набор выбора общим путем
(setq n_nab (sslength nab)) ; число элементов набора
(setq i 0)
(while (not (eq i n_nab))
	(setq name_nab (ssname nab i))
	; если name_nab - текст то разбить его
	(setq glk (txtlen name_nab))
	(SETQ lname_nab (entget name_nab))
	(cond (  (eq (cdr (assoc 0 lname_nab)) "TEXT")
		(rzb name_nab glk)
		)
	)
	(setq i (+ i 1))
)
(setvar "CMDECHO" lvar)
(setvar "BLIPMODE" oldb)
(setq lvar nil)
(prin1)
) ;   --------- defun mrazbt

(defun rzb ( in_xp_name glk / C41 C50 C7 DX gl_s_for_rzb X1 h i n newTXT
		newsost oldTXT point st st_time s_in  xp_name )
(setq gl_s_for_rzb 0)
; функция разбиения одной строки текста имя примитива которой
; (0562)46-33-48  Dniepropetrovsk city , V. Tkachenko
; передано в качестве параметра
; C50 - угол наклона	   h - высота текста
; C41 - степень растяжения
(SETQ XP_NAME (ENTGET IN_XP_NAME))
(setq st  (cdr (assoc 1  xp_name))  )
(setq C50 (cdr (assoc 50 xp_name))  )
(setq C41 (cdr (assoc 41 xp_name))  )
(setq X1  (cdr (assoc 10 xp_name))  )
(setq h   (cdr (assoc 40 xp_name))  )
(setq C7  (cdr (assoc 7  xp_name))  )
(setq n  (strlen st) )
(setq glk (/ glk n))  ; char width
(setq i 0)
(setq st_time "" )
	; reset input string
	(setq oldTXT (assoc 1 xp_name))
	(setq newTXT (cons  1 " "))
	(setq xp_name (subst  newTXT  oldTXT  xp_name))
	(entmod xp_name)
(setq continue T)
(while (eq continue T)
	(SETQ i (+ i 1))
	(setq s_in (substr st i 1))
	; FA table
	(if (and   (not (eq s_in " ") )
			(eq gl_s_for_rzb 0)
		)
		(progn
		(setq st_time (strcat st_time (substr st i 1))   )
		(setq newsost 1)
		)
	)
	(if (and   (not (eq s_in " ") )
			(eq gl_s_for_rzb 1)
		)
		(progn
		(setq st_time (strcat st_time (substr st i 1))   )
		(setq newsost 1)
		)
	)
	(if (and   (not (eq s_in " ") )
			(eq gl_s_for_rzb 2)
		)
		(progn
		(setq st_time "" )
		(setq st_time (strcat st_time (substr st i 1))   )
		(setq newsost 1)
		)
	)
	(if (and   (eq s_in " ")
			(eq gl_s_for_rzb 0)
		)
		(progn
		(setq newsost 2)
		)
	)
	(if (and   (eq s_in " ")
			(eq gl_s_for_rzb 1)
		)
		(progn
		; ---- выделить из буфера ----
		(setq DX  (* glk (1- (- i (strlen st_time)))   ))
		(setq point (polar X1 C50 DX))
		(command "copy" in_xp_name ""  X1  point)
		(setq time (entlast))
		(SETQ xp_time (ENTGET time))
		; замепнить текст во вновь созданной копии текста
			(setq oldTXT (assoc 1 xp_time))
		(setq newTXT (cons  1 st_time))
		(setq xp_time (subst  newTXT  oldTXT  xp_time))
		(entmod xp_time)
		(setq st_time "" )
		; ---- выделить из буфера ----
		(setq newsost 2)
		)
	)
	(if (and   (eq s_in " ")
			(eq gl_s_for_rzb 2)
		)
		(progn
		(setq newsost 2)
		)
	)
	(if (and   (> i n)
			(eq gl_s_for_rzb 1)
		)
		(progn
		; ---- выделить из буфера ----
		(setq DX  (* glk (1- (- i (strlen st_time)))   ))
		(setq point (polar X1 C50 DX))
		(command "copy" in_xp_name ""  X1  point)
		(setq time (entlast))
		(SETQ xp_time (ENTGET time))
		; замепнить текст во вновь созданной копии текста
		(setq oldTXT (assoc 1 xp_time))
		(setq newTXT (cons  1 st_time))
		(setq xp_time (subst  newTXT  oldTXT  xp_time))
		(entmod xp_time)
		(setq st_time "" )
		; ---- выделить из буфера ----
		(setq newsost 3)
		)
	)
	(if (and   (> i n)
			(eq gl_s_for_rzb 0)
		)
		(progn
		(setq newsost 3)
		)
	)
	(if (and   (> i n)
			(eq gl_s_for_rzb 0)
		)
		(progn
		(setq newsost 3)
		)
	)
(setq gl_s_for_rzb newsost)
(TERPRI)
(PRINC GL_S_FOR_RZB)
(if (eq gl_s_for_rzb 3) (setq continue F))
)  ; while
(command "ERASE" in_xp_name "")
)

Рассмотренныые примеры программирования на AutoLisp возникли как результат поиска эффективных приемов при разработке программных продуктов в среде AutoCAD и являются средством повышения производительности труда разработчиков АРМ. Очевидно, что перечень таких професиональных приемов может быть расширен.

Список литературы:
  1. АВТОКАД версия 10, руководство пользователя. Autodesk LTD, 1989.
  2. АРМ для инженеров-машиностроителей, журнал "Компьютер - пресс" 11/90.
  3. АВТОЛИСП версия 10, руководство по программированию. Autodesk LTD, 1989.
  4. О. И. Иванов, А. А. Чайкин, В. Н. Шевченко. Язык программирования AutoLISP, release 10, 11. TRINIKA Ltd, Москва 1992.


Copyright © ЧП "Компьютер-графика", 1997-2010