Создание объектов с помощью библиотеки ObjectDBX

Источник: http://www.cad.dp.ua/stats/dbx.php

Инструментарий ObjectARX позволяет создавать собственные объекты с помощью библиотеки ObjectDBX. В результате сборки приложения, написанного с помощью ObjectDBX, вы получаете файл с расширением .dbx, который загружается в AutoCAD аналогично .arx – файлу.

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


Рис. 1. Объекты Dimensions

Создание объектов – один из методов адаптации Автокада под отраслевые задачи. Так, например, если вы имеете дело с геоинформационными системами (ГИС), то в качестве примера объектов могут послужить условные обозначения на картах, в случае электроники – элементы микросхем, архитектуры – элементы архитектурных чертежей. Практически, все промышленные приложения, такие как Land Desktop, Architectural Desktop, Inventor обладает своими объектами.

Создание объекта можно разделить на два этапа: создание непосредственно самого объекта путем наследования нового класса от AcDbEntity либо AcDbObject и написание интерфейса для объекта, т.е. создание функций для добавления объекта в базу данных и работы с объектом (редактирование, взаимодействие с другими элементами чертежа). В данной статье рассматривается создание объектов для AutoCAD 2000-2002 (R-15) с помощью ObjectARX 2000, создание объектов для AutoCAD 2004 (R-16) c ObjectARX 2004 не значительно отличается – изменения касаются объявлений некоторых функций, которые приведены в рамках этой статьи.

Рассмотрим первый этап создания объектов - наследование. Для создания нового объекта следует выбрать пункт File – New… в редакторе Visual C++. В появившемся диалоговом окне следует выбрать ObjectARX 2000 AppWizrd. В следующем окне следует выбрать ObjectDBX (custom object definition). Также можно включить поддержку MFC, при этом будет выдано сообщение о том, что некоторые функции MFC будут недоступны, в случае, если приложение, которое загружает DBX не является MFC-хостом. Далее следует определится, от чего наследовать новый класс – от AcDbEntity или AcDbObject. Если Вы создаете не графический объект (например для хранения данных в словарях), тогда наследуйте от AcDbObject, если же Ваш объект требует графического представления в чертеже, тогда наследуйте от AcDbEntity. Для создания нового объекта следует вызвать ObjectARX Class Wizard, щелкнув на пиктограмме с изображением волшебной палочки на панели инструментов ObjectARX (рис. 2).


Рис. 2. Панель управления ObectARX.

В появившемся диалоговом окне нажмите на кнопку “Add Class…”, в результате появится окно “New Class”. В окне “New Class” заполните следующие поля: в поле “Class name” введите имя нового класса в поле “Derived from” укажите класс от которого следует наследовать новый (рекомендуется наследовать только от AcDbObject либо AcDbEntity), а поле “DFX name” укажите имя для обозначения вашего класса в DFX. Нажмите “Ok” и Вы снова вернетесь в окно “Object ARX ClassWizard”.

Для начала следует объявить переменные класса. Для этого перейдите на вкладку “Member Variables”. Для объявления новой переменной нажмите кнопку “Add variable”. В появившемся диалоговом окне введите имя переменной (поле “Var name”), выберите тип переменной из списка “Var type” и DXF-код из списка “DXF code”, нажмите Ok и объявление переменной и функций для работы с ней будут добавлены в файлы класса. Переменные в графических классах, как правило задают параметры геометрии примитива. Так, например, класс, представляющий собой окружность должен иметь, как минимум, две переменные - центр типа AcGePoint3d и радиус типа double.

Далее следует определится c тем, какие функции следует перегрузить в новом классе. Тут Ваш выбор зависит от того, насколько функциональным должен быть Ваш класс (имеется в виду возможность сохранения в файлы, редактирование с помощью различных команд, таких как ROTATE, MOVE, наличие в классе “ручек” (grips) и.т.д). Наиболее часто Вам придется перегружать следующие функции:

1. virtual Adesk::Boolean  worldDraw(AcGiWorldDraw* mode);
Отвечает за отображение примитива в чертеже.
2. virtual Acad::ErrorStatus getGeomExtents (AcDbExtents& extents) const;
Определяет размеры примитива.
3.  virtual Acad::ErrorStatus transformBy (const AcGeMatrix3d& xform);
Перемещение примитива, команда MOVE.
4. virtual Acad::ErrorStatus  getGripPoints( AcGePoint3dArray& gripPoints,
                                             AcDbIntArray&  osnapModes,
                                             AcDbIntArray&  geomIds) const;
Отвечает за формирование набора “ручек” (grips).
5. virtual Acad::ErrorStatus moveGripPointsAt (const AcDbIntArray& indices,
                                               const AcGeVector3d& offset);
Отвечает за перемещение “ручек” (grips).
6. virtual Acad::ErrorStatus getTransformedCopy ( const AcGeMatrix3d& xform,
                                                  AcDbEntity*& ent) const;
Отвечает за создание клона примитива при трансформации. Рассмотрим, реализацию этих функций.
  1. Функция worldDraw наиболее важная, так как отвечает за графическое представление примитива. В теле этой функции Вы определяете, как должен выглядеть объект. Для этого используется класс AcGiWorldDraw, отвечающий за графическое представление примитива. Класс предоставляет функции, которые возвращают ссылки на объекты классов AcGiSubEntityTraits и AcGiWorldGeometry. Первый предназначен для рисования примитивов, второй для задания параметров рисования (цвет, толщина линии, тип линии и.т.д). В качестве примера послужит фрагмент из кода функции, отображающий окружность синего цвета:
                   mode->subEntityTraits().setColor(m_circolor); // устанавливаем цвет
                   mode->geometry().circle(m_pos,m_cirrad,norm); // рисуем окружность
    
    функция subEntityTraits() возвращает указатель на объект класса AcGiSubEntityTraits для которого вызывается функция функция setColor, которая устанавливает цвет для всех последующих вызовов функций рисования примитивов; функция geometry() возвращает указатель на объект класса AcGiWorldGeometry, для которого вызывается функция circle, отображающая окружность. Полное описание всех функций класса AcGiSubEntityTraits Вы найдете в справке “ObjectARX Developer’s Guide” в разделе “AcGiSubEntityTraits Class”, описание функций рисования примитивов Вы найдете в разделе “AcGiGeometry Class”.
  2. Функция getGeomExtents должна быть реализована следующим образом: в теле функции определяется размер примитива, который затем присваивается переменной extents. Параметры переменной extents определяются разработчиком.
  3. Функция transformBy отвечает за перемещение (MOVE) примитива, следовательно в теле функции следует “перемещать” (с помощью transformBy) все ключевые точки примитива. Ниже приведен фрагмент реализации функции для примитива-окружности:
                m_centr.transformBy(xform);
    
    xform - матрица перемещения, которую Автокад передает при вызове функции.
  4. В теле функции getGripPoints разработчик определяет “ручки” (grips), которые примитив отображает при его выборе в чертеже. Точки, которые Вы хотите сделать ручками для данного примитива, должны быть добавлены в массив gripPoints. В ObjectARX 2004 введен новый тип данных – AcDbGripDataPtrArray, который представляет собой массив объектов класса AcDbGripData, содержащего данные о “ручках” для конкретного примитива. Поэтому в ObjectARX 2004 функция getGripPoints имеет дополнительное объявление с использованием AcDbGripDataPtrArray.
  5. Функция moveGripPointsAt аналогична функции, transformBy с той разницей, что она реализовывает перемещение не всего примитива, а его отдельных частей с помощью “ручек” (grips). При этом разработчик определяет, какие “ручки” были перемещены, используя массив indices, в котором записаны номера перемещаемых ручек в порядке их создания в функции getGripPoints. В ObjectARX 2004 функция имеет дополнительное объявление, связанное с наличием нового типа данных AcDbGripData (см. пред. пункт).

Мы рассмотрели наиболее часто перегружаемые функции. Если Вы хотите расширить функциональность Вашего класса, то также следует перегружать и остальные функции (см. раздел “Deriving from AcDbEntity” в справке “ObjectARX Developer’s Guide”).

Так, например, для того, чтобы Ваш примитив мог сохранятся в dwg-файле, необходимо перегрузить функции dwgInFields и dwgOutFields. Перед добавлением этих функций следует объявить все переменные - члены класса, тогда в функции будут автоматически добавлены операторы для сохранения и чтения этих переменных.

После того, как перегружены все необходимые функции, выполните компиляцию.

Теперь можно переходить к интерфейсной части. Интерфейсная часть – это arx-приложение, предназначенное для работы с объектом. После создания заготовки приложения с помощью ObjectARX AppWizard, выполните следующие действия: в файл stdarx.h добавьте заголовочный файл Вашего нового класса, в настройках проекта (Project -> Settings…) на вкладке Link добавьте lib-файл Вашего класса (появляется в результате компиляции dbx-проекта в папке Debug). Теперь можно создавать команды, использующие новый класс. Для работы со свойствами объекта можно написать собственный интерфейс, либо использовать Object Property Manager (OPM). Во втором случае Вам придется писать код автоматизации по средствам технологии COM, а в классе объекта перегрузить функцию getClassID (CLSID* pClsid)

Следует обратить внимание на то, что если Вы хотите передать чертеж, в котором присутствует объект нового класса, то также придется передать и dbx-файл для этого класса.

В качестве примера создание объектов смотрите следующие приложения: