Объектная модель AutoCad применительно к языку программирования Delphi

Данная статья подготовлена в качестве ознакомительного материала объектной модели AutoCAD.

Вкратце, для непосвящённых, объектная модель AutoCad - это структура объектов: приложения AutoCad, его документов (открытых чертежей), объектов черчения (точки, линии и т.п.), словарей (смотри документацию по AutoLisp), их свойств и методов. Все эти объекты описаны в системе COM и предоставлены любым языкам программирования. Описание объектной модели осуществлено в файлах с расширением tlb (type library - библиотека типов), а доступ к объёктам и их свойствам описано в реестре.

В данном издании все примеры будут строиться для языка программирования Delphi (точнее, для его версии Delphi 7).
Для самостоятельного ознакомления с объектной моделью AutoCad желательно транслировать в Delphi библиотеку типов AutoCad. Для AutoCad 2004 - это файл acax16enu.tlb, для более ранних версий - acad.tlb. Для этого в меню project выберете пункт меню Import Type Library. Появится окно для выбора прописанных в системном реестре библиотек типов. Может так случиться, что нужной нам библиотеки мы не найдём. Тогда щёлкните на кнопку Add (добавить) и вручную добавьте один из указанных выше файлов.

[Image]

В указанном примере я создал отдельную станицу (Palette Page) для объектов AutoCad. В результате у вас должно появиться три новых компонента: AcadDocument, AcadDatabase, AcadLayerStateManager .

Основным из них является первый компонент AcadDocument. Теперь можете с ним поэкспериментировать. Рекомендую, так же, просмотреть файл AutoCAD_TLB.pas , который был создан при импорте библиотеки типов. В нём вы найдёте полную информацию по объектной модели AutoCad, доступной в среде Delphi.

Сразу оговорюсь. Наиболее подробное руководство по объектной модели AutoCad представлено в книге "AutoCAD 2004. Разработка приложений и адаптация. Наиболее полное руководство" Николая Полещука. В этой книге помимо полного обзора объектной модели AutoCAD представлены основные положения по программированию на Delphi AutoCAD (5-ая глава).

Однако придётся смириться с тем фактом, что объектная модель AutoCAD в оригинале написана для встроенного в AutoCAD языка программирования Visual Basic. В некоторых случаях объектная модель AutoCAD работает не совсем так, как описано в справочных материалах. В дальнейшем мы с этим столкнёмся. Вероятно, программистам Autodesk ещё придётся произвести дополнительную отладку СОМ модели.

Помимо того, что можно использовать библиотеку типов, можно, имея уже достаточный опыт программирования для системы AutoCad, осуществлять соединение с AutoCad через переменную типа Variant, вызывая функцию GetActiveOleObject или CreateOleObject.

Дело в том, что при подключении к AutoCad первым способом, создаётся обращение только к новому или запущенному документу (напрямую отследить переключение документов не возможно: к сожалению метод OnDocumentActivate не работает как во встроенном Visual Basic).

В следующей главе мы рассмотрим оба способа связи с AutoCad и метод обхода несоответствия типов в COM и Visual Basic for AutoCad.

Часто возникает вопрос, какими средствами лучше пользоваться для программирования под AutoCad. И однозначно ответить на этот вопрос для человека, не работающего в Си++ достаточно сложно. Конечно, лучшим средством для программирования пол AutoCad является Object ARX - среда, разработанная для языка программирования Си++, которая позволяет помимо черчения и отслеживания данных в AutoCad так же создавать новые типы примитивов (с новыми свойствами и методами).

Далее следует вопрос, а что, собственно, вы хотите запрограммировать? Если задача стоит всего лишь в передаче графических данных от вашей программы в AutoCad и дальнейшее их редактирование, то лучше использовать язык программирования, на котором написана основная часть вашей программы, не прибегая к использованию технологии Object ARX (даже если вы используете Си++).

Кстати говоря, все недостатки объектной модели AutoCad, предоставляемой вашему компилятору, легко (или не совсем) компенсируются встроенными в AutoCad средами Visual Basic, Visual Lisp. На протяжении всего изложения кода я буду нередко прибегать к использованию этих языков программирования.

Далее немного оговорюсь о технической направленности излагаемого материала. Будучи инженером-проектировщиком в транспортном строительстве, я конечно же имел целью автоматизировать свою работу в AutoCad, поэтому в основном далее будут предлагаться задачи именно по этому направлению. Но я уверен, что специалистам и в других областях будет интересно познакомиться с предлагаемым материалом.

Присоединяемся к AutoCad из Delphi
В нашем первом примере будем использовать библиотеку типов AutoCAD_TLB, подгружая её вручную без использования компонентов TAcadDocument.

Для примера простого приложения для AutoCad создадим в Delphi обычное приложение (File>New>Application). В разделе uses вручную допишем модуль AutoCAD_TLB, ComObj .

Далее напишем функцию, которая будет отвечать за связь с AutoCad.

Function GetAcadApplication(Visible: Boolean): IAcadApplication;
var
 v : OleVariant;
begin
 Try
  // пытаемся присоединиться к уже запущенной копии AutoCad  
 v := GetActiveOleObject( 'AutoCAD.Application' ); 
 Except
  // если не получилось присоединиться, то запускаем новую копию AutoCad  
 v:= CreateOleObject( 'AutoCAD.Application' );
 end;
  // приводим переменную типа OleVariant к типу IAcadApplication  
 Result := IDispatch(v) as IAcadApplication;
  // далее проверяем, надо ли делать приложение AutoCad видимым  
 if Visible then Result.Visible := True;
end;

Функция возвращает интерфейс приложения AutoCad, объявленный в модуле AutoCAD_TLB.pas. Стоит отметить, что почти на каждом шагу нам придётся работать с переменными типа OleVariant и постоянно заниматься преобразованием типов.

Теперь можно в любом месте программы присоединиться к запущенной копии AutoCad или запустить новую.

Далее приведём нехитрый код программы, которая запускает AutoCad вместе с запуском приложения, в заголовке окна будет выводиться версия AutoCad.

unit Unit1;

interface

uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, AutoCAD_TLB, ComObj;

type TForm1 = class(TForm) Button1: TButton; procedure FormCreate(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); procedure Button1Click(Sender: TObject); private { Private declarations } AcadApp: IAcadApplication; public { Public declarations } end;

var Вернуться к оглавлению Form1: TForm1;

implementation

{$R *.dfm}

Function GetAcadApplication(Visible: Boolean): IAcadApplication; var v : OleVariant; begin Try // пытаемся присоединиться к уже запущенной копии AutoCad v := GetActiveOleObject( 'AutoCAD.Application' ); Except // если не получилось присоединиться, то запускаем новую копию AutoCad v:= CreateOleObject( 'AutoCAD.Application' ); end; // приводим переменную типа OleVariant к типу IAcadApplication Result := IDispatch(v) as IAcadApplication; // далее проверяем, надо ли делать приложение AutoCad видимым if Visible then Result.Visible := True; end;

procedure TForm1.FormCreate(Sender: TObject); begin // делаем так, что бы наша форма отображалась всегда поверх окна AutoCad FormStyle:= fsStayOnTop; Button1.Caption:= ‘Текущий чертёж’; // присоеденимся к приложению AutoCad AcadApp:= GetAcadApplication(true) end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); begin // при закрытии нашей программы закроем AutoCad AcadApp.Quit end;

procedure TForm1.Button1Click(Sender: TObject); begin // В заголовке нашей формы напишем наимонавание текущего чертежа Caption:= AcadApp.ActiveDocument.Name end;

end.

Всё работает замечательно. При нажатии кнопки в заголовке нашего приложения отображается наименование текущего чертежа. В Visual Basic for AutoCad всё гораздо проще - у объекта AcadDocument существует метод Activate, который срабатывает при переключении документов (чертежей) в приложении. В СОМ структуре AutoCad почему-то одноимённый метод так не работает. В принципе, он может и не пригодится, когда, например, нам необходимо просто что-то нарисовать в одном чертеже и забыть про все остальные открытые файлы. Но, всё же, проблема актуальна и требует решения.

В таком случае может пригодиться следующая методика. На нашу форму в Delphi поместим компонент Timer. Зададим не очень короткий и не очень длинный интервал (например, 150) и в методе OnTimer запишем код проверки текущего чертежа, например, по названию.

При этом можно использовать компонент AcadDocument со всеми объявленными в нём методами. В предыдущем примере этих методов у нас не было. Рассмотрим код такой программы.

unit Unit1;

interface

uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, OleServer, AutoCAD_TLB, ExtCtrls, ComObj;

type TForm1 = class(TForm) // таймер устанавливаем активным Timer1: TTimer; // свойство AutoConnect для AcadDocument1 устанавливаем в false AcadDocument1: TAcadDocument; procedure FormCreate(Sender: TObject); procedure Timer1Timer(Sender: TObject); procedure AcadDocument1ObjectAdded(ASender: TObject; const Object_: IDispatch); private { Private declarations } // переменная содержит полное имя текущего документа перед переключение к // другому LastDoc: string; public { Public declarations } end;

var Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject); var v : OleVariant; begin Try // пытаемся присоединиться к уже запущенной копии AutoCad v := GetActiveOleObject( 'AutoCAD.Application' ); AcadDocument1.ConnectTo((IDispatch(v) as IAcadApplication).ActiveDocument); Except // если не получилось присоединиться, то запускаем новую копию AutoCad AcadDocument1.Connect end; // сразу заполняем вспомогательную переменную для последующей проверки LastDoc:= AcadDocument1.Path+ '\' +AcadDocument1.Name; end;

procedure TForm1.Timer1Timer(Sender: TObject); var v : OleVariant; begin // очень тихо пытаемся проверить полное имя текущего документа, если // документ переключён, то отсоединяемся и заново присоединяемся к // текущему (уже другому) документу Вернуться к оглавлению try if LastDoc <> AcadDocument1.Application.ActiveDocument.Name then try AcadDocument1.Disconnect; v := GetActiveOleObject( 'AutoCAD.Application' ); AcadDocument1.ConnectTo((IDispatch(v) as IAcadApplication).ActiveDocument); // при этом обновляем дополнительную переменную LastDoc:= AcadDocument1.Path+ '\' +AcadDocument1.Name; except end; except end; Caption:= LastDoc; end;

// обработчик на создание нового объекта в текущем чертеже пишет // в командной строке соответствующую строчку и переводит каретку // на новую строчку (равносильно нажатию ESK в AutoCad) procedure TForm1.AcadDocument1ObjectAdded(ASender: TObject; const Object_: IDispatch); begin AcadDocument1.Utility.Prompt( 'Добавлен объект' +#13); end;

end.

Данный код приводит в соответствие класс TAcadDocument в СОМ модели AutoCad к соответствующему классу в Visual Basic for AutoCad.

По-видимому, требуется небольшое пояснение по поводу "очень тихой проверки имени документа". Такое количество блоков try except end объясняется тем, что при событии, когда пользователь переключит чертежи, по нашему сценарию происходит отсоединение переменной AcadDocument1 от AutoCad. Дальше всё зависит от промежутка времени, через которое произойдёт очередная попытка проверить имя текущего чертежа. Бедный пользователь замучается закрывать окошки с ошибками :-).

На первый взгляд можно было бы и не заниматься играми с таймером, тем более что есть такое свойство как AcadDocument1.Application.ActiveDocument. Однако, ActiveDocument - это свойство, а не класс (то есть без своих или унаследованных методов), и, самое главное, ссылки на функции и процедуры этого свойства (ActiveDocument: IActiveDocument) не относятся к действительно активному чертежу. В итоге - COM модель "ругается" по каждому вашему действию после переключения документа.

Техника черчения в AutoCad из Delphi
При черчении всех примитивов используются основные функции с приставкой add, объявлённые в специальных коллекциях: ModelSpace (контейнер объектов в пространстве модели), PaperSpace (контейнер объектов в пространстве текущего листа), Block (именованный набор объектов).

По способу создания объектов их можно разделить на простые и сложные. Простые объекты создаются напрямую одной функцией с заранее подготовленными координатами узловых точек. Сложные объекты, такие, как регионы (Region), твердотельные тела (solid) создаются в СОМ модели поэтапно. Сначала создаются простые примитивы, объединяемые потом в одно сложное.

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

При создании простых объектов, как уже говорилось, применяются заранее подготовленные координаты узловых точек (Vertex). В любом языке программирования для создания и передачи этих данных необходимо использовать специальный вариантный тип OleVariant.

Дело в том, что определённый в Delphi и некоторых других языках программирования тип Variant может не распознаваться в некоторых операционных средах или при передачи такого типа по протоколам электронных сетей. Тип OleVariant разработан специально для межплатформенной передачи данных.

Если помните, в начале первой главы я рассказывал о файлах с расширением tlb. Эти файлы тоже имеют структуру, предназначенную для межплатформенной передачи данных. Компанией Microsoft в своё время был разработан стандарт СОМ (Component Object Model - модель многокомпонентных объектов), который в последнее время стал основным средством взаимосвязи приложений и операционных сред. При наличии множества стандартов языков программирования cвязь приложений между собой была бы достаточно сложной и проблематичной. Стандарт СОМ внедрён в операционные среды, поэтому на сегодня является стандартом взаимосвязи приложений в полной мере этого слова. Такие приложения, как MOffice, AutoCad, да и сами операционные среды предоставляют программистам доступ к своим ресурсам по средствам так называемых интерфейсов (Interfaces). Такие программы так же называют COM-серверами. Чаще всего описания интерфейсов хранятся в файлах - библиотеках типов (с расширением tlb). В большинстве случаев описание библиотек типов создаётся на языке IDL (Interface Definition Language - язык определения интерфейсов). Однако, не стоит пугаться, учить этот язык для работы с библиотеками типов не придётся. Все языки программирования, поддерживающие СОМ-модели, распознают этот язык автоматически и генерируют файлы с его помощью, обрабатывая код на вашем "родном" языке программирования.

Однако, чтобы поближе разобраться с СОМ моделью, можно обратиться к многочисленным источникам, написанным специально для освещения этого достаточно объёмного вопроса. Я же вернусь к этому вопросу в следующих главах при разработке своих собственных серверов автоматизации для AutoCad.

Сейчас же вернёмся непосредственно к созданию примитивов в AutoCad. Я сразу приведу код небольшой программки, поясняя написанное по-месту. Для составления всех последующих программ я буду пользоваться шаблоном предыдущей программы.

unit Unit1;

interface

uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, OleServer, AutoCAD_TLB, ExtCtrls, ComObj, StdCtrls;

type TForm1 = class(TForm) Timer1: TTimer; AcadDoc: TAcadDocument; GroupBox1: TGroupBox; Label1: TLabel; Edit1: TEdit; Label2: TLabel; Edit2: TEdit; Edit3: TEdit; Label3: TLabel; Button1: TButton; procedure FormCreate(Sender: TObject); procedure Timer1Timer(Sender: TObject); procedure AcadDocObjectAdded(ASender: TObject; const Object_: IDispatch); procedure Button1Click(Sender: TObject); private { Private declarations } LastDoc: string; Вернуться к оглавлению public { Public declarations } end;

var Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject); var v : OleVariant; begin //… то же, что и в предыдущем примере end;

procedure TForm1.Timer1Timer(Sender: TObject); var v : OleVariant; begin //… то же, что и в предыдущем примере end;

procedure TForm1.AcadDocObjectAdded(ASender: TObject; const Object_: IDispatch); begin //… то же, что и в предыдущем примере end;

procedure TForm1.Button1Click(Sender: TObject); var BeginPoint, EndPoint: OleVariant; Line: IAcadLine; Begin // задаём вариантной переменной тип массива из трёх вещественных чисел BeginPoint:= VarArrayCreate([0, 2], varDouble); EndPoint:= VarArrayCreate([0, 2], varDouble); Try // начальную точку линии получаем, запросив точку у пользователя // в качестве первого параметра используем пустой параметр, второй параметр // функции - строка в командной стоке с запросом точки у пользователя BeginPoint:= AcadDoc.Utility.GetPoint(EmptyParam, ‘Укажите точку начала линии: '); // конечная точка задаётся нехитрым вычислением координат черезприращения, // указанные в в соответствующих строчках диалогового окна EndPoint[0]:= BeginPoint[0]+StrToFloat(Edit1.Text); // координата X EndPoint[1]:= BeginPoint[1]+StrToFloat(Edit2.Text); // координата Y EndPoint[2]:= BeginPoint[2]+StrToFloat(Edit3.Text); // координата Z // функция AddLine возвращает интерфейс IAcadLine постоенного объёкта, // который мы присваиваем соответствующей переменной Line:= AcadDoc.ModelSpace.AddLine(BeginPoint, EndPoint); // теперь с этой линией можно вытворять всё, что разрешает AutoCad, например // поменять цвет Line.color:= acRed; // поменять толщину линии Line.Lineweight:= acLnWt040 except end; end;

end.

[Image]

Разберёмся подробнее, что происходит при нажатии кнопки "Начертить от точки". Для задания линии необходимо иметь координаты двух точек.

Важное правило: при задании координат узловых точек любых примитивов обязательно указываются все три координаты, даже если чертится двухмерный объект. Это правило диктует СОМ-модель AutoCad.

Перед, собственно, присвоением значений координат узловым точкам необходимо определить тип вариантных переменных, указывающих на эти точки с помощью функции VarArrayCreate([0, 2], varDouble). Здесь 0, 2 - индексы крайних элементов создаваемого массива из трёх вещественных чисел, соответственно X, Y, Z. В дальнейшем обращение к этим координатам происходит так же, как и в простом массиве.

Для задания первой точки в примере я воспользовался функцией опроса пользователя GetPoint, возвращающей массив координат указанной пользователем точки в интерактивном режиме.

Вообще, все функции опроса пользователя в интерактивном режиме объявлены в разделе Utility объекта AcadDocument.

Координаты конечной точки отрезка я вычислил через приращения, задаваемые пользователем в соответствующих строчках диалогового окна. Конечно, приведённый выше код достаточно сырой. На практике необходимо осуществить проверку на возможные ошибки ввода чисел. Так же, каждое действие, связанное с переменной документа AutoCad, необходимо заключать в блок try except end на случай, если пользователь во время ваших действий или запросов решит заняться в AutoCad чем-нибудь другим или просто отменит вашу команду. При этом нужно осторожно производить все последующие вычисления, связанные с полученными данными из AutoCad.

Большинство функций добавления объектов возвращают интерфейсы добавляемых объектов, поэтому дальнейшее редактирование таких объектов не предоставляет трудностей. Необходимо только помнить, что не все свойства, объявленные в интерфейсе примитива AutoCad могут быть использованы в версиях AutoCad, отличных от той, в которой вы работаете. Так, например, в AutoCad 2004 добавлено свойство TrueColor, позволяющее применять цвета к объектам в формате RGB (Red Green Blue - красный, зелёный, синий). Если ваша программа использует это свойство, то необходимо понимать, что в более ранних версиях AutoCad ваша программа работать не сможет.

Примеры построения простых примитивов, таких, как точка, окружность, эллипс можно посмотреть в справочной литературе по Visual Basic for AutoCad. Все эти примеры легко переносятся на любой язык программирования. Следующий пример покажет, как можно выполнять такие построения. Обратите внимание на немаловажные детали, такие, как развёртывание окна AutoCad, если оно свёрнуто, когда вы делаете запрос пользователю (всё-таки терпение пользователя нужно уважать :-).

При написании кода построения примитивов в AutoCad часто приходится задавать координаты узловых точек через переменную типа OleVariant. Для упрощения я внёс небольшую функцию, делающую код программы менее громоздким и более читабельным.

Function GetPoint(X, Y, Z: double): OleVariant;
begin
 Result:= VarArrayCreate([0, 2], varDouble);
 Result[0]:= x;
 Result[1]:= y;
 Result[2]:= z;
end;

Построение двухмерных примитивов в AutoCad

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

Для этого напишем программу, интерфейс которой представлен на рисунке ниже.

Главная форма приложения

При запуске программа сразу пытается подгрузиться к AutoCAD. Точка вставки объекта определяется кнопкой [Image]. Координаты этой точки отображается в строке состояния внизу формы. Для построения примитива AutoCAD необходимо щёлкнуть на соответствующую строку элемента TrV: TTreeView. При этом происходит переключение по страницам в элементе PageControl.

Для всех типов примитивов AutoCAD я ввёл два основных свойства, которые можно изменять перед их построением: толщина линий и цвет. Толщина выбирается из списка стандартных величин, определённых в системе AutoCAD (свои толщины задавать нельзя). Толщина "по слою" соответствует толщине линий определённой в текущем слое. Для простоты задания толщины линий примитивов я буду использовать собственную функцию.

function LineW (Str: string): TOleEnum;
begin
 if (Str= 'ByLayer' ) or (Str= 'По слою' ) or (Str= 'ПоСлою' )
 or (Str= 'по слою' ) then Result:= acLnWtByLayer;
 if (Str= 'ByBlock' ) or (Str= 'По блоку' ) or (Str= 'ПоБлоку' )
 or (Str= 'по блоку' ) then Result:= acLnWtByBlock;
 if (Str= 'ByDefault' ) or (Str= 'По умолчанию' ) or (Str= 'ПоУмолчанию' )
 or (Str= 'по умолчанию' ) then Result:= acLnWtByLwDefault;
 Вернуться к оглавлению
 if (Str= '0,05' ) or (Str= '0.05' ) then Result:= acLnWt005;
 if (Str= '0,09' ) or (Str= '0.09' ) then Result:= acLnWt009;
 if (Str= '0,13' ) or (Str= '0.13' ) then Result:= acLnWt013;
 if (Str= '0,15' ) or (Str= '0.15' ) then Result:= acLnWt015;
 if (Str= '0,18' ) or (Str= '0.18' ) then Result:= acLnWt018;
 if (Str= '0,20' ) or (Str= '0.20' ) then Result:= acLnWt020;
 if (Str= '0,25' ) or (Str= '0.25' ) then Result:= acLnWt025;
 if (Str= '0,30' ) or (Str= '0.30' ) then Result:= acLnWt030;
 if (Str= '0,35' ) or (Str= '0.35' ) then Result:= acLnWt035;
 if (Str= '0,40' ) or (Str= '0.40' ) then Result:= acLnWt040;
 if (Str= '0,50' ) or (Str= '0.50' ) then Result:= acLnWt050;
 if (Str= '0,53' ) or (Str= '0.53' ) then Result:= acLnWt053;
 if (Str= '0,60' ) or (Str= '0.60' ) then Result:= acLnWt060;
 if (Str= '0,70' ) or (Str= '0.70' ) then Result:= acLnWt070;
 if (Str= '0,80' ) or (Str= '0.80' ) then Result:= acLnWt080;
 if (Str= '0,90' ) or (Str= '0.90' ) then Result:= acLnWt090;
 if (Str= '1,00' ) or (Str= '1.00' ) then Result:= acLnWt100;
 if (Str= '1,06' ) or (Str= '1.06' ) then Result:= acLnWt106;
 if (Str= '1,20' ) or (Str= '1.20' ) then Result:= acLnWt120;
 if (Str= '1,40' ) or (Str= '1.40' ) then Result:= acLnWt140;
 if (Str= '1,58' ) or (Str= '1.58' ) then Result:= acLnWt158;
 if (Str= '2,00' ) or (Str= '2.00' ) then Result:= acLnWt200;
 if (Str= '2,11' ) or (Str= '2.11' ) then Result:= acLnWt211;
end;

Эту и многие другие вспомогательные функции вы можете найти в модуле AcadDorUt.pas, прилагаемом к примеру из этой главы.

Цвет я решил задавать по цветовой шкале RGB. Эта шкала введена лишь с версии AutoCAD 2004. От стандартных цветов AutoCAD в примере я решил отказаться. В качестве альтернативы я ввёл кнопку ByLayer, которая устанавливает цвет примитива равным цвету текущего слоя AutoCAD.

Процедуру построения линии подробно описывать не буду. В прошлой главе об этом уже говорилось. Ограничусь приведением кода обработки на нажатие кнопки Начертить линию.

Перед выполнением любых интерактивных действий с AutoCAD из нашей программы я ввёл небольшую процедуру, которая развёртывает окно AutoCAD, если оно свёрнуто.

  // При разработке программы был использован компонент TRUSColorBox,   
  // написанный Матвеевым Игорем Владимировичем, E-Mail: teal_leap@mail.ru.  
  // (компонент является свободно распространяемым)  

procedure TForm1.NormalizeAcadWin; begin if AcadDoc.Application.WindowState = acMin then AcadDoc.Application.WindowState:= acMax; end; … // Процедура вычерчивания линии в AutoCAD procedure TForm1.drawLineClick(Sender: TObject); var line: IAcadLine; EndPoint: OleVariant; CmColor: IAcadAcCmColor; begin WrStrToFloat(Edit1.Text, Edit1, 0, 0); WrStrToFloat(Edit2.Text, Edit2, 0, 0); WrStrToFloat(Edit3.Text, Edit3, 0, 0); try if RadioButton1.Checked // чертим до тосчки then EndPoint:= GetStrPoint(Edit1.Text, Edit2.Text, Edit3.Text) else // чертим на расстояние begin EndPoint:= VarArrayCreate([0, 2], varDouble); EndPoint[0]:= InsertPoint[0]+StrToFloat(Edit1.Text); // координата X EndPoint[1]:= InsertPoint[1]+StrToFloat(Edit2.Text); // координата Y EndPoint[2]:= InsertPoint[2]+StrToFloat(Edit3.Text) // координата Z end; NormalizeAcadWin; // Внимание! Линии вычерчиваем либо в "модели", либо на "листе" if AcadDoc.ActiveSpace = acPaperspace then line:= AcadDoc.PaperSpace.AddLine(InsertPoint, EndPoint) else line:= AcadDoc.ModelSpace.AddLine(InsertPoint, EndPoint); // Настройка параметров линии: // Толщина линии line.Lineweight:= LineW(ComboBox1.Items.Strings[ComboBox1.ItemIndex]); // Цвет линии либо соответствует цвету слоя, либо устанавливается по // цветовой шкале RGB (в данном примере я отказался от стандартных // цветов AutoCAD) if RGBlb.Caption = 'ПоСлою' then line.color:= acByLayer else begin // Внимание! Сам по себе интерфейс IAcadAcCmColor не используется. Для его // использование необходимо создать ссылку на аналогичный интерфейс // существующего объекта - это отличается от способа работы с этим // интерфейсом, описанном в справочном материале "ActiveX и VisualBasic" // (файл …\AutoCAD 2004\Help\acadauto.chm) CmColor:= line.TrueColor; CmColor.SetRGB(GetRValue(cl), GetGValue(cl), GetBValue(cl)); line.TrueColor:= CmColor; end; except end; end;

Код получился достаточно насыщенным, но в нём содержится достаточно много разных вариантных действий и действий по обработке возможных исключений.

Поясню функцию

 
function WrStrToFloat(str: string; Sender: TObject; Col,
 Row: integer): extended. 

Данная функция работает с компонентами типа TEdit, либо TStringGrid (можно указать nil), проверяет строку str на преобразование в тип Extended и, если нужно, меняет разделитель дробной части на тот, который фигурирует в настройках вашей операционной системы. Корректированная строка выводится и в самом компоненте. Эту функцию я писал очень давно, её код далеко не идеален, но я ею давно пользуюсь и не как не могу переписать (лень). В качестве альтернативы можно использовать готовые компоненты на основе TEdit, в поле которых вводится, к примеру, только числовые значения.

Разберём построение полилинии в AutoCAD. Интерфейс ввода данных для полилинии приведён ниже.

[Image]

Для создания полилиний СОМ модель предоставляет два основных интерфейса IAcadPolyLine, IAcadLWPolyLine (LightweightPolyline), IAcad3DPolyLine. Разработчики AutoCAD рекомендуют применять второй и третий из них. Первый сохранён из более старых версий AutoCAD только для совместимости версий.

В любом случае, необходимо отметить, что при создании полилинии программно можно использовать любой интерфейс, однако, если вы программно читаете свойства полилинии, которую начертил пользователь, то необходимо использовать интерфейс IAcadLWPolyLine (если вы заранее знаете что читаете 2D полилинию) или IAcad3DPolyLine.

Для построения полилинии служат функциии:

function AddPolyline(VerticesList: OleVariant): IAcadPolyline;
function AddLightWeightPolyline(VerticesList: OleVariant): IAcadLWPolyline;
function Add3DPoly(PointsArray: OleVariant): IAcad3DPolyline;

Параметрами этих функций служат координаты вершин полилиний. Задаются они так же, как и координаты точки. Размер массива задаётся в соответствии с количеством вершин в полилинии. При этом в функции AddLightWeightPolyline вершины задаются двумя координатами X, Y (то есть, получается плоская полилиния), в остальных - тремя координатами.

Если полилиния состоит из N вершин, а для задания вершин используется f координат (2 или 3), то для задания массива VerticesList (PointsArray) можно использовать формулу: N*f-1. Для задания координатам соответствующих значений используются разные алгоритмы. Всё зависит от способа решения той или иной задачи.

В нашем случае массив координат нужно считать из компонента strGr1: TStringGrid. Для этого я использую операторы for do, перебирая значения координат по колонкам (col) и строкам (row) компонента strGr1. При этом i-ый элемент массива координат равен row*3-1+col. После заполнения массива координат и построения полилинии двигаем полилинию из первой вершины в точку, которую мы задавали как точку вставки объекта. Для действия над примитивами в AutoCAD в объектной модели используются следующие методы.

  // перемещения примитива из одной точки в другую  
procedure Move(FromPoint: OleVariant; ToPoint: OleVariant);
  // поворот в плоскости XY  
procedure Rotate(BasePoint: OleVariant; RotationAngle: Double);
  // поворот вокруг оси, заданной двумя точками  
procedure Rotate3D(Point1: OleVariant; Point2: OleVariant; RotationAngle:
Double);
  // масштабирование примитива относительно указанной точки  
procedure ScaleEntity(BasePoint: OleVariant; ScaleFactor: Double);
procedure TransformBy(TransformationMatrix: OleVariant);

Последний метод позволяет производить любые изменения с объектом посредством трансформации матрицы системы координат. В общем случае матрица трансформации имеет вид:

R00 R01 R02 T0
R10 R11 R12 T1
R20 R21 R22 T2
0 0 0 1

Где R - поворот элемента, Т - перенос. В модель примитивов так же встроены методы копирования объекта:

  // зеркальное отображение в плоскости XY относительно оси,  
  // проходящей относительно указанной точки  
function Mirror(Point1: OleVariant; Point2: OleVariant): IDispatch
  // зеркальное отображение относительно плоскости,  
  // проходящей через указанные три точки  
function Mirror3D(Point1: OleVariant; Point2: OleVariant; point3: 
OleVariant): IDispatch;
  // полярный массив (копирование примитива в полярной системе координат)  
function ArrayPolar(NumberOfObjects: SYSINT; AngleToFill: Double; 
CenterPoint: OleVariant): OleVariant;
  // копирование примитива в Декартовой системе координат  
function ArrayRectangular(NumberOfRows: SYSINT; NumberOfColumns: SYSINT; 
 NumberOfLevels: SYSINT; DistBetweenRows: Double; 
 DistBetweenCols: Double; DistBetweenLevels: 
 Double): OleVariant;
  // копирование объекта (со всеми установленными свойствами) другому  
function Copy: IDispatch;

Для каждого примитива AutoCAD имеются свои специфические свойства, характеризующие данный конкретный объект. Так, для полилинии имеются специфические свойства и методы над ними:

  // свойство отображение полилинии виде замкнутой/разомкнутой фигуры  
property Closed: WordBool;
  // свойство определяет координаты вершины с индексом Index  
property Coordinate[Index: SYSINT]: OleVariant;
  // метод добавления новой вершины к полилинии  
procedure AppendVertex(vertex: OleVariant);
  // метод позволяет разбить полилинию на более простые примитивы (линии, дуги)  
function Explode: OleVariant;
  // свойство возвращает площадь полилинии, считая её замкнутой  
property Area: Double readonly;

В нашем примере количество вершин полилинии ограничено десятью. Чтобы не тратить время на заполнения вершин приведён пример построения параболы с помощью полилинии, вершины которой заполняются при нажатии на кнопку Пример параболы. Код этой процедуры вам, наверняка, не интересен, поэтому сразу перейдём к процедуре построения полилинии.

  // Процедура вычерчивания полилинии в AutoCAD  
procedure TForm1.drawPolyLineClick(Sender: TObject);
var
 Poly: IAcadPolyLine;  // или IAcadLWPolyLine  
 Points: OleVariant;
 col, row: integer;
 CmColor: IAcadAcCmColor;
begin
  // создаём массив координат вершин полилинии, равный (N*3-1),  
  // где N = число вершин полилинии  
 Points:= VarArrayCreate([0, SpinEdit1.Value*3-1], VarDouble);
  // Заполняем массив вершин.  
  // Точкой вставки объекта считаем первую точку полилинии  
 for row:=0 to SpinEdit1.Value-1 do
 for col:=1 to 3 do
 Points[row*3-1+col]:= WrStrToFloat(strGr1.Cells[col, row+1], 
strGr1, col, row+1);
 try
 NormalizeAcadWin;
 if AcadDoc.ActiveSpace = acPaperspace
 then Poly:= AcadDoc.Application.ActiveDocument.PaperSpace.
 AddPolyline(Points)
 else Poly:= AcadDoc.Application.ActiveDocument.ModelSpace.
 AddPolyline(Points);
  // двигаем полилинию из первой вершину в точку вставки  
 Points:= VarArrayCreate([0, 2], VarDouble);
 Points:= Poly.Coordinate[0];
 Poly.Move(Points, InsertPoint);
  // Настройка параметров полилинии:  
  // Толщина полилинии  
 Poly.Lineweight:= LineW(ComboBox1.Items.Strings[ComboBox1.ItemIndex]);
  // Цвет полилинии либо соответствует цвету слоя, либо устанавливается по  
  // цветовой шкале RGB (в данном примере я отказался от стандартных  
  // цветов AutoCAD)   
 if RGBlb.Caption = 'ПоСлою' then
 Poly.color:= acByLayer
 else
 begin
  // Внимание! Сам по себе интерфейс IAcadAcCmColor не используется. Для его  
  // использование необходимо создать ссылку на аналогичный интерфейс  
  // существующего объекта - это отличается от способа работы с этим   
  // интерфейсом,  
  // описанном в справочном материале "ActiveX и VisualBasic" (файл   
  // acadauto.chm)  
 CmColor:= Poly.TrueColor;
 CmColor.SetRGB(GetRValue(cl), GetGValue(cl), GetBValue(cl));
 Poly.TrueColor:= CmColor;
 end;
 Edit4.Text:= FormatFloat( '#.##' , Poly.Length);
 Edit5.Text:= FormatFloat( '#.##' , Poly.Area);
 Poly.ConstantWidth:= StrToInt(Edit6.Text);
 except
 Edit4.Text:= '0' ;
 Edit5.Text:= '0' 
 end;
end;

Необходимо отметить, что полилиния - это элемент, состоящий из отрезков и дуг окружностей. Последовательность программного построения полилиний следующая. Выделяются узловые точки. Для них составляется массив координат. Приведёнными выше функциями стоится полилиния. Если элемент между вершинами i, (i+1) должен быть дугой окружности, то для этого используют процедуру

procedure SetBulge(Index: SYSINT; bulge: Double),

где Index = i, bulge (в переводе - выпуклость) - тангенс 1/4 угла между радиусами дуги, проведёнными из i-ой и (i+1) вершинами полилинии. Таким образом, отрицательное значение выпуклости указывает, что дуга идет по часовой стрелке от выбранной вершины. Выпуклость 0 указывает на прямую между вершинами, 1 - полукруг.

В качестве справочного материала для построения других примитивов AutoCAD приведу часть кода из файла AutoCAD_TLB.pas c необходимыми комментариями к ним:

[Image]

  // Позволяет создавать пользовательские объекты AutoCAD, определённые   
  // модулями ARX  
function AddCustomObject(const ClassName: WideString): IDispatch;
  // Создаёт поверхность по четырём точкам  
function Add3DFace(Point1: OleVariant; Point2: OleVariant; 
 point3: OleVariant; Point4: OleVariant): IAcad3DFace;
  // Позволяет создавать триангуляционную сеть по точкам  
  // (смотри рисунок)  
function Add3DMesh(M: SYSINT; N: SYSINT; 
 PointsMatrix: OleVariant): 
 IAcadPolygonMesh;
  // Создаёт дугу окружности. Дуга строится против  
  // часовой стрелки. Углы задаются в радианах.  
function AddArc(Center: OleVariant; Radius: Double;
 StartAngle: Double; EndAngle: Double): IAcadArc;
  // Создаёт параллелелограм  
function AddBox(Origin: OleVariant; Length: Double; 
 Width: Double; Height: Double): IAcad3DSolid;
  // Создаёт окружность  
function AddCircle(Center: OleVariant; Radius: Double): IAcadCircle;
  // Создаёт конус  
function AddCone(Center: OleVariant; BaseRadius: Double; Height: Double): 
IAcad3DSolid;
  // Создаёт цилиндр  
function AddCylinder(Center: OleVariant; Radius: Double; Height: Double): 
IAcad3DSolid;
  // создаёт "повёрнутый" размер. Первые два параметра - точки, указывают  
  // образмериваемыый отрезок. Третий параметр - точка, указывающая на   
  // положение текста  
function AddDimAligned(ExtLine1Point: OleVariant; ExtLine2Point: OleVariant; 
 TextPosition: OleVariant): IAcadDimAligned;
  // То же, только создаёт угловой размер  
function AddDimAngular(AngleVertex: OleVariant; FirstEndPoint: OleVariant; 
 SecondEndPoint: OleVariant; TextPoint: OleVariant): IAcadDimAngular;
  // Создаёт размер, указывающий диаметр  
function AddDimDiametric(ChordPoint: OleVariant; FarChordPoint: OleVariant; 
LeaderLength: Double): IAcadDimDiametric;
  // Создаёт линейный размер между точками ExtLine1Point, ExtLine2Point. Точка  
  // DimLineLocation задёт положение размерной линии, RotationAngle - поворот  
  // размерной линии. То есть, такой размер показывает не реальное расстояние  
  // между точками, а его проекцию на размерную линию  
function AddDimRotated(ExtLine1Point: OleVariant; ExtLine2Point: OleVariant; 
 DimLineLocation: OleVariant; RotationAngle: Double):IAcadDimRotated;
  // Создаёт трёхмерное тело из плоского (Profile) посредством его выдавливания  
  // на величину Height и смещением боковых граней на угол TaperAngle  
function AddExtrudedSolid(const Profile: IAcadRegion; Height: Double; 
 TaperAngle: Double): IAcad3DSolid;
  // То же, только выдавливание происходит по существующему примитиву.  
  // При этом, Path - это отрезок, или окружность, или дуга окружности,  
  // или полилиния, или эллипс, или сплайн  
function AddExtrudedSolidAlongPath(const Profile: IAcadRegion; 
 const Path: IDispatch): IAcad3DSolid;
  // Создание региона из существующих элементов. Этот тип примитива   
  // служит промежуточным звеном для создания трёхмерных примитивов  
function AddRegion(ObjectList: OleVariant): OleVariant;
  // создание трёхмерного тела путём поворота региона на заданный угол вокруг  
  // указанной оси  
function AddRevolvedSolid(const Profile: IAcadRegion; AxisPoint: OleVariant; 
 AxisDir: OleVariant; Angle: Double): IAcad3DSolid;

Приведён лишь небольшой отрывок из существующих функций для создания примитивов. Данный материал достаточно обширен. Его можно изучить самостоятельно, просмотрев справочный материал по СОМ модели AutoCAD и файл AutoCAD_TLB.pas.

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

Последняя функция в примере к главе представляет собой пример параметрического черчения детали. Интерфейс к примеру изображён на рисунке ниже. В этом же примере рассмотрены действия по созданию и использованию слоёв AutoCAD.

[Image]

Построение трёхмерных примитивов в AutoCad

Примером к этой главе будет служить дополненная программа из прошлой главы. Для иллюстрации примера работы с трёхмерными примитивами AutoCAD напишем пример базы данных сортамента металлопроката.

[Image]

Основные размеры швеллера представлены на следующем рисунке. Такие же обозначения мы будем вводить в коде отрисовки двутавра. Рассмотрим код программы сразу.

[Image]

type
 TForm1 = class(TForm)
. . .
 private
  { Private declarations }  
. . .
  // точка для определения угла поворота профиля металлопроката  
 RotPt: OleVariant;
  // угол поворота профиля металлопроката  
 ang: double;
. . .

// обработка нажатия на кнопку "Чертить профиль" procedure TForm1.SpeedButton6Click(Sender: TObject); begin try WrStrToFloat(Edit30.Text, Edit30, 0, 0) except Application.MessageBox( 'Ошибка ввода данных' , 'Ошибка' ); exit end; if AcadDoc.Application.WindowState = acMin then AcadDoc.Application.WindowState:= acMax; // чертим двутавр №10 if ComboBox3.ItemIndex = 0 then DrawDvt( '' , 100, 55, 4.5, 7.2, 7, 2.5); // чертим двутавр №12 if ComboBox3.ItemIndex = 1 then DrawDvt( '' , 120, 64, 4.8, 7.3, 7.5, 3); // чертим двутавр №14

if ComboBox3.ItemIndex = 2 then DrawDvt( '' , 140, 73, 4.9, 7.5, 8, 3); // чертим двутавр №16

if ComboBox3.ItemIndex = 3 then DrawDvt( '' , 160, 81, 5, 7.8, 8.5, 3.5); end;

// процедура вычерчивания двутавра procedure TForm1.DrawDvt(Element: string; h, b, s, t, r1, r2: double); var PL: AcadPolyline; VL: OleVariant; y1, x1, y2, x2, tn, sn, cn: extended; begin // точка для определения угла поворота профиля RotPt := VarArrayCreate([0, 2], varDouble); // массив координат вершин контура двутавра VL := VarArrayCreate([0, 65], varDouble); // ПЕРЕХОД К ЛЕВОЙ НИЖН.ТОЧКЕ (ПО УМОЛЧАНИЮ) InsertPoint[0]:= InsertPoint[0] + b/2; tn:= 1/10; cn:= 1/(1+Sqr(tn)); sn:= tn*cn;

y1:= t-tn*b/4-r2*cn; x1:= r2-r2*sn; x2:= s/2+r1-r1*sn; y2:= t+tn*b/4 - r1*(1-sn)*tn;

{1} VL[0]:= 0 + InsertPoint[0]; VL[1]:= 0 + InsertPoint[1]; VL[2]:= 0 + InsertPoint[2]; {2} VL[3]:= b/2 + InsertPoint[0]; VL[4]:= 0 + InsertPoint[1]; VL[5]:= 0 + InsertPoint[2]; {3} VL[6]:= b/2 + InsertPoint[0]; VL[7]:= y1 + InsertPoint[1]; VL[8]:= 0 + InsertPoint[2]; {4} VL[9]:= b/2-x1 + InsertPoint[0]; VL[10]:= y1+r2*cn + InsertPoint[1]; VL[11]:= 0 + InsertPoint[2]; {5} VL[12]:= x2 + InsertPoint[0]; VL[13]:= y2 + InsertPoint[1]; VL[14]:= 0 + InsertPoint[2]; {6} VL[15]:= s/2 + InsertPoint[0]; VL[16]:= y2+r1*cn + InsertPoint[1]; VL[17]:= 0 + InsertPoint[2]; {7} VL[18]:= s/2 + InsertPoint[0]; VL[19]:= h-(y2+r1*cn) + InsertPoint[1]; VL[20]:= 0 + InsertPoint[2]; {8} VL[21]:= x2 + InsertPoint[0]; VL[22]:= h-y2 + InsertPoint[1]; VL[23]:= 0 + InsertPoint[2]; {9} VL[24]:= b/2-x1 + InsertPoint[0]; VL[25]:= h-(y1+r2*cn) + InsertPoint[1]; VL[26]:= 0 + InsertPoint[2]; . . . // чертим полилинию if AcadDoc.ActiveSpace = acPaperspace then PL:= AcadDoc.Application.ActiveDocument.PaperSpace.AddPolyline(VL) else PL:= AcadDoc.Application.ActiveDocument.ModelSpace.AddPolyline(VL); // от точки №2 создаёмделаем закругление PL.SetBulge(2, 42*PI/360); PL.SetBulge(4, -42*PI/360); PL.SetBulge(6, -42*PI/360); PL.SetBulge(8, 42*PI/360); PL.SetBulge(12, 42*PI/360); PL.SetBulge(14, -42*PI/360); PL.SetBulge(16, -42*PI/360); PL.SetBulge(18, 42*PI/360);

// спрашиваем у пользователя как расположить нижний пояс двутавра try RotPt := AcadDoc.Utility.GetPoint(EmptyParam, 'Укажите точку поворота полки профиля: ' ); except end; // определяем угол поворота и поворачиваем профиль на найденный угол ang:= AcadDoc.Utility.AngleFromXAxis(InsertPoint, RotPt); PL.Rotate(InsertPoint, ang);

// процедура выдавливания полилинии, построенной последней командой DrawSolid;

AcadDoc.Application.Update end;

procedure TForm1.DrawSolid; var Reg: IAcadRegion; Acad3DSolid: IAcad3DSolid; Poly: IAcadPolyline; RegOle, Contur: OleVariant; Begin // в следующих командах переменной Reg: IAcadRegion присваивается значение // последнего построенного объекта в AutoCAD. В данном случае мы точно знаем, // что последний объект имеет тип IAcadPolyline if AcadDoc.ActiveSpace = acPaperspace then Poly:= (AcadDoc.Application.ActiveDocument.PaperSpace.Item(AcadDoc.Application.ActiveDocument.PaperSpace.Count-1) as IAcadPolyline) else Poly := (AcadDoc.Application.ActiveDocument.ModelSpace.Item(AcadDoc.Application.ActiveDocument.ModelSpace.Count-1) as IAcadPolyline); Poly.Closed:= true; // Функция выдавливания профиля в качестве параметра профиля требует массив // переменных типа IAcadRegion, поэтому следующие операции требуются для // создания региона на основе полилинии.Регион приходится создавать через // переменную Соntur: OleVariant. Такой "кривой" метод создания региона // диктуется тем, что параметром функции создания региона является переменная // типа OleVariant - массив объектов (из одного объекта). Contur:= VarArrayCreate([0, 0], varDispatch); Contur[0]:= Poly; if AcadDoc.ActiveSpace = acPaperspace then RegOle:= AcadDoc.Application.ActiveDocument.PaperSpace.AddRegion(Contur) else RegOle:= AcadDoc.Application.ActiveDocument.ModelSpace.AddRegion(Contur);

if AcadDoc.ActiveSpace = acPaperspace then Reg:= (AcadDoc.Application.ActiveDocument.PaperSpace.Item(AcadDoc.Application.ActiveDocument.PaperSpace.Count-1) as IAcadRegion) else Reg:= (AcadDoc.Application.ActiveDocument.ModelSpace.Item(AcadDoc.Application.ActiveDocument.ModelSpace.Count-1) as IAcadRegion); // И, наконец то, собственно 3DSolid! if AcadDoc.ActiveSpace = acPaperspace then Acad3DSolid:= AcadDoc.Application.ActiveDocument.PaperSpace.AddExtrudedSolid(Reg, -StrToFloat(edit30.Text), 0) else Acad3DSolid:= AcadDoc.Application.ActiveDocument.ModelSpace.AddExtrudedSolid(Reg, -StrToFloat(edit30.Text), 0);

// Важно не захламлять чертёж вспомогательными объектами. Poly.Delete; Reg.Delete end;

Код получился путанным и объёмным. Но, честно говоря, другого способа выдавливания трёхмерных тел я не знаю. Как вариант, можно использовать функции создания стандартных объёмных тел (цилиндр, параллелогрм), а потом совершать над ними операции сложения и вычитания, но не думаю, что код при этом упростится.

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

В заключение
В статье разобраны основные методы работы с AutoCAD через объектную модель. Рассмотрены методы построения и модификации основных объектов. Этих первичных знаний и, само собой, владение техникой черчения в AutoCAD достаточно, чтобы самостоятельно продолжить освоение СОМ модели AutoCAD и создавать утилиты, помогающие вам в работе. В заключение, хочу добавить, что разработка приложений для AutoCAD с помощью технологии СОМ - достаточно сложная задача, в ходе решения которой могут возникать многие вопросы. Многие из них уже решались. Главное - знать, где их искать. Наиболее часто обновляющийся источник в Интернете - это форум для программистов AutoCAD.ru (в том числе и для программистов на Delphi).

Успехов!

 


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