Связка AutoCAD+Delphi. Рабочий пример простого приложения

Источник: dwg
Валерий Кострецкий

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

Более практичным решением этой задачи является использование связки AutoCAD и системы программирования Delphi, где AutoCAD выполняет роль сервера, а программа-приложение - роль клиента.
В статье рассматривается пример такой связки. Это простое приложение к AutoCAD, имеющее интерфейс обычных Windows-приложений. 

art4_1



Оно запускается из AutoCAD, после ввода параметров формирует внешний текстовой INI-файл и загружает LISP-программу, которая отрисовывает деталь по данным из текстового файла, в нашем примере - трехмерную пружину. 

art4_2



По сравнению с технологией черчения из Delphi напрямую, без промежуточных файлов, описываемый метод имеет свои положительные особенности.
Во-первых, с минимальными переделками можно использовать наработанные библиотеки LISP-программ, которые отлично "заточены" под AutoCAD, к тому же код LISP-программы намного проще и короче аналогичного кода на Object Pascal.
Во-вторых, в приложении основную работу выполняет LISP-программа с открытым кодом, она доступна для настройки под определенную задачу, а редактирование откомпилированного EXE-файла уже невозможно при отсутствии исходного кода.
В-третьих, внешние текстовые INI-файлы можно использовать как простые и удобные базы данных для записи координат точек элементов детали, информации о материале, цвете, стоимости и других данных.

Разработка приложения.


Все файлы приложения удобнее размещать в одной директории, в нашем примере это будет C:\Unit_1. Сюда мы поместим два файла helix.exe (исполняемый файл приложения) и helix.lsp (LISP-программа), сюда же будет записываться и промежуточный файл helix.ini.
Создание приложения начнем с построения его оболочки в Delphi.
На форме размером 440х580 pix размещаются следующие компоненты:

  • Image1 для загрузки иллюстрации-подсказки (файл JPG размером 300х350 pix);
  • четыре текстовых поля Edit1, Edit2, Edit3, Edit4 для ввода параметров пружины - высоты, радиуса, шага и толщины витка;
  • переключатель RadioGroup1 для изменения направления витков (по часовой или против часовой стрелки, если смотреть сверху);
  • две кнопки Button1 и Button2 - одна из них запускает построение детали, другая закрывает приложение.

Можно было бы предусмотреть построение технологически более сложных пружин растяжения и сжатия, но для понимания принципа работы приложения достаточно будет самой простой пружины-спирали.
Перед разработкой приложения в Delphi следует импортировать библиотеку типов AutoCAD:
Пункт меню Project - Import Type Library - Add, указать файл C:\Program Files\Autodesk Shared\acax16enu.tlb. В окне Import Type Library выбрать строку AutoCAD (Version 1.1) и нажать кнопку Instal.

Теперь все готово для разработки приложения. Установите на форму указанные выше компоненты, свойству ItemIndex переключателя RadioGroup1 присвойте значение 0, удалите исходный текст из текстовых полей, свойствам Caption кнопок Button1 и Button2 присвойте значение "ПОСТРОИТЬ" и "ЗАКРЫТЬ".
Перейдем к редактированию кода. В раздел USES необходимо добавить IniFiles, ActiveX,ComObj, Jpeg, в раздел объявления переменных var Form1: TForm1; добавить ModelSpace, Acad: OleVariant;
Все следующие действия для лучшего понимания можно разбить на три этапа.

Этап1.Формирование текстового файла - базы данных детали.

Основную работу приложения выполняет процедура, обрабатывающая событие Button1Click - щелчек на кнопке "ПОСТРОИТЬ". В раздел описания переменных процедуры TForm1.Button1Click запишем новую переменную rec для работы с файлом

var
rec: TIniFile;

Далее в указанной директории (в нашем примере - 'C:\Unit_1') создается INI-файл helix.ini, в который с помощью процедуры WriteString происходит построчная запись пареметров детали.

rec:=TIniFile.Create('C:\Unit_1\helix.ini'); 
rec.WriteString('Размеры пружины','R',Edit1.Text);
rec.WriteString('Размеры пружины','Step_v',Edit2.Text);
rec.WriteString('Размеры пружины','H',Edit3.Text);
rec.WriteString('Толщина профиля','RP',Edit4.Text);
rec.WriteString('Направление витков','NV',IntToStr(RadioGroup1.ItemIndex));

В INI-файл в секциях записываются значения радиуса и высоты пружины, толщины ее профиля, которые считываются из соответствующих текстовых полей Edit1, Edit2, Edit3 и Edit4. В последней строке записывается признак направления витков пружины (0 - против и 1 - по часовой стрелке). В конце записи закрывается сеанс работы с файлом:

rec.Free;

После записи INI-файл будет иметь примерно такой вид:

[Размеры спирали]
R=60
Step_v=20
H=200
[Толщина профиля]
RP=10
[Направление витков]
NV=0

Обратите внимание на соотношение двух параметров: вертикального шага витка Step_v и толщины профиля RP. Когда толщина профиля будет больше, чем половина шага, то витки сольются и деталь уже не будет выглядеть пружиной, а превратится в резьбу с круглым профилем, эту ситуацию можно использовать для построения резьбовых деталей.
Следующее действие - проверка, запущен AutoCAD или нет. Если нет - запускается, если да - посылается команда на загрузку LISP-файла.

try
Acad := GetActiveOleObject('AutoCAD.Application');
except
Acad := CreateOleObject('AutoCAD.Application');
end;
Acad.Visible := True;
ModelSpace := Acad.ActiveDocument.ModelSpace;
Acad.ActiveDocument.SendCommand('(load "C:\\Unit_1\\helix.lsp")' + #13);

Команду на загрузку LISP-программы helix.lsp мы посылаем в AutoCAD как строковую переменную. Обратите внимание на то, что в Object Pascal строковая переменная ограничивается одиночными кавычками, а в AutoLISP - двойными, а при указании пути к файлу в AutoLISP используется обратный двойной слэш, в отличие от двойного слэша в комментариях программ на Pascal, эти особенности синтаксиса разных программ нужно иметь ввиду. AutoCAD требует в завершении ввода команды нажать Enter, поэтому в строчку добавляется код этой клавиши #13. Если LISP-программа оформлена как функция, то после ее загрузки посылается команда на выполнение этой функции

Acad.ActiveDocument.SendCommand('helix' + #13);

В коде программы нашего приложения приложения есть еще две процедуры. Одна из них TForm1.Edit1KeyPress защищает от ошибок при вводе данных. Она позволяет вводить в цифровые поля только цифры от 0 до 9 и точку - десятичный разделитель:

if not (Key in ['0'..'9','.']) then Key:=#0; 

Другая процедура (Button2Click) просто закрывает приложение без построения детали. Ниже приведет полный листинг программы, добавленные нами операторы подчеркнуты.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls, IniFiles, ActiveX,ComObj, Jpeg;

type
  TForm1 = class(TForm)
    Label1: TLabel;
    Edit1: TEdit;
    Label2: TLabel;
    Edit2: TEdit;
    Label3: TLabel;
    Edit3: TEdit;
    Button1: TButton;
    Button2: TButton;
    Edit4: TEdit;
    Label4: TLabel;
    RadioGroup1: TRadioGroup;
    Image1: TImage;
    procedure Button2Click(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Edit1KeyPress(Sender: TObject; var Key: Char);

  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  ModelSpace, Acad: OleVariant;

implementation

{$R *.DFM}


procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
if not (Key in ['0'..'9','.']) then Key:=#0;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
rec: TIniFile;
begin
rec:=TIniFile.Create('C:\Unit_1\helix.ini'); 
rec.WriteString('Размеры пружины','R',Edit1.Text);
rec.WriteString('Размеры пружины','Step_v',Edit2.Text);
rec.WriteString('Размеры пружины','H',Edit3.Text);
rec.WriteString('Толщина профиля','RP',Edit4.Text);
rec.WriteString('Направление витков','NV',IntToStr(RadioGroup1.ItemIndex));
rec.Free;
try
Acad := GetActiveOleObject('AutoCAD.Application');
except
Acad := CreateOleObject('AutoCAD.Application');
end;
Acad.visible := True;
ModelSpace := Acad.ActiveDocument.ModelSpace;
Acad.ActiveDocument.SendCommand('(load "C:\\Unit_1\\helix.lsp")' + #13);
Acad.ActiveDocument.SendCommand('helix' + #13);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
Close;
end;
end.

Далее запускается LISP-программа, под управлением которой AutoCAD отрисовывает деталь.

Этап 2. Передача данных из текстового файла в LISP-программу.

Так как LISP-программа построения пружины приводится в качестве примера и на ее месте может быть любая другая, то нет необходимости детально описывать ее работу, особенно "рисующей" части. Подробнее становимся только на той части программы, которая считывает данные из INI-файла и инициализирует переменные.
Строка
(setq f (open "C:\\Unit_1\\helix.ini" "R"))
создает файловую переменную f и связывает с ней открытый для чтения ("R") INI-файл. Затем следуют два цикла: считывания строк в переменную str (while lin ... (setq str (read-line f)) ...) и вложенный цикл посимвольного чтения каждой строки (while rep...(substr str i 1)...).
Для построения детали из INI-файла нужны только те данные, которые стоят справа от знаков равенства, все остальное можно пропустить. Поэтому для выделения нужных данных вводятся два условия.
Первое условие - в случае если ASCII код первого символа строки равен 91, а это соответствует символу "[", означающему начало секции, то чтение этой строки прекращается (setq rep nil).
Второе условие - если ASCII код очередного символа равен 61, то это означает, что найден знак равенства "=" и все, что последует за ним - необходимые для построения детали данные, их следует извлечь в переменные. Для этого из строки выделяется подстрока из четырех (в нашем примере достаточно четырех, но в другой ситуации это число может быть другим) символов, начиная со следующего после знака равенства символа (substr str (+ i 1) 4), эта подстрока из строковой переменной преобразуется в действительное число функцией atof и его значение присваивается
соответствующим переменным, означающим тот или иной размер детали, при завершении всех циклов INI-файл закрывается строкой (close f).
После окончания сеанса работы с приложением INI-файл остается в указанноей директории. В последующих сеансах он перезаписывается под тем же именем с другими данными. Можно предусмотреть удаление этого файла после каждого сеанса работы, но лучше его оставлять для анализа полученных результатов.
Теперь у AutoCAD есть почти все данные для отрисовки пружины. Только добавляются еще несколько переменных
(setq a_gr 10.0 p1 (list R 0.0 0.0) z 0.0),
смысл которых будет пояснен ниже.

Этап 3. Построение пружины.

В нашем примере пружина будет строиться экструдированием окружности вдоль спирального пути, построенном трехмерной полилинией (3DPoly).
Сложность состоит в том, что такая полилиния не позволяет создать кривизну, поэтому каждый виток спирали разбивается на короткие прямолинейные участки и при большом количестве таких участков спираль выглядит плавной. В нашем примере окружность витка разбита на 36 участков по 10 градусов каждый (setq a_gr 10.0). Увеличение количества участков приводит к более плавной поверхности пружины, но потребует большего времени отрисовки.
Спираль будет строиться вращением конца трехмерной полилинии вверх вокруг оси Z против часовой стрелки (NV=0 по умолчанию) с приращением Step_v на каждый виток. Стартовая точка полилинии p1 имеет координаты p1 (list R 0.0 0.0), это означает, что она лежит в плоскости XY (z=0) по оси X на расстоянии R от вертикальной оси. Параметр R - это первый из тех, что были указаны в текстовых полях нашего приложения.
Далее функцией command вызывается команда рисования трехмерной полилинии

(command "_3Dpoly" p1). 

AutoCAD отмечает первую точку p1 и ожидает координаты следующей, которые будут ему передаваться в цикле while (< z H) - до тех пор, пока текущая высота спирали z не сравняется с параметром H. Следующая точка находится из простых геометрических построений, если рассматривать треугольник, объединяющий обе точки и начало координат

(setq x (* R (cos a_rad)) y (* R (sin a_rad))), 

предварительно угол поворота из градусов переводится в радианы

(setq a_rad (* pi (/ a_gr 180.0))). 

И, наконец, добавляется приращение по координате Z, расчитанное как вертикальный шаг витка Step_v, разбитый на 36 участков

z (+ z (/ Step_v 36)). 

Все три координаты текущей точки передаются в графический редактор AutoCAD через команду

(command (list x y z)), 

после чего следует расчет новой точки и так до конца цикла. Чтобы угол поворота на накапливался до больших значений, после каждого оборота его следует сбрасывать до исходного

(if (> a_gr 360)
(setq a_gr 10)
..)

Когда цикл построения спирали завершен, ей, как последнему неудаленному графическому примитиву присваивается системное имя

(setq helix (entlast)),

на которое будет ссылка при указании пути экструдирования. Далее создается ПСК и устанавливается в стартовую точку спирали с поворотом на 90 градусов по оси Х

(command "._ucs" "m" p1)
(command "._ucs" "n" "x" "90")

В этой точке строится окружность радиуса RP перпендикулярно направлению оси спирали

(command "._circle" "0,0,0" RP),

этой окружности присваивается имя

(setq profil (entlast))

и восстанавливается МСК

(command "._ucs" "w").

Последним решающим действием является собственно построение пружины командой

(command "._extrude" profil "" "p" helix).


После отрисовки пружины можно опять строить пружину с новыми параметрами или закрыть приложение и полностью перейти в AutoCAD.
Ниже приводится полный листинг LISP-программы.

(defun C:helix (/ f k lin str i rep lt k R Step_v H RP NV a_gr a_rad p1 R os x y z helix profil )
(setq f (open "C:\\Unit_1\\helix.ini" "R"))
(setq k 0 lin t
)
  (while lin
    (setq str (read-line f))
    (setq rep t  i 1
    )
    (while rep
      (setq lt (substr str i 1))
      (cond
	((= (ascii lt) 91) (setq rep nil))
	((= (ascii lt) 61)
	 (progn
	   (setq k (+ k 1))
	   (cond ((= k 1) (setq R (atof (substr str (+ i 1) 4))))
		 ((= k 2) (setq Step_v (atof (substr str (+ i 1) 4))))
		 ((= k 3) (setq H (atof (substr str (+ i 1) 4))))
		 ((= k 4) (setq RP (atof (substr str (+ i 1) 4))))
		 ((= k 5) (setq NV  (atof (substr str (+ i 1) 4))
			        lin nil
		  )
		 )
	   );cond
	   (setq rep nil)
	 );progn
	)
      );cond
      (setq i (+ i 1))
    );while rep
  );while_lin
  (close f)
  (setq a_gr 10.0 p1 (list R 0.0 0.0) z 0.0
  )
  (setq os (getvar "osmode"))
  (setvar "osmode" 0)
  (setvar "cmdecho" 0)

  (command "_3Dpoly" p1)
  (while (< z H)    
    (setq a_rad (* pi (/ a_gr 180.0)) 
          x (* R (cos a_rad))
	      y (* R (sin a_rad))
	      z (+ z (/ Step_v 36))
    )
    (if (= NV 1)
      (setq y (* -1.0 y))
    );if

    (command (list x y z))
    (setq a_gr (+ a_gr 10))
    (if (> a_gr 360)
      (setq a_gr 10)
    )
  );while
  (command "")
  (setq helix (entlast))
  (command "._ucs" "m" p1)
  (command "._ucs" "n" "x" "90")
  (command "._circle" "0,0,0" RP)
  (setq profil (entlast))
  (command "._ucs" "w")
  (command "._extrude" profil "" "p" helix)
  (setvar "osmode" os)
);defun

Когда приложение отлажено и доведено до рабочего состояния его можно запускать непосредственно из AutoCAD кнопкой с макросом

^C^C_start;C:/Unit_1/helix.exe;

Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=30838