От Топологического Traversers до Объектов
Traversers - пересечение, обход, кромка
Топологический traversers представляют списки смежных вершин топологических объектов, которые связаны в контексте топологического объекта с более высоким измерением.
Каждый специфический тип traverser выставляет, и объект, который это использует для контекста (то есть владелец списка смежных вершин) и объект, на который это в настоящее время указывает (то есть позиция списка смежных вершин) с функциями set* и get*.
Топологический traversers
Class | Objects | ||
AcBrBrepComplexTraverser | AcBrBrep (owner)
AcBrComplex (position) | ||
AcBrBrepShellTraverser |
| AcBrBrep (owner)
AcBrShell (position) | |
AcBrBrepFaceTraverser | AcBrBrep (owner)
AcBrFace (position) | ||
AcBrBrepEdgeTraverser | AcBrBrep (owner)
AcBrEdge (position) | ||
AcBrBrepVertexTraverser | AcBrBrep (owner)
AcBrVertex (position) | ||
AcBrShellFaceTraverser | AcBrShell (owner)
AcBrFace (position) | ||
AcBrFaceLoopTraverser | AcBrFace (owner)
AcBrLoop (position) | ||
AcBrLoopEdgeTraverser | AcBrLoop (owner)
AcBrEdge (position) | ||
AcBrLoopVertexTraverser | AcBrLoop (owner)
AcBrVertex (position) | ||
AcBrVertexLoopTraverser | AcBrVertex (owner)
AcBrLoop (position) | ||
AcBrVertexEdgeTraverser | AcBrVertex (owner)
AcBrEdge (position) | ||
AcBrEdgeLoopTraverser | AcBrEdge (owner)
AcBrLoop (position) |
Вершина для граней | Край может иметь в большинстве двух вершины. Они выставлены явными функциями в AcBrEdge классе (см. следующий раздел), поскольку traverser был бы расточителен для такой тривиальной смежности. | ||
Вершина для циклов | Цикл может иметь много вершины, но может иметь только один (в случае единственного края, или в случае особенности, где не имеется никакой геометрии края, типа вершины конуса). LoopVertex обход охватывает оба общий список границ вершины на лице также как особенностях. Этот список может быть более экономический чем формирование дампа граней на цикле, если единственная вещь, представляющая интерес - геометрия точки для границы лица. | ||
EdgeLoop обход | Этот класс определяет функции, которые связаны с радиальным упорядочением лиц, которые совместно используют общий край. Чтобы обеспечивать самую плотную связь к обходам списка края (AcBrLoopEdgeTraverser), лицо представлено его границей цикла в общедоступном крае. SetEdgeAndLoop () функция устанавливает владельца края и исходную позицию цикла. SetEdge () функция устанавливает владельца края и исходную позицию цикла. Позиция цикла не может быть установлена отдельно от края, поскольку радиальные обходы должны быть с сильной связью с лицо-контекстными списками края (то есть AcBrLoopEdgeTraverser). | ||
VertexLoop обход | Этот класс определяет функции, которые связаны с радиальным упорядочением лиц, которые совместно используют общую вершину. Чтобы обеспечивать самую плотную связь к обходам списка края (AcBrLoopEdgeTraverser), лицо представлено его границей цикла в общедоступной вершине. SetVertexAndLoop () функция устанавливает владельца вершины и исходную позицию цикла. SetVertex () функция устанавливает владельца вершины и устанавливает исходную позицию цикла. Позиция цикла не может быть установлена отдельно от вершины, поскольку радиальные обходы должны быть с сильной связью с лицо-контекстными списками вершины (то есть AcBrLoopVertexTraverser). |
Отключение переключения документа
Команды, которые имеют длинные процессы вычисления и опрос для cancelation, восприимчивы к наличию событий пользователя, или внешние запросы ActiveX
входят и вызывают проблемы с их внутренним состоянием, если они изменяют{*заменяют*} документы.
Поэтому, некоторые команды отключат переключение документа в течение их стадии обработки. Имеется список команд, включенных В AutoCAD или снабженный связанными ObjectARX-приложениями, которые отключают переключение документа при обработке:
§ PLOT
§ REGEN
§ RENDER
§ HIDE
§ SHADE
Следующие команды отключают переключение документа повсюду их обращения:
§ NEW
§ OPEN
§ TABLET
Кроме того, AcApDocManager::disableDocumentActivation() и AcApDocManager::enableDocumentActivation() отключит и будет вновь давать возможность активации документа. Прототипы для этих методов:
virtual Acad::ErrorStatus
disableDocumentActivation() = 0;
virtual Acad::ErrorStatus
enableDocumentActivation() = 0;
Следующая функция указывает, позволяется ли активация документа:
virtual bool
isDocumentActivationEnabled() = 0;
Открытие и Закрытие объекта ObjectARX
Все примеры кода, показанные в этой главе иллюстрируют протокол для открытия и закрытия объектов, который вы будете должны соблюсти всякий раз, когда Вы работаете с объектами резидента-базы. Этот протокол гарантирует, что объекты - физически в памяти, когда к ним должны обращаться.Прежде, чем Вы можете изменять объект, Вы должны открыть его:
acdbOpenObject(pObject, objId, AcDb::kForWrite);
Открытые функции имеют параметр режима, который определяет, открываете ли Вы объект для чтения, записи или уведомления. В то время как объект открыт для записи, Вы можете изменять его. Когда Вы закончите, Вы должны явно закрыть объект как показано в следующем примере, независимо от режима, в котором это было открыто:
pObject->close();
Следующее - типовой код для изменения цвета объекта:
Acad::ErrorStatus
changeColor(AcDbObjectId entId, Adesk::UInt16 newColor)
{
AcDbEntity *pEntity;
acdbOpenObject(pEntity, entId,AcDb::kForWrite);
pEntity->setColorIndex(newColor);
pEntity->close();
return Acad::eOk;
}
ПРЕДУПРЕЖДЕНИЕ! Непосредственно удаление объекта, который был добавлен к базе, заставляет AutoCAD грохаться.
Открытие и Закрытие Объектов Базы данных
Каждый объект AcDbObject может быть упомянут тремя различными способами:
его маркером{*дескриптором*}
его объектом ID
указателем образца C++
Когда AutoCAD не выполняется, рисунок сохранен в файловой системе.
Объекты, содержащиеся в DWG файле идентифицированы их маркерами{*дескрипторами*}.
После того, как рисунок открыт, информация рисунка доступна через объект AcDbDatabase. Каждый объект в базе данных имеет объект ID, который сохраняется в течение текущего сеанса редактирования, от создания до стирания AcDbDatabase, в котором объект постоянно находится. Открытые функции берут объект ID как параметр и возвращают указатель на объект AcDbObject. Этот указатель правилен, пока объект не закрыт, как показано в следующем рисунке.
Вы можете открывать объект, используя acdbOpenObject ():
Acad::ErrorStatus
AcDbDatabase::acdbOpenObject(AcDbObject*& obj,
AcDbObjectId id,
AcDb::OpenMode mode,
Adesk::Boolean
openErasedObject =
Adesk::kFalse);
Вы можете отображать маркер(дескриптор) к объекту ID, используя эту функцию:
Acad::ErrorStatus
getAcDbObjectId(AcDbObjectId& retId,
Adesk::Boolean createIfNotFound,
const AcDbHandle& objHandle,
Adesk::UInt32 xRefId=0);
Вы можете также открывать объект и затем запрашивать его маркер(дескриптор):
AcDbObject* pObject;
AcDbHandle handle;
pObject->getAcDbHandle(handle);
ОБРАТИТЕ ВНИМАНИЕ Всякий раз, когда объект базы данных открыт, это должно быть закрыто в самой ранней возможной возможности. Вы можете использовать AcDbObject:: близко () функция, чтобы закрыть объект базы данных.
Ads_name эквивалентен
AcDbObjectId. AcDb библиотека обеспечивает две автономных функции, которые позволяют Вам транслировать между AcDbObjectId и ads_name:
// Returns an ads_name for a given object ID.
//
acdbGetAdsName(ads_name& objName,
AcDbObjectId objId);
// Returns an object ID for a given ads_name.
//
acdbGetObjectId(AcDbObjectId& objId,
ads_name objName);
Вообще, Вы получаете объект через выбор, и это возвращено в форме ads_name. Вы тогда должны обменять ads_name на AcDbObjectId и открывать это. Следующая функция демонстрирует этот процесс:
AcDbEntity*
selectEntity(AcDbObjectId& eId, AcDb::OpenMode openMode)
{
ads_name en;
ads_point pt;
acedEntSel("\nSelect an entity: ", en, pt);
// Exchange the ads_name for an object ID.
//
acdbGetObjectId(eId, en);
AcDbEntity * pEnt;
acdbOpenObject(pEnt, eId, openMode);
return pEnt;
}
Вы можете открывать объект в одном из трех режимов:
kForRead. Объект может быть открыт для читаемого до 256 устройств считывания, пока объект не уже открытый для записи, или для уведомляют.
kForWrite. Объект может быть открыт для записи, если это не уже открыто. Иначе, открытые сбои.
kForNotify. Объект может быть открыт для уведомления, когда объект закрыт, открытым для чтения, или открытый для записи, но не, когда это уже открыто для, уведомляют. См. главу 15, “Уведомление”. Приложения будут редко должны открыться, объект для уведомляет и посылает этому уведомление.
Следующая таблица показывает возвращенным кодам ошибки, когда Вы пытаетесь открывать объект в различных режимах, и объект уже открытый.
Открытие объектов в различных режимах
Object already opened for: |
kForRead |
kForWrite |
kForNotify |
openedForRead |
eAtMaxReaders (if readCount = 256; otherwise succeeds) |
eWasOpenForRead |
(Succeeds) |
openedForWrite |
eWasOpenForWrite |
eWasOpenForWrite |
(Succeeds) |
openedForNotify |
eWasOpenForNotify |
eWasOpenForNotify |
eWasOpenForNotify |
wasNotifying |
(Succeeds) |
eWasNotifying |
eWasNotifying |
Undo |
eWasOpenForUndo |
eWasOpenForUndo |
(Succeeds) |
Для получения дополнительной информации относительно того, как управлять комплексными последовательностями открытия и закрытия объектов, см. “ Менеджер Транзакции ” на странице 451.
Отладка Приложений ObjectArx с Динамический MFC
При отладке Приложений ObjectArx, сформированных с динамически связанной MFC
библиотекой, свяжите с выпускаемой версией C время выполнения и MFC библиотеки.
Это позволяет использование MFC или C
отладку время выполнения средств, но не позволяет шагать в Microsoft MFC
отлаживающий исходный текст.
Отмена и транзакции
Операционные образцовые использования механизм отмены AutoCAD и AcDbObject:: отмена () в осуществлении AcTransactionManager:: abortTransaction (). Это требует, чтобы Вы не включили никакую операцию, которая использует механизм отмены команды подсистемы AutoCAD в транзакции. Это будет путать AcDbTransactionManager:: abortTransaction () и могло бы производить неожиданных результатов. Примеры операций, которые используют механизм отмены команды подсистемы - КОМАНДЫ SPLINEDIT и PEDIT.
Отмена и Восстановление (Undo и Redo)
Имеются два основных пути регистрации состояния для операции отмены. Автоматический механизм отмены, значение по умолчанию, позволяет системе копировать законченное состояние объекта, вызывая dwgOutFields объекта () функция с регистратором отмены. Альтернативный механизм, упомянутый как частичный механизм отмены, требует большего количества усилия программирования, но дает возможность Вам выписывать и читать в только определенную информацию относительно специфических модификаций, которые были сделаны к объекту. Каждая функция модификации для вашего нового класса (например, любой набор () функция) требуется, чтобы назвать assertWriteEnabled () функцией, которая проверяет{*отмечает*}, чтобы объект был допускаемый записью. Если значение параметра автоотмены для этой функции - kTrue, объект зарегистрирован для отмены. Когда объектная модификация закончена, и объект закрыт, содержание регистратора сохранено в файл отмены. Для данного класса, некоторые функции модификации могут использовать авто механизм отмены, и другие могут осуществлять частичный механизм отмены. Частичный механизм отмены полезен, если модификация возводит в степень маленькое количество данных.
Когда команда UNDO вызвана, и авто операция отмены была выполнена, AutoCAD вызывает dwgInFields () на объекте, таким образом считывая содержание файла отмены.
Отношения между AcDbObjects и Объектами Автоматизации
AutoCAD осуществляет его модель объекта ActiveX Automation, создавая связь между объектом резидента базы (AcDbObject) и объектом COM, который представляет это. Эта связь составлена из с двух одним направленным указателей. Первый указатель - IUNKNOWN объекта COM, который сохранен, используя переходный реактор на AcDbObject. Второй указатель - AcDbObjectId объекта резидента базы, который сохранен как поле на объекте COM.
Эта связь позволяет Вам отыскивать существующий указатель IUnknown данного объекта COM указатель AcDbObject, как показано в следующем коде:
AcAxOleLinkManager* pOleLinkManager = AcAxGetOleLinkManager();
// pObject is an AcDbObject*
//
IUnknown* pUnk = pOleLinkManager->GetIUnknown(pObject);
// NOTE: AcAxOleLinkManager::GetIUnknown() does not AddRef()
// the IUnknown pointer.
Наоборот, Вы можете отыскивать AcDbObjectId объекта резидента базы, представляемого данным объектом COM указатель IUnknown, как показано в следующем коде:
IAcadBaseObject* pAcadBaseObject = NULL;
// pUnk is the IUnknown* of a COM object representing
// some object in the database
//
HRESULT hr = pUnk->QueryInterface(IID_IAcadBaseObject, (LPVOID*) &pAcadBaseObject);
AcDbObjectId objId;
if(SUCCEEDED(hr))
{
pAcadBaseObject->GetObjectId(&objId);
}
Относительные Испытания
Если Вы не определяете иначе, имеется подразумеваемый ", “равняется" испытанию между примитивом и каждым элементом{*пунктом*} в списке фильтра. Для числовых групп (целые числа, реальные значения, точки, и векторы), Вы можете определить другие отношения включением относительных операторов в списке фильтра. Относительные операторы пропускают как специальные -4 группа, чей значение - строка, которая указывает испытание, которое нужно применить к следующей группе в списке фильтра.
Следующий типовой код выбирает все круги, чей радиус больший чем или равняются 2.0:
eb3.restype = 40; // Radius
eb3.resval.rreal = 2.0;
eb3.rbnext = NULL;
eb2.restype = -4; // Filter operator
strcpy(sbuf1, ">=");
eb2.resval.rstring = sbuf1; // Greater than or equals
eb2.rbnext = &eb3;
eb1.restype = 0; // Entity type
strcpy(sbuf2, "CIRCLE");
eb1.resval.rstring = sbuf2; // Circle
eb1.rbnext = &eb2;
// Select circles whose radius is >= 2.0.
acedSSGet("X", NULL, NULL, &eb1, ssname1);
Отображение прокси-примитивов
AutoCAD не может отображать прокси-примитив, используя worldDraw объекта () или viewportDraw () функции, потому что родительское приложение отсутствует. Это использует информацию в графическом метафайле примитива, который содержит данные, полученные из worldDraw примитива () или saveAs () функция, когда рисунок был последний{*прошлый*} сохранен (с командой SAVE ИЛИ SAVEAS). Формат дисплея является или таковым примитива непосредственно, или поля ограничения, указывающего том{*объем*}, населенный примитивом. Формат определен установкой PROXYGRAPHICS переменной системы во время рисунка, сохранен. Формат поля ограничения
Является полезным прежде всего, чтобы минимизировать размер выходного файла, когда данные для заказных примитивов большие.
Ответ на Сообщения AutoCAD
Имеются четыре категории сообщений, что AutoCAD посылает приложениям ObjectARX:
· Сообщения, которые посланы всем приложениям
· Сообщения, которые посланы только если приложение имеет зарегистрированную Alisp-функцию с acedDefun()
· Сообщения, которые посланы приложениям, которые зарегистрированы как сервисы ObjectARX
· Сообщения, отвечающие приложениям, которые используют ActiveX Автоматизацию
Следующие пять таблиц описывают сообщения, что AutoCAD посылает toObjectARX приложению. Первая таблица перечисляет сообщения, посланные всем приложениям.
Сообщения, посланные всем приложениям Message
Сообщение | Описание | ||
KInitAppMsg | Посланный, когда приложение ObjectARX загружено, чтобы открыть связь между AutoCAD и приложением. | ||
KUnloadAppMsg | Посланный, когда приложение ObjectARX разгруженно (или когда пользователь разгружает приложение или когда сам AutoCAD закончен). Закрывает файлы и исполняет операции очистки. | ||
KLoadDwgMsg | Посланным однажды, когда рисунок открыт. Тогда, если приложение регистрируется, любые функции с Автошепелявят, AutoCAD посылает это сообщение однажды за каждый рисунок, загруженный в редактора. Редактор AutoCAD полностью инициализирован в этом точке, и все глобальные функции доступны. Однако, Вы не можете использовать acedCommand () функция от kLoadDwgMsg | ||
KPreQuitMsgSent, | когда AutoCAD выходит, но прежде, чем это начинает разгружать все приложения ObjectARX.. |
Следующая таблица перечисляет сообщения, что AutoCAD посылает приложениям, которые имеют буферизованный функцию AutoLISP с acedDefun ():
Сообщение | Описание | ||
kUnloadDwgMsg | Посланный, когда пользователь выходит из сеанса рисунка. | ||
kInvkSubrMsg | Посланным, чтобы вызвать функции буферизованное использование acedDefun (). | ||
kEendMsg | Посланным только, когда команда END введена и имеются изменения(замены), которые должны быть сохранены (когда dbmod! = 0). KEndMsg не послан для НОВОГО или ОТКРЫТОГО, вместо этого, kSaveMsg и kLoadDwgMsg посланы. В течение КОНЦА, если dbmod = 0, то kQuitMsg послан вместо kEndMsg. | ||
kQuitMsg | когда выход из AutoCAD (заканчивается без того, чтобы сохранить рисунок потому что QUIT команда была введена. KQuitMsg может также быть получен с командой END, как отмечено выше.
Если команда END послана и dbmod = 0, то kQuitMsg послан. | ||
kSaveMsg | когда AutoCAD сохраняет рисунок потому что SAVE
SAVEAS, NEW или команда OPEN введены. | ||
kCcfgMsgSent | когда AutoCAD возвращается от программы конфигурации, и используемый только для изменения драйвера дисплея. |
Следующая таблица перечисляет сообщения, что приложение получает, если это имеет зарегистрированный сервис с ObjectARX.
Сообщение |
Описание |
KDependencyMsg |
когда приложение ObjectARX имеет буферизованный объект AcRxService и индекс зависимости на тех сервисных изменениях от 0 до 1. |
KNoDependencyMsgSent |
когда приложение ObjectARX имеет буферизованный объект AcRxService и индекс зависимости на тех сервисных изменениях от 1 до 0. |
Следующая таблица перечисляет сообщения, что приложение должно ответить к тому, если это использует ActiveX Автоматизацию. См. главу 23, “ ОБЩАЯ ОБЪЕКТНАЯ МОДЕЛЬ, ActiveX Автоматизация, и Объектный Менеджер Свойства. ”
Сообщение |
Описание |
kOleUnloadAppMsg |
Посылается, чтобы определить, может ли приложение быть выгружено (то есть ни один из его объектов ActiveX или интерфейсов не упомянут другими приложениями). |
См. rxdefs.h файл, где эти константы перечисления определены в соответствии с объявлением типа AppMsgCode. Вы будете должны решить, на которые сообщения ваше приложение ObjectARX ответит.
Следующая таблица описывает рекомендуемые действия после получения данного сообщения. ObjectARX прикладные реакции на сообщения AutoCAD
Сообщение |
Рекомендуемые действия |
kInitAppMsg |
Регистрируйте услуги, классы, AcEd команды и реакторы, AcRxDynamicLinker реакторы. Инициализируйте системные ресурсы приложения типа устройств и окон. Исполните всю одноразовую раннюю инициализацию. AcRx, AcEd, и AcGe весь активен. Сохраните значение pkt параметра, если Вы хотите разблокировать и повторно блокировать ваше приложение. Не ожидайте, что драйверы устройства будут инициализированы, любые ресурсы интерфейса пользователя быть активными, приложения, которые будут загружены в специфическом порядке, будут автошепелявить, чтобы присутствовать, или любые базы данных, чтобы быть открытыми. Запросы, возводящие в степень любое из этих предположений приведут к состоянию ошибки, иногда фатальному. AcDb и AcGi библиотеки вообще еще не активен, хотя связано AcRx, и другие структуры на месте |
KUnloadAppMsg |
Исполняют заключительную системную очистку ресурса. Что-нибудь начатое или созданное в kInitAppMsg должно теперь быть остановлено или разрушено. Не ожидайте вещи быть любой отличным от описания kInitAppMsg. AutoCAD мог быть главным образом демонтирован к времени, этот запрос сделан, если бы не перечисленные библиотеки, поскольку активный в kInitAppMsg Делают описание. |
KOleUnloadAppMsg |
Это сообщение нужно ответить только приложениями, использующими ActiveX Автоматизацию. Ответьте с AcRx:: kRetOK, если приложение может быть вызгружено (ни один из его объектов ActiveX или интерфейсов не упомянут другими приложениями). Если это не может быть выгружено, отвечать с AcRx:: kRetError. |
KLoadDwgMsg |
исполняют инициализацию, уместную текущему рисунку сеанса редактирования. AcDb, AcGi, и интерфейс пользователя API - все теперь активные. Было ли что - нибудь сделано к рисунку, не определен. Все Снабженные автохамом API теперь активны. Вы можете исполнять регистрацию функции AutoLISP в это время, и инициализировать интерфейс пользователя. Другие операции, чтобы исполнить теперь включают поллинг драйверы AutoCAD и запрос AcEditorReactor события, если Вы хотите самый ранний возможный доступ к acdbHostApplicationServices()->workingDatabase(). Не Делайте что - нибудь, что Вы не хотели бы случиться для каждого рисунка сеанс редактирования. Предположите, что это сообщение послано больше чем однажды в выполнение программы. |
KUnloadDwgMsg |
выпускают или очищают все начатое или буферизованный в ответ на код kLoadDwgMsg. Выпустите все AcDb реакторы, исключая постоянные реакторы. Не выпустите системные ресурсы, которые не связаны к сеансу редактирования, или очищают классы AcRx, AcEd реакторы, или команды; они остаются правильными(допустимыми) поперек сеансов редактирования. |
KDependencyMsg |
исполняют любые действия, которые являются необходимыми для вашего приложения, когда другие приложения станут зависящий от этого, типа блокировки вашего приложения так, чтобы это не могло быть разгруженно. |
KNoDependencyMsg |
исполняют любые действия, которые являются необходимыми для вашего приложения, когда имеются больше не никакие другие приложения, зависящие вашего, типа разблокирования вашего приложения так, чтобы это могло быть разгруженно пользователем если желательно. |
KInvkSubrMsg |
вызывают функции, буферизованные с acedDefun (). Определите функцию, звоня по телефону к acedGetFuncode (). Возвращаемые значения с acedRetxxx (). Не Делайте много здесь кроме функционального обращения. |
KPreQuitMsg |
разгружают любые зависимости (приложения, DLLs, и так далее) который ваше приложение управляет, чтобы гарантировать, что они являются разгруженным before your приложением. |
kEndMsg kCfgMsg kQuitMsg kSaveMsg |
Рассмотрите использование AcEditorReactor повторных вызовов случая как альтернатива к ответу на эти сообщения. Не ответьте на эти сообщения, если вы отвечаете на эквивалентные повторные вызовы случая, сделанные через AcEditorReactor. |
Параллельные процессы
В то время как ObjectDBX DLLs поддерживают множественные процессы на Windows NT и Windows 95, они не были сделаны безопасными потока.
Параметрическая Геометрия
Следующие разделы обсуждают работу параметрической геометрии.
Параметры настройки Проекта Visual C++ для Динамически Связанного MFC
Формировать Приложение ObjectArx, используя общедоступную MFC
библиотеку
1 Выбирают MFC AppWizard опция (DLL) для проекта.
2 Расширение Выбора DLL использование общедоступного MFC DLL.
3 Идут к диалоговому окну Project Settings и выбирают позицию табуляции General.
4 Использование Выбора MFC в Общедоступном DLL для поля Microsoft foundation class.
5 Добавляют функцию acrxEntryPoint к CPP файлу проекта. См. пример в конце главы для законченной установки для проекта MFC.
Перечислимые типы
AcBr struct содержит перечислимые типы, которые являются уникальными к AcBr библиотеке, и это используется как коды возврата или на списке параметров местных функций класса. Различные перечислимые поля описаны ниже.
Передавать-разовые руководящие принципы
Когда наиболее удаленные операционные концы, операционный менеджер обстреливает endCalledOnOutermostTransaction () уведомление (см. “ Реакторы Транзакции ” на странице 456) и начинает передающийся{*совершающийся*} процесс, в котором модификации на всех объектах, связанных с транзакцией совершены{*переданы*} базе данных.
Каждый объект совершен{*передан*} индивидуально, один за другим, пока все из них не совершены{*переданы*}. В течение этой операции, не измените никакой из объектов, вовлеченных в передающийся{*совершающийся*} процесс, и не запустите никакие новые транзакции. Если Вы делаете так, AutoCAD прервется с сообщением об ошибках eInProcessOfCommitting.
Вы можете изменять индивидуальные объекты после того, как каждый был совершен{*передан*}, но рекомендуется, чтобы Вы кэшировали ИДЕНТИФИКАТОРЫ объектов, которых Вы хотите изменять и ждать, пока Вы не получаете transactionEnded () уведомление, сигнализирующее конец всех транзакций, затем делайте модификации.
Перегрузка AcDbObject Виртуальные функции
Если вы подклассифицируете от AcDbObject, имеется множество виртуальных функций, которые Вы должны отменить, как показано в следующих разделах. Этот показ разделов, которые другие функции обычно отменяются и которые функции только редко отменяются.
Перегрузка deepClone () Функция
Типовой код в этой секции - приближение к заданному по умолчанию поведению deepClone(). Глубокая клонируемая операция имеет две основных стадии:
§ Клонирование (Вы можете перегружать эту стадию)
§ Трансляция (Вы не будете должны повторно осуществить эту стадию; это может управляться тем, что помещено в карту ID)
В течение клонируемой стадии в этом примере, информация относительно старого объекта скопирована к новому объекту, используя определенный тип регистратора, чтобы выписать объект и читать это назад. Регистратор следит за объектами, принадлежащими первичному объекту так, чтобы они могли быть скопированы также.
Закончить клонируемую стадию
1 Создают новый объект того же самого типа как старый.
2 Добавляют новый объект его владельцу.
§ если объект - примитив, его владелец - запись таблицы блоков, и Вы можете использовать appendAcDbEntity().
§ если объект - AcDbObject, его владелец - AcDbDictionary, и Вы можете использовать setAt (), чтобы добавить это к словарю.
Если бы это - не первичный объект, Вы обычно добавили бы это к базе данных, используя addAcDbObject () и затем идентифицировать ее владельца, использующего setOwnerId ().
Чтобы устанавливать монопольное использование, владелец должен регистрировать из ID находящегося в собственности объекта, используя соответствующий тип монопольного использования.
3 Запрос dwgOut () на первоначальном объекте, используя глубокого клонируемого регистратора (AcDbDeepCloneFiler), чтобы выписать объект. (Или, если Вы перегружаете wblockClone () функция, использует AcDbWblockCloneFiler.)
4 Перематывают регистратора и затем вызывают dwgIn () на новом объекте.
5 Запроса setObjectIdsInFlux () на каждом новом объекте прежде, чем Вы добавляете его значение к карте объекта ID. Этот важный шаг используется, чтобы указать, что недавно созданный объект - часть глубокой клонируемой операции, и ее объект ID подчинен, чтобы измениться как часть стадии трансляции. Этот флажок автоматически выключен, когда трансляция полна.
6 Добавляют новую информацию к idMap. IdMap содержит AcDbIdPairs, которые являются парами старого (оригинала) и новых (клонированных) объектных ID. Конструктор для пары ID устанавливает первоначальный объект ID и флажок isPrimary. В этой точке, Вы устанавливаете объект ID для клонированного объекта, устанавливает флажок isCloned в TRUE, и добавляете (назначают) это на idMap.
7 Клонируют находящиеся в собственности объекты. (Этот шаг рекурсивен.)
§ Спрашивают регистратора, если имеются больше находящиеся в собственности объекты. (Для клона wblock, спросите, если имеются больше жесткие объекты.)
§ чтобы клонировать подобъект, получите его ID, и откройте объект для чтения.
§ Вызывают deepClone () на объекте. (Обратите внимание, что isPrimary установлен в FALSE, потому что это - находящийся в собственности объект.) deepClone () функция клонирует объект и устанавливает его владельца. Это также добавляет запись на карту ID.
§ Закрыли бы подобъект, если это было создано в это время.
§
Следующий типовой код иллюстрирует эти шаги:
Acad::ErrorStatus
AsdkPoly::deepClone(
AcDbObject* pOwner,
AcDbObject*& pClonedObject,
AcDbIdMapping& idMap,
Adesk::Boolean isPrimary) const
{
// You should always pass back pClonedObject == NULL
// if, for any reason, you do not actually clone it
// during this call. The caller should pass it in
// as NULL, but to be safe, we set it here as well.
//
pClonedObject = NULL;
// If this object is in the idMap and is already
// cloned, then return.
//
bool isPrim = false;
if (isPrimary)
isPrim = true;
AcDbIdPair idPair(objectId(), (AcDbObjectId)NULL, false, isPrim);
if (idMap.compute(idPair) && (idPair.value() != NULL))
return Acad::eOk;
// Step 1: Create the clone.
//
AsdkPoly *pClone = (AsdkPoly*)isA()->create();
if (pClone != NULL)
pClonedObject = pClone; // Set the return value.
else
return Acad::eOutOfMemory;
// Step 2: Append the clone to its new owner. In this
// example, the original object is derived from AcDbEntity,
// so the owner is expected to be an AcDbBlockTableRecord,
// unless an ownership relationship is set up with another
// object. In that case, it is important to establish a
// connection to that owner. This sample shows a generic
// method using setOwnerId().
//
AcDbBlockTableRecord *pBTR =
AcDbBlockTableRecord::cast(pOwner);
if (pBTR != NULL) {
pBTR->appendAcDbEntity(pClone);
} else {
if (isPrimary)
return Acad::eInvalidOwnerObject;
// Some form of this code is only necessary if
// anyone has set up an ownership for the object
// other than with an AcDbBlockTableRecord.
//
pOwner->database()->addAcDbObject(pClone);
pClone->setOwnerId(pOwner->objectId());
}
// Step 3: Now contents are copied to the clone. This is done
// using an AcDbDeepCloneFiler. This filer keeps a list of all
// AcDbHardOwnershipIds and AcDbSoftOwnershipIds contained in
// the object and its derived classes. This list is then used
// to know what additional, "owned" objects need to be cloned
// below.
//
AcDbDeepCloneFiler filer;
dwgOut(&filer);
// Step 4: Rewind the filer and read the data into the clone.
//
filer.seek(0L, AcDb::kSeekFromStart);
pClone->dwgIn(&filer);
// Step 5: This must be called for all newly created objects
// in deepClone(). It is turned off by endDeepClone()
// after it has translated the references to their
// new values.
//
pClone->setAcDbObjectIdsInFlux();
// Step 6: Add the new information to the ID map. We can use
// the ID pair started above.
//
idPair.setValue(pClonedObject->objectId());
idPair.setIsCloned(Adesk::kTrue);
idMap.assign(idPair);
// Step 7: Using the filer list created above, find and clone
// any owned objects.
//
AcDbObjectId id;
while (filer.getNextOwnedObject(id)) {
AcDbObject *pSubObject;
AcDbObject *pClonedSubObject;
// Some object’ s references may be set to NULL,
// so don’t try to clone them.
//
if (id == NULL)
continue;
// Open the object and clone it. Note that "isPrimary" is
// set to kFalse here because the object is being cloned,
// not as part of the primary set, but because it is owned
// by something in the primary set.
//
acdbOpenAcDbObject(pSubObject, id, AcDb::kForRead);
pClonedSubObject = NULL;
pSubObject->deepClone(pClonedObject,
pClonedSubObject,
idMap, Adesk::kFalse);
// If this is a kDcInsert context, the objects
// may be "cheap" cloned. In this case, they are
// "moved" instead of cloned. The result is that
// pSubObject and pClonedSubObject will point to
// the same object. Therefore, we only want to close
// pSubObject if it really is a different object
// than its clone.
//
if (pSubObject != pClonedSubObject)
pSubObject->close();
// The pSubObject may either already have been
// cloned, or for some reason has chosen not to be
// cloned. In that case, the returned pointer will
// be NULL. Otherwise, since we have no immediate
// use for it now, we can close the clone.
//
if (pClonedSubObject != NULL)
pClonedSubObject->close();
}
// Leave pClonedObject open for the caller.
//
return Acad::eOk;
}
Перегрузка Общих Функций Примитива
Общие{*обычные*} функции объекта описаны в главе 6, “примитивах”. Эта глава предполагает, что Вы знакомы с материалом, представленным там. Следующие разделы описывают, как перегрузить функции, которые отображают объекты и функции, которые используют объектные поспешные пункты{*точки*}, пункты{*точки*} власти{*захвата*}, и пункты{*точки*} протяжения. Кроме того, перегружая преобразование, пересечение, и взрывается, функции обсуждены.
Перегрузка saveAs ()
Вы должны перегрузить saveAs () если Вы хотите сохранить дополнительное графическое представление для сохранения полномочной графики объекта, Выпустите 12 DWG файлы, или оба.
Если ваш заказной объект не перегружает AcDbEntity:: saveAs () функция, AutoCAD усилит ваш worldDraw () функция, чтобы поддержать полномочную графику объекта или Выпускать 12 DWG файлы. AcDbEntity:: saveAs() просто вызывает worldDraw ().
virtual void
AcDbEntity:: saveAs (
AcGiWorldDraw *pWd,
AcDb:: SaveType saveType);
SaveType параметр используется, когда Вы хотите формировать уникальный, чередовать графические представления для обоих видов сохранения; это указывает, для которого цель saveAs () называлась. SaveType параметр имеет любое из следующих значений:
§ kR13Save указывает, что saveAs () назывался, чтобы сохранить{*экономить*} полномочные графические данные.
§ kR12Save указывает, что saveAs () назывался для сохранения, чтобы Выпустить 12 DWG файлы.
Изнутри saveAs (), Вы можете хотеть назвать worldDraw () функцией для одного значения saveType и делать прямой AcGiWorldGeometry, и AcGiSubEntityTraits запрашивает другого значения, или Вы не можете хотеть назвать worldDraw () функцией вообще.
В любом случае, перед запросом saveAs (), AutoCAD первые замены геометрия AcGiWorldDraw и черты возражает со специальными подклассами AcGiWorldGeometry и AcGiSubEntityTraits. Геометрический примитив Этих подклассов и функции черт свойства кэшируют данные в соответствующем формате скорее чем выполнение дисплея. После запроса saveAs (), AutoCAD записывает кэшируемые данные на диск.
Никакой вид сохранения не разрешает сохранять любую графику иждивенца представления. ViewportDraw () функция не называется как часть любой из сохраняющихся операций.
Ваш заказной объект может полагаться на его viewportDraw () функция для его графики, так ее worldDraw () функция один не произвела бы соответствующее изображение.
В том случае, вы будете должны перегрузить saveAs () чтобы произвести разумную графику для Выпуска 12 и полномочных объектов.
Для получения дополнительной информации на полномочных графических данных, см. главу 14, “ Полномочные Объекты. ”
В Выпуске 12 DWG файлы, информация относительно первоначального объекта не сохранена в файле. Однако, первый Выпуск, 12 объекта будет иметь тот же самый маркер{*дескриптор*} как первоначальный объект, и любой дополнительный Выпуск 12 объектами, будет иметь первоначальный маркер{*дескриптор*} объекта, помещенный в их data. (Просмотр под прикладным названием{*именем*} ACAD, после строкового компонента данных R13OBJECT.) Эта особенность обеспечивается так, чтобы Вы могли группировать Выпуск 12 объектов в блок.
Перегрузка wblockClone () Функция
Когда операция клона wblock выполнена, AutoCAD создает имеющую силу базу данных для нового рисунка, который содержит словарь имен объектов, все таблицы идентификаторов, и полный набор переменных заголовка. Следующий код приближает заданное по умолчанию выполнение wblockClone (). Перечисленные шаги соответствуют, те перечисляли в секции “ Перегрузка deepClone () Функцию ” на странице 484.
WblockClone () функция использует регистратора класса AcDbWblockCloneFiler, который возвращает список жесткого указателя и жестких связей владельца первичного объекта. Прежде, чем Вы вызываете wblockClone () на этих подобъектах, Вы должны проверить владельца подобъекта. В этой точке, вы будете делать одну из двух вещей:
N, если Вы - владелец объекта, заставляет владельца подобъекта быть клоном вас непосредственно.
N, если Вы - не владелец объекта, проход в базе данных клона как pOwner параметр в wBlockClone () функциональный запрос. В это время, объект добавлен в конец к новой базе данных, получает объект ID, и помещен в карту ID. Вход карты ID для этого объекта определит FALSE для isOwnerTranslated поля.
Если pOwner установлен в базу данных, wblockClone () должен установить владельца клонированного объекта тому же самому владельцу как таковой первоначального объекта. Тогда, когда ссылки оттранслированы в соответствии с AutoCAD, это модифицирует ссылку владельца к клонированному объекту в новой базе данных.
Важно гарантировать, что все объекты обладания клонировались. AutoCAD всегда клонирует таблицы идентификаторов, словарь имен объектов, пространство модели, и пространство листа (для клонируемых контекстов других чем AcDb:: kDcXrefBind) в течение клона wblock. Приложения с обладанием объектов ответствены за обеспечение, чтобы эти объекты клонировались{*имитировались*} в случае необходимости. Если объект обладания не клонировался и не найден в карте ID, wblock клонируемыми аварийными прекращениями работы с AcDb:: eOwnerNotSet.
Вы должны пройти в базе данных как владелец объекта, когда Вы копируете примитив, которых ссылок таблица идентификаторов делает запись. Например, предположите, что Вы вызываете wblockClone () на объекте сферы. Запись таблицы блоков - жесткий владелец этого объекта сферы. Объект сферы содержит жесткую ссылку к таблице уровня.
Сначала, в beginDeepClone () стадия, новая база данных создана и основывать с заданными по умолчанию элементами. Следующий рисунок показывает запись таблицы блоков пространства модели и таблицу уровня, потому что они уместны этому разделу. Клонирование, которое происходит в этой стадии всегда, случается в течение wblock операции.
В beginWblock () стадия, набор выборов клонировался, как показано в следующем рисунке. В этом примере, сфера клонировался.
Поскольку сфера содержит жесткий указатель на Уровень 1, Уровень 1 клонирован.
Затем, указатели должны быть оттранслированы, чтобы отнесится к клонированным объектам, как показано в следующем рисунке. BeginDeepCloneXlation() уведомление указывает начало этой стадии.
Карта ID предыдущего рисунка
KEY |
VALUE |
isCloned |
isPrimary |
isOwnerXlated |
BTR1 |
BTR2 |
TRUE |
FALSE |
TRUE |
SPH1 |
SPH2 |
TRUE |
TRUE |
TRUE |
LT1 |
LT2 |
TRUE |
FALSE |
* |
LTR1 |
LTR2 |
TRUE |
FALSE |
FALSE† |
† В течение трансляции, эта установка указывает, что уровень транслирует его владельца от LayerTable 1 к LayerTable 2.
Процесс клона wblock используется для таблицы перекрестных ссылок, связывают также как wblock. Потребности оба очень похожи, но там - несколько различий, которые требуют специального внимания при перегрузке wblockClone ().
Wblock клонирует все отобранные примитивы. Однако, таблица перекрестных ссылок связывает, никогда не клонирует примитивы, которые находятся в пространстве листа. Это оставляет две вещи рассмотреть при создании объектов или примитивов, и использования AcDbHardPointerIds. Сначала, в начале wblockClone любого AcDbEntity (), выясните, если клонируемый контекст - AcDb:: kDcXrefBind и, если так, клонируется ли примитив в пространстве листа. Если это, то никакое клонирование не должно быть сделано, и wblockClone () должен возвратить Acad:: eOk.
Если ваш класс пользователя имеет любой AcDbHardPointerIds, который может указывать на примитивы (типа, мы делаем с AcDbGroup), то примитивы могли бы быть в пространстве листа и не будут поэтому клонироваться. В таком случае, AcDbHardPointerIds будет установлен в NULL.
Wblock не следует за жесткими ссылками указателя поперек баз данных. Однако, таблица перекрестных ссылок связывает, делает это все время. Например, примитив в рисунке таблицы перекрестных ссылок может быть на VISRETAIN уровне в ведущем рисунке. Так, если Вы осуществляете, ваш wblockClone () с циклом, чтобы проверить подобъекты, и базу данных подобъекта - не тот же самый как таковой клонируемого объекта, Вы должны пропустить подобъект, если клонируемый контекст - не AcDb:: kDcXrefBind. Например:
if(pSubObject->database() != database() && idMap.deepCloneContext() != AcDb::kDcXrefBind)
{
pSubObject->close();
continue;
}
Следующие показы кода, перегружающие wblockClone () чтобы осуществить это для заказного примитива (AsdkPoly). Эта функция вызвана с кодом, показанным в “ Редактор Функции Уведомления Реактора ” на странице 504.
Acad::ErrorStatus
AsdkPoly::wblockClone(AcRxObject* pOwner,
AcDbObject*& pClonedObject,
AcDbIdMapping& idMap,
Adesk::Boolean isPrimary) const
{
// You should always pass back pClonedObject == NULL
// if, for any reason, you do not actually clone it
// during this call. The caller should pass it in
// as NULL, but to be safe, it is set here as well.
//
pClonedObject = NULL;
// If this is a fast wblock operation, no cloning
// should take place, so we simply call the base class’s
// wblockClone() and return whatever it returns.
//
// For fast wblock, the source and destination databases
// are the same, so we can use that as the test to see
// if a fast wblock is in progress.
//
AcDbDatabase *pDest, *pOrig;
idMap.destDb(pDest);
idMap.origDb(pOrig);
if (pDest == pOrig)
return AcDbCurve::wblockClone(pOwner, pClonedObject, idMap, isPrimary);
// If this is an xref bind operation and this AsdkPoly
// entity is in paper space, then we don’t want to
// clone because xref bind doesn’t support cloning
// entities in paper space. Simply return Acad::eOk.
//
static AcDbObjectId pspace = AcDbObjectId::kNull;
if (pspace == AcDbObjectId::kNull) {
AcDbBlockTable *pTable;
database()->getSymbolTable(pTable, AcDb::kForRead);
pTable->getAt(ACDB_PAPER_SPACE, pspace);
pTable->close();
}
if ( idMap.deepCloneContext() == AcDb::kDcXrefBind && ownerId() == pspace)
return Acad::eOk;
// If this object is in the idMap and is already
// cloned, then return.
//
bool isPrim = false;
if (isPrimary)
isPrim = true;
AcDbIdPair idPair(objectId(), (AcDbObjectId)NULL, false, isPrim);
if (idMap.compute(idPair) && (idPair.value() != NULL))
return Acad::eOk;
// The owner object can be either an AcDbObject or an
// AcDbDatabase. AcDbDatabase is used if the caller is
// not the owner of the object being cloned (because it
// is being cloned as part of an AcDbHardPointerId
// reference). In this case, the correct ownership
// will be set during reference translation. If
// the owner is an AcDbDatabase, then pOwn will be left
// NULL here, and is used as a "flag" later.
//
AcDbObject *pOwn = AcDbObject::cast(pOwner);
AcDbDatabase *pDb = AcDbDatabase::cast(pOwner);
if (pDb == NULL)
pDb = pOwn->database();
// Step 1: Create the clone.
//
AsdkPoly *pClone = (AsdkPoly*)isA()->create();
if (pClone != NULL)
pClonedObject = pClone; // Set the return value.
else
return Acad::eOutOfMemory;
// Step 2: If the owner is an AcDbBlockTableRecord, go ahead
// and append the clone. If not, but we know who the
// owner is, set the clone’s ownerId to it. Otherwise,
// we set the clone’s ownerId to our own ownerId (in
// other words, the original ownerId). This ID will
// then be used later, in reference translation, as
// a key to finding who the new owner should be. This
// means that the original owner must also be cloned at
// some point during the wblock operation.
// EndDeepClone’s reference translation aborts if the
// owner is not found in the ID map.
//
// The most common situation where this happens is
// AcDbEntity references to symbol table records, such
// as the layer an entity is on. This is when you will
// have to pass in the destination database as the owner
// of the layer table record. Since all symbol tables
// are always cloned in wblock, you do not need to make
// sure that symbol table record owners are cloned.
//
// However, if the owner is one of your own classes,
// then it is up to you to make sure that it gets
// cloned. This is probably easiest to do at the end
// of this function. Otherwise you may have problems
// with recursion when the owner, in turn, attempts
// to clone this object as one of its subobjects.
//
AcDbBlockTableRecord *pBTR = NULL;
if (pOwn != NULL)
pBTR = AcDbBlockTableRecord::cast(pOwn);
if (pBTR != NULL) {
pBTR->appendAcDbEntity(pClone);
} else {
pDb->addAcDbObject(pClonedObject);
pClone->setOwnerId( (pOwn != NULL) ? pOwn->objectId() : ownerId());
}
// Step 3: The AcDbWblockCloneFiler makes a list of
// AcDbHardOwnershipIds and AcDbHardPointerIds. These
// are the references that must be cloned during a
// wblock operation.
//
AcDbWblockCloneFiler filer;
dwgOut(&filer);
// Step 4: Rewind the filer and read the data into the clone.
//
filer.seek(0L, AcDb::kSeekFromStart);
pClone->dwgIn(&filer);
// Step 5:
// This must be called for all newly created objects
// in wblockClone. It is turned off by endDeepClone()
// after it has translated the references to their
// new values.
//
pClone->setAcDbObjectIdsInFlux();
// Step 6: Add the new information to the ID map. We can use
// the ID pair started above. We must also let the
// idMap entry know whether the clone’s owner is
// correct, or needs to be translated later.
//
idPair.setIsOwnerXlated((Adesk::Boolean)(pOwn != NULL));
idPair.setValue(pClonedObject->objectId());
idPair.setIsCloned(Adesk::kTrue);
idMap.assign(idPair);
// Step 7: Using the filer list created above, find and clone
// any hard references.
//
AcDbObjectId id;
while (filer.getNextHardObject(id)) {
AcDbObject *pSubObject;
AcDbObject *pClonedSubObject;
// Some object references may be set to NULL,
// so don’t try to clone them.
//
if (id == NULL)
continue;
// If the referenced object is from a different
// database, such as an xref, do not clone it.
//
acdbOpenAcDbObject(pSubObject, id, AcDb::kForRead);
if (pSubObject->database() != database()) {
pSubObject->close();
continue;
}
// To find out if this is an AcDbHardPointerId
// versus an AcDbHardOwnershipId, check if we are the
// owner of the pSubObject. If we are not,
// then we cannot pass our clone in as the owner
// for the pSubObject’s clone. In that case, we
// pass in our clone’s database (the destination
// database).
//
// Note that "isPrimary" is set to kFalse here
// because the object is being cloned, not as part
// of the primary set, but because it is owned by
// something in the primary set.
//
pClonedSubObject = NULL;
if (pSubObject->ownerId() == objectId()) {
pSubObject->wblockClone(pClone,
pClonedSubObject,
idMap, Adesk::kFalse);
} else {
pSubObject->wblockClone(
pClone->database(),
pClonedSubObject,
idMap, Adesk::kFalse);
}
pSubObject->close();
// The pSubObject may either already have been
// cloned, or for some reason has chosen not to be
// cloned. In that case, the returned pointer will
// be NULL. Otherwise, since there is no immediate
// use for it now, the clone can be closed.
//
if (pClonedSubObject != NULL)
pClonedSubObject->close();
}
// Leave pClonedObject open for the caller.
//
return Acad::eOk;
}
ПРИМЕЧАНИЕ Помните, что, когда wblock () функция находится в процессе выполнения, ссылки указателя в базе данных адресата еще не были оттранслированы. Следующий код не работает правильно, потому что, хотя это пробует открывать запись таблицы блоков пространства модели базы данных адресата, запись таблицы блоков пространства модели исходной базы данных открыта вместо этого. Неоттранслированная ссылка в таблице блоков базы данных адресата - все еще что касается пространства модели исходной базы данных.
void
AsdkWblockReactor::otherWblock(
AcDbDatabase* pDestDb,
AcDbIdMapping& idMap,
AcDbDatabase* pSrcDb)
{
AcDbBlockTable *pDestBlockTable;
AcDbBlockTableRecord *pDestBTR;
pDestDb->getSymbolTable(pDestBlockTable, AcDb::kForRead);
pDestBlockTable->getAt(ACDB_MODEL_SPACE, pDestBTR, AcDb::kForRead);
pDestBlockTable->close();
// Now pDestBTR is pointing to pSrcDb database’s model
// space, not to the destination database’s model space!
// The code above is not correct!
}
Чтобы находить пространство модели адресата, Вы должны смотреть это в карте ID:
void
AsdkWblockReactor::otherWblock(
AcDbDatabase* pDestDb,
AcDbIdMapping& idMap,
AcDbDatabase* pSrcDb)
{
// To find the destination model space, you must look
// it up in the ID map:
AcDbBlockTable *pSrcBlockTable;
pSrcDb->getSymbolTable(pSrcBlockTable, AcDb::kForRead);
AcDbObjectId srcModelSpaceId;
pSrcBlockTable->getAt(ACDB_MODEL_SPACE, srcModelSpaceId);
pSrcBlockTable->close();
AcDbIdPair idPair;
idPair.setKey(srcModelSpaceId);
idMap.compute(idPair);
AcDbBlockTableRecord *pDestBTR;
acdbOpenAcDbObject((AcDbObject*&)pDestBTR,
idPair.value(), AcDb::kForRead, Adesk::kTrue);
}
Перегрузка worldDraw () и viewportDraw ()
AutoCAD называет worldDraw () и viewportDraw () функциями, чтобы отобразить объект. Вы должны осуществить worldDraw () функция для любого класса, полученного из AcDbEntity. ViewportDraw () функция необязательный.
virtual Adesk::Boolean
AcDbEntity::worldDraw( AcGiWorldDraw *pWd);
virtual void
AcDbEntity::viewportDraw( AcGiViewportDraw *pVd);
Всякий раз, когда AutoCAD должен восстановить графику, чтобы отобразить объект, worldDraw () и viewportDraw () функции называются следующим способом:
if (!entity->worldDraw(pWd))
for (each relevant viewport)
entity->viewportDraw(pVd);
WorldDraw() функция формирует часть из графического представления объекта, которое может быть определено независимо от любой частности modelspace представление{*вид*} или бумажно - пространственные контексты области просмотра. ViewportDraw () функция тогда формирует часть иждивенца представления из графики объекта. Если любая из графики объекта - иждивенец представления, worldDraw() функция должна возвратить kFalse и viewportDraw () функция должна быть осуществлена. Наоборот, если объект не имеет никакой графики иждивенца представления, то worldDraw() должна возвратить kTrue, и заказной объект не осуществляет ViewportDraw().
AcDbEntity:: worldDraw () функция берет указатель на объект AcGiWorldDraw. AcGiWorldDraw - контейнерный класс для AcGi геометрии и объектов черт. Определенно, AcGiWorldDraw содержит два других объекта:
§ AcGiWorldGeometry
§ AcGiSubEntityTraits
К объекту AcGiWorldGeometry можно обращаться изнутри worldDraw(), используя AcGiWorldDraw::geometry(), и объект AcGiSubEntityTraits можно обращаться, используя
AcGiWorldDraw:: subEntityTraits() функция. Векторы записей объекта AcGiWorldGeometry к AutoCAD освежают память, используя ее набор рисунка примитивов. Примитив - команда с самым низким уровнем Используемым, чтобы тянуть{*рисовать*} графические объекты. Объект для геометрии имеет следующие функции для рисунка примитивов во внешних мировых координатах:
§ Circle
§ Circular arc
§ Polyline
§ Polygon
§ Mesh
§ Shell
§ Text
§ Xline
§ Ray
Объект AcGiSubEntityTraits устанавливает графические атрибуты со значением, использующие его набор функций черт:
§ Color
§ Layer
§ Linetype
§ Polygon fill type
§ Selection marker
AcDbEntity:: viewportDraw () функция берет указатель на объект AcGiViewportDraw и формирует представление-определенное представление объекта. Область просмотра тянет{*рисует*} объект - также контейнерный объект для других объектов, которые включают следующее:
§ AcGiViewportGeometry
§ AcGiSubEntityTraits
§ AcGiViewport
Объект геометрии области просмотра обеспечивает тот же самый список примитивов, поскольку мировая геометрия возражает, и прибавляет к этому следующие примитивы, которые используют глаз - и координаты пространства дисплея, чтобы рисовать ломаные линии и многоугольники:
§ polylineEye ()
§ polygonEye ()
§ polylineDc ()
§ polygonDc ()
Объект черт подобъекта области просмотра - тот же самый, поскольку используемое миром рисует объект (AcGiSubEntityTraits). Объект области просмотра обеспечивает функции для запроса матриц преобразования области просмотра и рассмотрения параметров.
ПРЕДУПРЕЖДЕНИЕ! Объект AcGi типа AcGiWorldDraw или AcGiViewportDraw не должен быть сохранен как глобальная или статическая переменная. Не сохраните копии объектов AcGi поперек запросов к worldDraw () и viewportDraw () функции. Однажды это возвращение функций, объекты AcGi больше не допустимы.
Для получения дополнительной информации относительно AcGi библиотеки, см. главу 26, “ Графическая Библиотека Интерфейсов. ”
Переименование Класса
Переименование классов для каждой новой версии - самый простой метод, поскольку это не возводит в степень осуществляющие новые элементы данных или функции, чтобы обнаруживать и ответить на различные номера версии класса.
Переменные, типы и значения, определенные в ObjectARX
ObjectARX
определяет несколько типов данных для среды AutoCAD. Это также определяет множество символических кодов для значений, которые пропускают функции (или просто для общей ясности). Наконец, это объявляет и инициализирует несколько глобальных переменных.
Определения и объявления появляются в ObjectARX
файлах заголовка.
ОБРАТИТЕ ВНИМАНИЕ, не ли приложение твердо придерживается соглашений, наложенных по определениям и объявлениям, описанным в этой главе, будет трудно читать и поддержать в лучшем; в худшем случае, это не будет связываться с AutoCAD правильно. Также, будущие версии ObjectARX могут вовлекать изменения к файлам заголовка. Поэтому, не замените целочисленную константу на ее символический код, если такой код был определен.
Перемещение к ObjectARX
Существующие приложения ADS должны быть перенесены к ObjectARX. Все библиотечные функции, которые были предварительно доступны в библиотеке ADS, включены в ObjectARX. Приложения, которые часто поддерживали связь с AutoCAD через библиотеку ADS или другой, вызывают выполненным быстрее в среде программы ObjectARX чем в среде программы ADS.
Пересечение с Другими примитивами
IntersectWith () функция имеет две формы:
virtual Acad::ErrorStatus
intersectWith(
const AcDbEntity* ent,
AcDb::Intersect intType,
AcGePoint3dArray& points,
int thisGsMarker = 0,
int otherGsMarker = 0) const;
virtual Acad::ErrorStatus
intersectWith(
const AcDbEntity* ent,
AcDb::Intersect intType,
const AcGePlane& projPlane,
AcGePoint3dArray& points,
int thisGsMarker = 0,
int otherGsMarker = 0) const;
Первая форма intersectWith () функциональные испытания на простое пересечение двух объектов. Вторая форма вычисляет пересечение на проекционную плоскость. Однако, обе функции возвращают перекрестные пункты{*точки*} на объекте непосредственно.
Использовать проекционную плоскую форму intersectWith () функция
1 Проектируют ваш объект и объект параметра на плоскость.
2 Проверяют объекты на пересечение на проекционной плоскости.
3 Проектируют перекрестные пункты{*точки*} назад на объект и возвращают их.
Заказной AsdkPoly класс перегружает обеих формы intersectWith () функция.
Acad::ErrorStatus
AsdkPoly::intersectWith(
const AcDbEntity* ent,
AcDb::Intersect intType,
AcGePoint3dArray& points,
int /*thisGsMarker*/,
int /*otherGsMarker*/) const
{
assertReadEnabled();
Acad::ErrorStatus es = Acad::eOk;
if (ent == NULL)
return Acad::eNullEntityPointer;
// The idea is to intersect each side of the polygon
// with the given entity and return all the points.
//
// For non-R12-entities with intersection methods defined,
// we call that method for each of the sides of the polygon.
// For R12-entities, we use the locally defined intersectors,
// since their protocols are not implemented.
//
if (ent->isKindOf(AcDbLine::desc())) {
if ((es = intLine(this, AcDbLine::cast(ent),
intType, NULL, points)) != Acad::eOk)
{
return es;
}
} else if (ent->isKindOf(AcDbArc::desc())) {
if ((es = intArc(this, AcDbArc::cast(ent), intType,
NULL, points)) != Acad::eOk)
{
return es;
}
} else if (ent->isKindOf(AcDbCircle::desc())) {
if ((es = intCircle(this, AcDbCircle::cast(ent),
intType, NULL, points)) != Acad::eOk)
{
return es;
}
} else if (ent->isKindOf(AcDb2dPolyline::desc())) {
if ((es = intPline(this, AcDb2dPolyline::cast(ent),
intType, NULL, points)) != Acad::eOk)
{
return es;
}
} else if (ent->isKindOf(AcDb3dPolyline::desc())) {
if ((es = intPline(this, AcDb3dPolyline::cast(ent),
intType, NULL, points)) != Acad::eOk)
{
return es;
}
} else {
AcGePoint3dArray vertexArray;
if ((es = getVertices3d(vertexArray))
!= Acad::eOk)
{
return es;
}
if (intType == AcDb::kExtendArg
|| intType == AcDb::kExtendBoth)
{
intType = AcDb::kExtendThis;
}
AcDbLine *pAcadLine;
for (int i = 0; i < vertexArray.length() - 1; i++) {
pAcadLine = new AcDbLine();
pAcadLine->setStartPoint(vertexArray[i]);
pAcadLine->setEndPoint(vertexArray[i + 1]);
pAcadLine->setNormal(normal());
if ((es = ent->intersectWith(pAcadLine, intType,
points)) != Acad::eOk)
{
delete pAcadLine;
return es;
}
delete pAcadLine;
}
}
return es;
}
Acad::ErrorStatus
AsdkPoly::intersectWith(
const AcDbEntity* ent,
AcDb::Intersect intType,
const AcGePlane& projPlane,
AcGePoint3dArray& points,
int /*thisGsMarker*/,
int /*otherGsMarker*/) const
{
assertReadEnabled();
Acad::ErrorStatus es = Acad::eOk;
if (ent == NULL)
return Acad::eNullEntityPointer;
// The idea is to intersect each side of the polygon
// with the given entity and return all the points.
//
// For non-R12-entities, with intersection methods defined,
// we call that method for each of the sides of the polygon.
// For R12-entities, we use the locally defined intersectors,
// since their protocols are not implemented.
//
if (ent->isKindOf(AcDbLine::desc())) {
if ((es = intLine(this, AcDbLine::cast(ent),
intType, &projPlane, points)) != Acad::eOk)
{
return es;
}
} else if (ent->isKindOf(AcDbArc::desc())) {
if ((es = intArc(this, AcDbArc::cast(ent), intType,
&projPlane, points)) != Acad::eOk)
{
return es;
}
} else if (ent->isKindOf(AcDbCircle::desc())) {
if ((es = intCircle(this, AcDbCircle::cast(ent),
intType, &projPlane, points)) != Acad::eOk)
{
return es;
}
} else if (ent->isKindOf(AcDb2dPolyline::desc())) {
if ((es = intPline(this, AcDb2dPolyline::cast(ent),
intType, &projPlane, points)) != Acad::eOk)
{
return es;
}
} else if (ent->isKindOf(AcDb3dPolyline::desc())) {
if ((es = intPline(this, AcDb3dPolyline::cast(ent),
intType, &projPlane, points)) != Acad::eOk)
{
return es;
}
} else {
AcGePoint3dArray vertexArray;
if ((es = getVertices3d(vertexArray))
!= Acad::eOk)
{
return es;
}
if (intType == AcDb::kExtendArg
|| intType == AcDb::kExtendBoth)
{
intType = AcDb::kExtendThis;
}
AcDbLine *pAcadLine;
for (int i = 0; i < vertexArray.length() - 1; i++) {
pAcadLine = new AcDbLine();
pAcadLine->setStartPoint(vertexArray[i]);
pAcadLine->setEndPoint(vertexArray[i + 1]);
pAcadLine->setNormal(normal());
if ((es = ent->intersectWith(pAcadLine, intType,
projPlane, points)) != Acad::eOk)
{
delete pAcadLine;
return es;
}
delete pAcadLine;
}
// All the points that we selected in this process are on
// the other curve; we are dealing with apparent
// intersection. If the other curve is 3D or is not
// on the same plane as poly, the points are not on
// poly.
//
// In this case, we need to do some more work. Project the
// points back onto the plane. They should lie on
// the projected poly. Find points on real poly
// corresponding to the projected points.
//
AcGePoint3d projPt, planePt;
AcGePoint3dArray pts;
AcGeLine3d line;
AcGePlane polyPlane;
AcDb::Planarity plnrty;
getPlane(polyPlane,plnrty);
for (i = 0; i < points.length(); i++) {
// Define a line starting from the projPt and
// along the normal. Intersect the polygon with
// that line. Find all the points and pick the
// one closest to the given point.
//
projPt = points[i].orthoProject(projPlane);
line.set(projPt, projPlane.normal());
if ((es = intLine(this, line, pts))
!= Acad::eOk)
{
return es;
}
planePt = projPt.project(polyPlane,
projPlane.normal());
points[i] = pts[0];
double length = (planePt - pts[0]).length();
double length2;
for (int j = 1; j < pts.length(); j++) {
if ((length2 = (planePt - pts[j]).length())
< length)
{
points[i] = pts[j];
length = length2;
}
}
}
}
return es;
}
Пересечение точек
IntersectWith () функция возвращает пункты{*точки*}, где примитив пересекает другой примитив в рисунке. Входные значения для этой функции - примитив и перекрестный тип, который может быть один из следующего:
§
kOnBothOperands (никакой примитив расширен{*продлен*})
§ kExtendThis
§ kExtendArg
§ kExtendBoth
Например, предположите, что рисунок содержит три строки, показанные в следующей иллюстрации. Line1 - "это" и line3 - примитив параметра. Если перекрестный тип - kExtendThis, пункт{*точка*} возвращен как пункт{*точка*}, где line1 ("это") пересек бы line3, если line1 были расширены{*продлены*}. Если перекрестный тип - kExtendArgument, и line2 - примитив параметра, никакие данные не возвращены, потому что, даже если это было расширено{*продлено*}, line2 не будет пересекать line1. Если перекрестный тип - kExtendBoth, и line2 - примитив параметра, пункт{*точка*} B возвращен. Если перекрестный тип - kExtendNone, и line2 - примитив параметра, никакие данные не возвращены.
IntersectWith () функция - перезагруженная функция с двумя формами.
Вторая форма берет дополнительный параметр, который является самолетом проектирования для определения очевидного пересечения двух примитивов. Они - сигнатуры для intersectWith () функция:
virtual Acad::ErrorStatus
AcDbEntity::intersectWith(
const AcDbEntity* ent,
AcDb::Intersect intType,
AcGePoint3dArray& points,
int thisGsMarker = 0,
int otherGsMarker = 0) const;
virtual Acad::ErrorStatus
AcDbEntity::intersectWith(
const AcDbEntity* ent,
AcDb::Intersect intType,
const AcGePlane& projPlane,
AcGePoint3dArray& points,
int thisGsMarker = 0,
int otherGsMarker = 0) const;
Возвращенные пункты{*точки*} - всегда на примитиве ("this"). Поэтому, в случаях{*делах*} очевидного пересечения, пересеченные пункты{*точки*} проектируются назад к примитиву прежде, чем они возвращены.
Обе версии intersectWith () функция позволяют Вам снабжать необязательные GS маркеры, чтобы оптимизировать выполнение{*работу*} для этой функции. Если intersectWith примитива () функция осуществила использование GS маркеров, то поставляющий{*снабжающий*} GS маркеры может ограничивать перекрестную область и ускорять испытание.
Например, в следующем рисунке, если пользователь выбирает, одна строка многоугольника, проходящего в GS маркере для той строки устраняет потребность проверить другие пять строк многоугольника.
Пересечение Заказного Примитива с Другим Примитивом
ObjectARX - открытая архитектура, где множественные приложения могут осуществлять их собственные заказные объекты. Возможно, что множественные приложения будут загружены в то же самое время в сеансе AutoCAD. Пользователь мог бы выбирать ваш заказной объект в операции, которая заставляет это пересекаться с другим заказным объектом, который Вы не знаете. Следующие рекомендации должны помочь Вам осуществить intersectWith () функция вашего заказного объекта.
§ Каждый заказной объект, как ожидается, будет способным пересечься с родными объектами. Родные объекты - объекты, определенные в AutoCAD, например, AcDbLine, AcDbEllipse, и AcDbSpline.
§ если intersectWith () функция вашего заказного объекта называется с другим объектом, который - не родной объект, Вы должны взорвать ваш заказной объект (например, используя взрывающийся () функция) к набору распознаваемых родных объектов, затем поворачивать вокруг и вызывать{*называть*} intersectWith () на объекте, который вошел как параметр к вашему intersectWith () в функцию. Поскольку каждый, как ожидается, будет способным пересечься с родными объектами, объект в параметре был бы способен пересечься с вашей вырезанной версией.
В течение этого процесса, Вы должны быть внимательным относительно того, как Вы называете intersectWith () функцией параметра и как Вы интерпретируете пункты{*точки*}, которые являются результатами пересечения. Например, если бы перекрестный тип был kExtendArg, Вы хотели бы изменить{*заменить*} это к kExtendThis перед запросом intersectWith () на параметре. Точно так же, если пересечение - очевидное пересечение на плоскости проектирования, пункты{*точки*}, возвращенные от intersectWith () обращаются к объекту параметра, будет на объекте параметра, не обязательно на вашем объекте. Вы, как предполагается, возвращаете перекрестные пункты{*точки*} на вашем объекте; поэтому, Вы должны проектировать пункты{*точки*} назад на плоскость проектирования (где они будут лежать на вашем проектируемом объекте) и затем проектировать их назад на ваш объект перед возвращением.
Поддержка МНОГОДОКУМЕНТАЛЬНОГО ИНТЕРФЕЙСА
С ObjectARX Вы можете создавать приложения, которые поддержат многодокументную среду AutoCAD и смогут должным образом взаимодействовать с другими приложениями Windows.
Поддержка Версии объекта
Несколько механизмов прежде использовались для руководящих версий заказных объектных классов, используемых в Приложениях ObjectArx:
§
Переименование класса для каждой новой версии.
§ Поддержание номера версии как первый компонент данных класса.
§ Поддержание номера версии как расширенные данные (xdata) или в словаре расширения.
Эти механизмы были заменены классом versioning система.
Эти более ранние механизмы описаны ниже (после описания класса versioning), помогать обслуживать{*поддерживать*} код, который использует их.
Подготовка
Границы Обрезки закрыты, не-самопересекающиеся, вогнутые 2-ые многоугольники.
Необязательная передняя сторона и назад Z отсекающие значения может быть назначена. Граница обрезки выражена в произвольной системе координат относительно отсекаемых объектов.
В AutoCAD, когда пользователь определяет, граница отсечения для блока, направления вида и завихрения текущего вида используется, чтобы определить координатную систему для границы обрезки. Это могло бы быть тот же самый как система координат отсекаемой блок-ссылки. Это отражено в API в соответствии с условием преобразования к пространству отсечения от системы блок-ссылки:
Границы Обрезки могут быть вложены. Составной объект может определять границу отсечения, и объекты, которые это содержит, может также определять границы для их внутренней геометрии. В этом случае, вложенная геометрия сначала отсечена против границы ее родителя, и любые результирующие фрагменты тогда отсечены против границы обрезки внешнего блока.
Подготовка к Разгрузке
Когда ваше приложение разгружено, Вы должны очистить любые заказные классы или
Команды, которые ваше приложение создало. Это должно иметь место в AcRx:: kUnloadAppMsg случае вашего acrxEntryPoint () функция, или в функции Названный от того случая.
Разгрузить приложение ObjectARX
1, если Вы создали команды с acedRegCmds макрокомандой или acedDefun (), Удалите их. Обычно ObjectARX команды удалены группами, использованием AcedRegCmds- > removeGroup ().
2, если Вы создали заказные классы, удаляют их.
Используйте deleteAcRxClass () функция, чтобы удалить ваши заказные классы из AcRx дерево во время выполнения. Классы должны быть удалены, начинаясь с Полученные классы сначала, подготавливая дерево классов к родительским классам.
3 Удаляют любые объекты, добавленные приложением.
Не имеется никакого способа сообщить AutoCAD забывать относительно AcDbObject образцов это
Являются в настоящее время резидентом в базе данных. Однако, когда приложение Разгруженный, AutoCAD автоматически повернет такие объекты в образцы AcDbProxyObject или AcDbProxyEntity.
4 Удаляют любые реакторы, которые были приложены к любому AcDbObject, AcDbDatabase, AcRxDynamicLinker, или объект{*цель*} AcEditor. (Постоянные реакторы На AcDbObjects - исключение; они станут полномочными объектами, когда Приложение разгружено.)
5, если Вы создали, сервисное название, удаляет это.
Вы можете использовать AcrxServiceDictionary- >, удаляют () функцию, чтобы удалить любого
Обслуживание{*служба*}, которое ваше приложение имеет буферизованный. См. распечатку для
AcrxServiceDictionary в ObjectARX Ссылке{*справочниках*}.
Подробности относительно областей просмотра
Имеются несколько тонкости относительно различной области просмотра, напечатывает AcDb, которые стоят обзор.
Объект AcDbDatabase может содержать и области просмотра пространства листа и пространство модели.
Они представлены различными типами AcDb внутренне.
Области просмотра Пространства модели представлены AcDbViewportTableRecords, которые содержатся в AcDbViewportTable. Они полностью несвязанны с AcDbViewport примитивами. Выпуск AutoCAD 13 и выше требует существования по крайней мере одного AcDbViewportTableRecord в AcDbViewportTable. Это будет названо “*ACTIVE”, который подразумевает, что это была активная область просмотра, когда рисунок был сохранен. Больше чем одна область просмотра могут быть “*ACTIVE” одновременно. Незаконно иметь ViewportTableRecord без имени.
Области просмотра Пространства листа представлены AcDbViewport. Они могут только существовать в пределах записи таблицы блоков пространства листа AcDbBlockTable. Само пространство листа должно иметь основную область просмотра пространства листа как AcDbViewport примитив в записи таблицы блоков пространства листа. В AutoCAD, эта заданная по умолчанию область просмотра создана автоматически, когда TILEMODE 0 команды выполнен. Имеется предположение в API, что эта область просмотра будет создана автоматически. Таким образом, когда новый AcDbViewport инициализирован и добавлен к записи таблицы блоков пространства листа, если основная область просмотра пространства листа еще не существует, это будет создано в течение завершения нового AcDbViewport. Обратите внимание, что это означает, что никогда не необходимо создать основную область просмотра пространства листа явно. Это совершенно имеет силу, чтобы добавить примитивы к пространству листа без когда-либо создания области просмотра пространства листа. Это - то, потому что AutoCAD может успешно открывать рисунок этого характера и автоматически создаст основную область просмотра пространства листа первый раз, когда TILEMODE, 0 команды используется. AutoCAD должен правильно отобразить примитивы в записи таблицы блоков пространства листа в пределах основной области просмотра пространства листа в то время.
В ObjectDBX, AcDbViewport::number () функция будет всегда возвращаться -1.
В ObjectARX, это сообщает о номере области просмотра текущей области просмотра в редакторе AutoCAD. Поскольку AutoCAD – не активен в ObjectDBX, это значение не имеет никакого значения. ObjectDBX обеспечивает acdbGetCurVportId () функцией, которая возвращает текущий объект ID области просмотра, когда рисунок был сохранен.
Строго рекомендуется, чтобы Вы делали обзор ObjectARX SDK документация относительно областей просмотра всех типов.
Подсказка Дисплея
Подсказка дисплея - текст, показанный на командной строке в течение перетащенной последовательности. Используйте следующую функцию, чтобы установить подсказку дисплея:
void
AcEdJig::setDispPrompt(const char* prompt);
Поиск активных областей просмотра в пространстве модели
При создании нового DWG файла с AcDbDatabase (Adesk:: kTrue) конструктор, Вы должны установить заданную по умолчанию область просмотра пространства модели в некоторые разумные параметры. Это будет гарантировать, что DWG будет видим в AutoCAD когда открыто. Отказ устанавливать заданную по умолчанию область просмотра пространства модели может требовать, чтобы пользователь AutoCAD, чтобы выбрать Изменил масштаб изображения Всех, чтобы делать геометрию из видимого рисунка. Кроме того, некоторые примитивы AutoCAD требуют, чтобы разумные параметры области просмотра вычислили аспекты их появления. Например, объект AcDbSpline, созданный в ObjectDBX, и сохраненный к рисунку без любого внимания к области просмотра, может отображать с острыми углами, подобно pline, когда загружено в AutoCAD. Данные не повреждены и первый раз, когда примитив отредактирован в AutoCAD, AcDbSpline возвратится к его надлежащей форме.
Однако, это конечно дезориентирует пользователям рисунков вашей программы.
Разработчики, обладающие утвержденным - позволенным AutoCAD, вероятно, будут видеть, утверждает результат assert() при загрузке рисунка, который был сохранен без надлежащего внимания к области просмотра. Экспериментирование демонстрирует, который параметры являются лучшими для вашего приложения и геометрии. Чтобы устанавливать область просмотра пространства модели, вставьте следующее:
// Набор некоторая информация области просмотра.
AcDbViewportTable* pViewportTable;
if (db.getViewportTable(pViewportTable, AcDb::kForRead) == Acad::eOk)
{
// Find the first viewport and open it for write.
AcDbViewportTableRecord *pRecord;
if (pViewportTable->getAt( "*ACTIVE", pRecord, AcDb::kForWrite) == Acad::eOk)
{
pRecord->setCenterPoint(AcGePoint2d(0.5, 0.5));
pRecord->setHeight(1.0);
pRecord->setWidth(1.0);
pRecord->close();
}
pViewportTable->close();
}
Поиск Файла
AcedFindFile () функция дает возможность приложению искать файл специфического имени. Приложение может определить каталог, чтобы искать, или это может использовать поток путь библиотеки AutoCAD.
В следующем типовом кодовом фрагменте, acedFindFile () ищет требуемое имя файла согласно пути библиотеки AutoCAD.
char *refname = "refc.dwg";
char fullpath[100];
.
.
.
if (acedFindFile(refname, fullpath) != RTNORM) {
acutPrintf("Could not find file %s.\n", refname);
return BAD;
Если запрос к acedFindFile () успешен, fullpath параметр установлен в полностью квалифицированную строку имени пути, типа следующего:
/home/work/ref/refc.dwg
Вы можете также запрашивать пользователей вводить имя файла посредством стандарта файловое диалоговое окно AutoCAD. Чтобы отображать файловое диалоговое окно, вызовите acedGetFileD ().
Следующий типовой кодовый фрагмент использует файловое диалоговое окно, чтобы запросить пользователей относительно имени ObjectARX-приложения.
struct resbuf *result;
int rc, flags;
if (result = acutNewRb(RTSTR) == NULL) {
acdbFail("Unable to allocate buffer\n");
return BAD;
}
result->resval.rstring=NULL;
flags = 2; // Disable the "Type it" button.
rc = acedGetFileD("Get ObjectARX Application", // Title
"/home/work/ref/myapp", // Default pathname
NULL, // The default extension: NULL means "*".
flags, // The control flags
result); // The path selected by the user.
if (rc == RTNORM)
rc = acedArxLoad(result->resval.rstring);
Поиск расширенных данных
Приложение может получить зарегистрированные расширенные данные, вызывая acdbEntGetX () функция, которая является подобной acdbEntGet (). В то время как acdbEntGet () возвращает только данные определения, acdbEntGetX () возвращает оба
Данные определения и расширенные данные для приложений это запрашивают. Это требует дополнительного параметра, приложений, который определяет прикладные названия (это отличается от AutoLISP, в котором (entget) функция была расширена на ввод необязательный параметр, который определяет прикладные названия).
Названия прошли к acdbEntGetX () должен соответствовать приложениям, зарегистрированным предыдущим запросом к acdbRegApp (); они могут также содержать групповые символы. Если параметр приложений - указатель NULL, запрос к acdbEntGetX () идентичен acdbEntGet () запрос.
Следующий типовой кодовый фрагмент показывает типичной последовательности для восстановления{*поиска*} расширенных{*продленных*} данных для двух указанных приложений. Обратите внимание, что параметр приложений передает прикладные названия в связанных буферах результатов.
static struct resbuf appname2 = {NULL, RTSTR},
appname1 = {&appname2, RTSTR},
*working_ent;
strsave(appname1.rstring, "MY_APP_1");
strsave(appname2.rstring, "SOMETHING_ELSE");
.
.
.
// Only extended data from "MY_APP_1" and
// "SOMETHING_ELSE" are retrieved:
working_ent = acdbEntGetX(&work_ent_addr, &appname1);
if (working_ent == NULL) {
// Gracefully handle this failure.
.
.
.
}
// Update working entity groups.
status = acdbEntMod(working_ent);
// Only extended data from registered applications still in the
// working_ent list are modified.
Как типовые показы кода, расширенные данные, отысканные acdbEntGetX() функция может изменяться последующим запросом к acdbEntMod (), также, как acdbEntMod () используется, чтобы изменить нормальные данные определения. (Расширенные данные могут также быть созданы, определяя, это в списке примитива прошло к acdbEntMake ()).
При возвращении расширенных данных только определенно требуемые приложения защищают одно приложение от повреждения данных другого приложения. Это также управляет объемом памяти что прикладные использования, и упрощает расширенную обработку данных, которую приложение исполняет.
ОБРАТИТЕ ВНИМАНИЕ, поскольку строки прошли с приложениями, может включать групповые символы, прикладное имя “*” заставит acdbEntGetX () возвращать все расширенные данные, приложенные примитиву.
Полезные значения
ObjectARX
определяет следующие директивы препроцессора:
#define TRUE 1
#define FALSE 0
#define EOS'\0 ’ // Строковый символ завершения
Символ ПАУЗЫ, строка, которая содержит единственную наклонную черту влево, определен для acedCommand () и acedCmd () функции, следующим образом:
#define PAUSE "\\" // Пауза в списке параметров команды
ОБРАТИТЕ ВНИМАНИЕ, что ObjectARX библиотека не определяет значения, ХОРОШИЕ и ПЛОХИЕ, которые появляются как возвращаемые значения в выборках кода повсюду этого руководства (особенно по ошибке -обрабатывающий код). Вы можете определять их, если Вы хотите, или заменяете соглашение, которое Вы предпочитаете.
Полилиния
Pline () функция позволяет заказному примитиву рисовать графические примитивы, использующие AcDbPolyline как шаблон:
virtual Adesk::Boolean pline(
const AcDbPolyline& lwBuf,
Adesk::UInt32 fromIndex = 0,
Adesk::UInt32 numSegs = 0) const;
AcDbPolylines мультисегментированы и поддерживают прямо и изогнутые доли с или без ширины. Использование pline () обеспечивает способность генерировать смежные прямые и изогнутые доли с шириной. Ни один из других AcGi
примитивных функций не поддерживает ширину, так без того, чтобы использовать pline () было бы необходимо генерировать много параллельных дуги и доли линии, чтобы моделировать заполненную дугу или долю линии с шириной. Это - неэффективный, и надлежащий дисплей, зависим от вида (усиление).
Получение ID объекта
Через ID Вы можете получить указатель на фактический объект базы данных для обеспечения исполнения операций с ним. Для примера, см. “Открытие и Закрытие ObjectARX Объекты” на странице 27.
Вы можете получить ID объекта несколькими способами:
·
Создание объекта и добавление его в конец базы данных. База данных тогда дает объекту ID и возвращает его Вам.
· Используют протокол базы данных для получения ID объектов, которые созданы автоматически, когда база данных создана (типа фиксированного набора таблиц идентификаторов и названного объектного словаря).
· Используют класс - определенный протокол для получения объекта IDs. Некоторые классы, типа таблиц идентификаторов и словарей, определяют объекты, которые имеют другие объекты. Эти классы обеспечивают протокол для получения объекта IDs находящихся в собственности объектов.
· Используют iterator, чтобы шагнуть через список или устанавить объект. AcDb библиотека обеспечивает множество iterators, которые могут использоваться, чтобы шагнуть через различные виды контейнерных объектов (AcDbDictionaryIterator, AcDbObjectIterator).
· Сделать запрос набору выборов. После того, как пользователь выбрал объект, Вы можете спрашивать, чтобы набор выборов для списка имен объекта выбранных объектов, и от имен, преобразовал к объекту IDs. Для получения дополнительной информации на selectionsets, см. главу 6, “Объекты”.
Получение ID Объектного Реактора
Каждый объект базы данных обслуживает{*поддерживает*} список реакторов на себе. Некоторые - переходные реакторы, и некоторые постоянны. Переходные реакторы - образцы классов, полученных из AcDbObjectReactor, принимая во внимание, что постоянные реакторы - объект IDs объектов резидента базы.
Следующий код показывает, как перерыть список реакторов, чтобы найти ваш переходный или постоянный реактор. Чрезвычайно важно, что Вы проверяете специфический вход в реакторном списке, чтобы быть постоянным реактором, используя функцию AcDbIsPersistentReactor. Если это - постоянный реактор, Вы можете использовать соответствующую функцию, чтобы получить ее объект ID. Если это - не постоянный реактор, Вы можете приводить вход в AcDbObjectReactor.
AcDbVoidPtrArray *pReactors;
void *pSomething;
AcDbObjectReactor *pObjReactor;
AcDbObjectId persObjId;
AcDbObject *pPersReacObj;
pReactors = pEnt->reactors();
if (pReactors != NULL && pReactors->length() > 0) {
for (int i = 0; i < pReactors->length(); i++) {
pSomething = pReactors->at(i);
// Is it a persistent reactor?
//
if (acdbIsPersistentReactor(pSomething)) {
persObjId = acdbPersistentReactorObjectId(pSomething);
acutPrintf("\n\nPersistent reactor found.");
// Echo the keyname to the user.
//
char *keyname = NULL;
getPersReactorKey(keyname, persObjId);
if (keyname) {
acutPrintf("\nThis is the reactor named %s",keyname);
free (keyname);
}
// Open it up and see if it’s one of ours. If it is,
// fire the custom notification.
//
if ((retStat = acdbOpenAcDbObject(pPersReacObj, persObjId, AcDb::kForNotify))
!= Acad::eOk)
{
acutPrintf("\nFailure for"
" openAcDbObject: retStat==%d\n",
retStat);
return;
}
AsdkPersReactor *pTmpPers;
if ((pTmpPers = AsdkPersReactor::cast((AcRxObject*)
pPersReacObj)) != NULL)
{
pTmpPers->custom();
}
pPersReacObj->close();
} else {
// Or is it transient?
//
pObjReactor = (AcDbObjectReactor *)
(pReactors->at(i));
acutPrintf("\n\nTransient Reactor found");
// Report what kind we found.
//
if (pObjReactor->isKindOf(
AsdkSimpleObjReactor::desc()))
{
acutPrintf(" of type"
" AsdkSimpleObjReactor");
} else if (pObjReactor->isKindOf(AcDbEntityReactor::desc()))
{
acutPrintf(" of type"
" AcDbEntityReactor");
} else if (pObjReactor->isKindOf(AcDbObjectReactor::desc()))
{
acutPrintf(" of type"
" AcDbObjectReactor");
} else {
acutPrintf(" of unknown type.");
}
}
}
} else {
acutPrintf("\nThis entity has no reactors.\n");
}
Получение указателей на объекты в транзакции
И AcTransactionManager:: getObject () и AcTransaction:: getObject () может использоваться, чтобы получить объектные указатели от объекта IDs. Указатели, таким образом полученные связаны с самой современной транзакцией. Попытка получить указатель, используя любых других операционных результатов по ошибке. Также, указатели, таким образом полученные имеют силу до транзакции они связаны с, или одна из содержащих транзакций, прерван. Когда наиболее удаленные операционные концы, изменения{*замены*} на всех имеющих силу указателях до того момента совершены{*переданы*}.
Оба из getObject () функции берут параметр типа AcDb:: OpenMode, и Вы можете получить объектный указатель для чтения, записывать, или уведомлять.
Все эти запросы преуспевают если бы не один случай: если объект уведомляет, и запрос должен получить указатель для записи (то есть с намерением изменения этого), ошибка (eWasNotifying) возвращена. Объект не должен измениться, в то время как это уведомляет другие относительно его состояния.
Если Вы используете getObject () функция, чтобы получить объектный указатель, Вы никогда не должны вызвать близко () на том объектном указателе. Запрос близко () имеет силу только, если Вы получили указатель, используя acdbOpenObject () или объект был недавно создан. Для подробной информации на том, когда Вы можете вызывать близко () на объектном указателе, см. следующие секции, “ Недавно Созданные Объекты и транзакции ” и “ Смешивание Модели Транзакции с Открытым и Близким Механизмом. ”
Получение ввода пользователя
Несколько глобальных функций позволяют ObjectARX-приложению запросить данные в интерактивном режиме от пользователя AutoCAD.
Порядок поиска
Когда команда вызвана, стек команды обыскан названием{*именем*} группы, затем названием{*именем*} команды в пределах группы. Вообще, первая группа зарегистрировалась, будет первый обысканный, но Вы не можете всегда предсказывать, каков этот заказ{*порядок*} будет. Используйте AcEdCommandStack:: popGroupToTop () функция, чтобы определить, что специфическая группа должна быть обыскана сначала. На уровне пользователей, опция Group команды ARX позволяет пользователю определять которую группу искать сначала.
Последовательность событий в приложении ObjectARX
Процесс проходящих сообщений между AutoCAD и приложением ObjectARX течет почти полностью в одном направлении — от AutoCAD до приложения ObjectARX. Следующая диаграмма показывает типичной последовательности прохождения сообщения.
Если приложение загружено, когда рисунок уже открытый, последовательно посланы сообщения kInitAppMsg и kLoadDwgMsg. Когда приложение ObjectARX разгружено, в то время как сеанс редактирования происходит - kUnloadDwg и kUnloadApp.
Постоянные AcGe примитивы
Этот раздел описывает классы, имел обыкновение обеспечивать инерцию для AcGe примитивов, и иллюстрирует выполнение инерции. Три класса используются, чтобы обеспечить инерцию для AcGe
примитивов: AcGeFiler, AcGeLibVersion, и AcGeFileIO.
AcGeFiler - абстрактный класс, формирующий интерфейс для требований преобразования в последовательную форму AcGe. Пользователь должен обеспечить выполнение, полученное из AcGeFiler. В частности читая и пишущий внешних примитивов, и всех поверхностей кроме AcGePlane, требуют dwgFiler () функция на AcGeFiler, который будет осуществлен.
AcGeLibVersion формирует версию AcGe. Это поддержано системой.
Пользователь AcGe следит за версией AcGe, используемого через глобальную переменную, AcGe:: gLibVersion. Все записи и читают AcGe примитивов, выполнены в контексте версии используемого AcGe. Как правило, пользователь должен записать AcGe:: gLibVersion к файлу перед записью любого другого AcGe примитива (соответственно, это было бы первое чтение объекта AcGe от файла в последующем чтении). Следующие функции используются, чтобы записывать и читать этот объект (также см. следующее обсуждение AcGeFileIO класса):
Acad::ErrorStatus outFields (AcGeFiler*, const AcGeLibVersion&)
Acad::ErrorStatus inFields (AcGeFiler*, AcGeLibVersion&)
Функции I/O файла AcGe примитивов - scoped в пределах AcGeFileIO класса.
Они - коллекция статических функций для чтения и записи AcGe
примитивов.
Постоянство Данных Диалога
CAcUiDialog и классы CACUITAB автоматически наследуют постоянство. Постоянство, как определено диалогами и средством управления в AcUi15.dll, означает, что память{*хранение*} для любого и всего пользователя модальные диалоги в AutoCAD, полученном из этих классов сохранит данные с текущим параметром пользователя, делая это виртуальное предпочтение.
Ваш диалог должен иметь уникальное имя, потому что это будет использовать общедоступную область пространства{*пробела*} системного реестра параметра пользователя. Учитывая, что разработчики обычно создают их приложения, использующие их зарегистрированный префикс разработчика, следующий метод рекомендуется:
Module-name:dialog-name
Например, если бы ваше ObjectARX-приложение названо AsdkSample, и Вы имеете диалог названные Координаты, Вы назвали бы это AsdkSample:Coordinates.
Имеются два типа постоянства данных диалога: из -the-box и определенный разработчиком.
Из -the-box постоянства обращается{*относится*} к позиции диалога, размеру, и размерам столбца представления{*вида*} списка. Определено разработчиком обращается{*относится*} к любым данным, которые разработчик выбирает сохранять в параметре пользователя или в течение срока службы{*продолжительности жизни*} или смещения диалога и который может быть восстановлен{*отыскан*} поперек обращений диалога.
Построение заказного диалога с табуляторами, расширяемого
Создайте ваш табулированный диалог, используя CAcUiTabMainDialog для основной структуры{*рамки*} диалога и CACUITABCHILDDIALOG для каждой позиции табуляции. В OnInitDialog () или конструкторе CACUITABMAINDIALOG немедленно вызывают SetDialogName () с изданным именем вашего расширяемого диалога. ObjectARX-приложения будут использовать это имя, чтобы добавить позиции табуляции к вашему диалогу. После того, как Вы добавляете, что ваши позиции табуляции с звонят к AddTab (), в OnInitDialog, вызывать AddExtendedTabs (). Помните, что ваш табулированный диалог может иметь любое число добавленных позиций табуляции в этом, так что не примите установленное число позиций табуляции в другом месте в коде диалога.
Например
BOOL CPrefTabFrame:: OnInitDialog ()
// Инициализация Диалога для моей табулированной структуры{*рамки*} диалога.
{
SetDialogName
("Предпочтение");
CAcUiTabMainDialog:: OnInitDialog ();
...
// Добавить мои позиции табуляции здесь.
m_tab.AddTab(0,IDS_FILES_TABNAME,IDD_FILES_TAB,&m_filesTab);
m_tab.AddTab(1,IDS_PERF_TABNAME,IDD_PERF_TAB,&m_performTab);
m_tab.AddTab(2,IDS_COMP_TABNAME,IDD_COMP_TAB,&m_compatTab);
// Добавить любые расширенные{*продленные*} позиции табуляции. Этот запрос состоит в том то, что делает это
// Расширяемая позиция табуляции диалога
AddExtendedTabs ();
}
Поверхности
Ориентацию поверхности частично определяют ее оцененные нормальные векторы.
Параметрическая поверхность имеет два параметра, u и v, каждое представляет направление параметрических линий на поверхности. Если Вы берете векторное произведение u касательного вектора и v касательного вектора в той же самой точке, Вы получаете вектор, который является нормальным к поверхности. Этот вектор - естественная нормаль поверхности в той точке. Вы можете полностью изменять ориентацию поверхности, вызывая следующую функцию:
AcGeSurface&
AcGeSurface::reverseNormal()
Поверхностный вычислитель возвращает или естественную нормаль или ее инверсию, в зависимости от того, был ли reverseNormal () вызван четный или нечетное число времен. Следующая функция возвращает значение "ИСТИННО", если ориентация поверхности - напротив естественной ориентации:
Adesk::Boolean
AcGeSurface::isNormalReversed() const
Этот пример создает круг и проектирует это на план XY. Тип проектируемого примитива тогда проверен, чтобы видеть какой примитив, в который это проектировалось:
AcGePlane plane; // Constructs XY-plane.
AcGePoint3d p1(2,3,5);
AcGeVector3d v1(1,1,1);
AcGeCircArc3d circ (p1, v1, 2.0);
AcGeEntity3d *projectedEntity = circ.project(plane,v1);
if (projectedEntity->type() == AcGe::kEllipArc3d)
...
else if (projectedEntity->type() == AcGe::kCircArc3d)
...
else if (projectedEntity->type() == AcGe::kLineSeg3d)
...
Следующий пример создает НЕОДНОРОДНЫЙ РАЦИОНАЛЬНЫЙ В-СПЛАЙН, изгибают, и находит самую близкую точку на кривой к сути p1. Самая близкая точка возвращена как объект AcGePointOnCurve3d, от которого координаты и значение параметра получены:
AcGeKnotVector knots;
AcGePoint3dArray cntrlPnts;
AcGePointOnCurve3d pntOnCrv;
AcGePoint3d p1(1,3,2);
knots.append (0.0);
knots.append (0.0);
knots.append (0.0);
knots.append (0.0);
knots.append (1.0);
knots.append (1.0);
knots.append (1.0);
knots.append (1.0);
cntrlPnts.append (AcGePoint3d(0,0,0));
cntrlPnts.append (AcGePoint3d(1,1,0));
cntrlPnts.append (AcGePoint3d(2,1,0));
cntrlPnts.append (AcGePoint3d(3,0,0));
AcGeNurbCurve3d nurb (3, knots, cntrlPnts);
nurb.getClosestPointTo(p1,pntOnCrv);
p2 = pntOnCrv.point();
double param = pntOnCrv.parameter();
Повторение
Фильтры Точки ввода и мониторы могут быть вызваны множественные времена для единственной точки ввода при следующих условиях:
§
когда КЛАВИША ТАБУЛЯЦИИ нажата после события движения цифрового преобразователя, объектный поспешный кандидат на позицию курсора циклически повторяется. Как только движение курсора обнаружено, объектный поспешный кандидат, циклически повторяющий индекс сброшен.
§ когда зарегистрированный фильтр точки ввода возвращает индикацию Булевой переменной, чтобы повторить для точки, системные подсказки для нового события, не возвращение точка от события которое будет повторено.
Повторно неиспользуемые команды AutoCAD
Некоторые команды AutoCAD неповторно используемы. Они - команды, которые просят о вводе пользователя и поэтому обычно позволили бы выключатель окна документа, но вызовут серьезные осложнения, если это было переключено, или если команда была вызвана{*названа*} снова в другом документе. Три команды соответствуют эту конфигурацию:
n TABLET
n NEW
n OPEN
Команда TABLET ограничена, потому что пользователь определяет, как устройство управления позицией собирается работать, так, чтобы операция была должна быть закончена прежде, чем что - нибудь еще может быть сделано. Поскольку НОВЫЙ и ОТКРЫТЫЙ должны открыть окно, и затем просить о имени (когда FILEDIA=0), переключая к другому окну в тот момент мог быть подвержен ошибкам.
Повторно неспользуемые команды
Повторно неиспользуемые команды - те команды, которые не могут быть вызваны в одном документе когда уже в продвижении различного документа. Это должно быть расценено как способ делать ваше приложение MDI-знающими(Aware) быстро, без того, чтобы иметь необходимость изолировать всю вашу командо-определенную информацию о состоянии.
Предложения конфигурации
ObjectDBX клиентское приложение может выбирать один из следующих методов отобразить содержание DWG
file:
§
Приспосабливают снабженный SimpleView библиотечный источник к использованию ведущими приложениями, и используют все три компонента. Изучение и приспосабливается, любой код от ViewAcDb производит выборку приложения как необходимо. Это - вероятно лучший способ статься знакомым с использованием AcGix и SimpleView AcGix
выполнение платформы. Однако, Вы свободны изменить SimpleView источник как желательно.
§ Формируют модуль, чтобы управлять желательной графической системой через AcGix библиотеку. Это позволяет многое из существующей логики разработки многократно использоваться и все еще предлагает значительную гибкость в фактической сопроводительной графике и выполнении. Это вовлекло бы использование AcGix модуля, но записи выполнения AcGiViewport и AcGixVectorTaker на пустом месте, в действительности замена SimpleView и-или WhipView библиотек полностью.
§ Используют WhipView подсистему, чтобы сохранить элементы SimpleView, необходимого, чтобы поддержать определение AcGixBlockView. Это включает снабженную комбинацию WHIP! И HEIDI DLLs. Прямое использование WHIP! И HEIDI компоненты не поддержаны в этом выпуске.
§ Записывают, полное заказное выполнение AcGi
связывает с помощью интерфейса и не использует никакой из снабженного AcGix, SimpleView, или WhipView компонентов. Вы можете достигать максимального выполнения этот путь с максимальным количеством работы развития.
Преобразования
Функции, описанные в этой секции - утилиты для преобразования типов данных и модулей.
Графический конвейер может применять три возможных преобразования к примитиву:
§
блочные преобразования примитива
§ преобразование взгляда области просмотра
§ перспективное преобразование (если перспектива позволяется от DVIEW)
Каждое преобразование производит новый тип координат, как показано в следующем рисунке. Если не в перспективном режиме, глаз и координаты дисплея идентичен.
(фронтальное и заднее отсечение выполнено здесь если определено)
Для REGEN, HIDE, и SHADE команд, мировые координаты примитива посланы через графический конвейер, показанный в числе{*рисунке*} выше. Преобразование представления{*вида*} определяет специфическое представление{*вид*} мировых координат, аналогичных рассмотрению сцены с камерой. Камера имеет местоположение в мировом пространстве{*пробеле*} и специфической ориентации к мировой координате “сцена”.
Когда преобразование представления{*вида*} полно, мировые координаты преобразованы к координатам глаза, смотрящим вниз Z оси камеры.
Если перспектива позволяется, координаты глаза преобразованы, чтобы отобразить координаты.
Это преобразование возводит в степень раздел{*деление*} согласно тому, как далеко кое-что является от камеры, так, чтобы объекты дальше далеко от камеры казались меньшими чем объекты ближе на камеру.
Следующие разделы обсуждают эти системы координат в большей подробности.
Преобразования Системы координат
AcedTrans () функция транслирует точку или смещение от одной системы координат в другой. Требуется параметр точки, запятая, которая может интерпретироваться или как трехмерная точка или трехмерный вектор смещения. Это управляется параметром, вызвал disp, который должен быть отличный от нуля, если с запятой обращаются как вектор смещения; иначе, с запятой обращаются как точка. Оттранслированная точка или вектор возвращены в параметре результата " передача параметра по ссылке ", который, подобно запятой, имеют тип ads_point.
Параметры, которые определяют эти две системы координат, от и до, являются обоими буферами результатов. От параметра определяет систему координат, в которой запятая выражена, и к параметру определяет систему координат результата. И от и до параметр может определить систему координат следующим способом:
§ целочисленный код (restype == RTSHORT) который определяет WCS, текущий UCS, или текущий DCS (или текущей области просмотра или пространства листа).
§ имя примитива (restype == RTENAME), как возвращено одним имени примитива или выбора устанавливают функции. Это определяет ECS названного примитива. Для плоских примитивов, ECS может отличиться от WCS. Если ECS не отличается, преобразование между ECS и WCS - операция тождества.
§ трехмерный вектор вытеснения (restype == RT3DPOINT), который является другим методом определения ECS примитива. Векторы Вытеснения всегда представляются в мировых координатах; вектор вытеснения (0,0,1) определяет WCS непосредственно.
Следующее - описания систем координат AutoCAD, которые могут быть определены от и до параметров.
WCS |
Мировая система координат. Система координат “ссылки”. Все другие системы координат определены относительно WCS, который никогда не изменяется. Значения, измеренные относительно WCS устойчивы поперек изменений{*замен*} к другим системам координат. | ||
UCS |
Система координат Пользователя. “Рабочая” система координат. Все точки прошли к AutoCAD, командует, включая возвратился от подпрограмм AutoLISP и внешних функций, - точки в текущем UCS (если пользователь не предшествует им с * в Приглашении ко вводу команды). Если Вы хотите, чтобы ваше приложение послало координаты в WCS, ECS, или DCS к AutoCAD командует, Вы должны сначала преобразовать их к UCS, вызывая acedTrans (). | ||
ECS |
Система координат Примитива. Значения Точки, возвращенные acdbEntGet () выражены в этой системе координат относительно примитива непосредственно. Такие точки бесполезны, пока они не преобразованы{*конвертированы*} в WCS, текущий UCS, или текущий DCS, согласно предназначенному использованию примитива. Наоборот, точки должны быть оттранслированы в ECS прежде, чем они написаны к базе данных посредством acdbEntMod () или acdbEntMake (). | ||
DCS |
Система координат Дисплея. Система координат, в которую объекты преобразованы прежде, чем они отображены. Начало координат DCS - точка, сохраненная в переменной ЦЕЛЕВОЙ СИСТЕМЫ AutoCAD, и ее Z ось - направление рассмотрения. Другими словами, область просмотра - всегда представление{*вид*} плана его DCS. Эти координаты могут использоваться, чтобы определить, где кое-что появляется пользователю AutoCAD. Когда от и до целочисленных кодов - 2 и 3, в любом порядке, 2 указывает, что DCS для текущей области просмотра пространства модели, и 3 указывает DCS для пространства листа (PSDCS). Когда этот код 2 используется с целым числом, закодируют другой чем 3 (или другие средства определения системы координат), принято указать DCS пространства потока (пространство листа или пространство модели), и другой параметр принят, чтобы указать систему координат в текущем пространстве. | ||
PSDCS |
DCS Пространства листа. Эта система координат может быть преобразована только к или от DCS в настоящее время активной области просмотра пространства модели. Это - по существу 2-ое преобразование, где X и координаты Y всегда масштабируются и смещены, если disp параметр - 0. Координата Z масштабируется, но никогда не оттранслирована; это может использоваться, чтобы найти коэффициент масштаба между этими двумя системами координат. PSDCS (целое число закодируют 2) может быть преобразован только в текущую область просмотра пространства модели: если от параметра равняется 3, к параметру должен равняться 2, и наоборот. |
Следующий пример транслирует точку с WCS в текущий UCS.
ads_point pt, result;
struct resbuf fromrb, torb;
pt[X] = 1.0;
pt[Y] = 2.0;
pt[Z] = 3.0;
fromrb.restype = RTSHORT;
fromrb.resval.rint = 0; // WCS
torb.restype = RTSHORT;
torb.resval.rint = 1; // UCS
// disp == 0 indicates that pt is a point:
acedTrans(pt, &fromrb, &torb, FALSE, result);
Если текущий UCS вращается на 90 градусов против часовой стрелки во всем мире Z ось, запрос к acedTrans() устанавливают результат к сути (2.0, -1.0,3.0).
Однако, если acedTrans() вызван, как показано в следующем примере, результат - (-2.0,1.0,3.0).
acedTrans(pt, &torb, &fromrb, FALSE, result);
Прерывания от пользователя
Функции ввода пользователя и acedCommand (), acedCmd (), acedEntSel (), acedNEntSelP (), acedNEntSel (), acedDragGen (), и acedSSGet () функции возвращают RTCAN, если пользователь AutoCAD отвечает, нажимая ESC. Внешняя функция должна обращаться с этим ответом как запрос отмены задания и возвращение немедленно.
ObjectARX также обеспечивает функцию, acedUsrBrk (), это явно проверяет{*отмечает*}, нажал ли пользователь ESC. Эта функция позволяет приложениям ObjectARX проверить прерывание пользователя.
Приложение не должно вызвать acedUsrBrk () если это не исполняет длинное вычисление между взаимодействиями с пользователем. Функция acedUsrBrk () никогда не должна использоваться как замена{*заместитель*} проверки значения, возвращенного функциями ввода пользователя, которые могут возвращать RTCAN.
В некоторых случаях, приложение будет хотеть игнорировать запрос отмены пользователя. Если дело обстоит так, это вызвало acedUsrBrk () чтобы очистить запрос; иначе, ESC будет все еще невыполненный{*выдающийся*} и вызовет следующий запрос ввода пользователя терпеть неудачу. (Если приложение игнорирует ESC, это должно печатать сообщение, чтобы сообщить пользователю, которого это делает так.) Всякий раз, когда ObjectARX-приложение вызвано, условие ESC автоматически очищено.
Например, следующий кодовый фрагмент терпит неудачу, если пользователь вводит ESC в подсказку.
int test()
{
int i;
while (!acedUsrBrk()) {
acedGetInt("\nInput integer:", &i); // WRONG
.
.
.
}
}
Слегка изменяемый кодовый фрагмент, который следует правильно за метками ввод ESC без того, чтобы вызвать acedUsrBrk ().
int test()
{
int i;
for (;;) {
if (acedGetInt("\nInput integer:", &i) != RTNORM)
break;
...
}
}
Следующая выборка изменяет{*заменяет*} условие цикла. Это строительство также работает правильно.
int test()
{
int i;
while (acedGetInt("\nInput integer:", &i) == RTNORM) {
...
}
}
Имеющее силу место, чтобы использовать acedUsrBrk () находится в длинной операции. Например, код, который шагает через каждый примитив в базу данных рисунка, может быть потребление времени и должен вызвать acedUsrBrk ().
Причины Повторного вызова
Причина повторного вызова, возвращенная как поле причины пакета повторного вызова, определяет, почему действие или повторный вызов произошли. Его значение установлено для любого вида действия, но Вы должны осмотреть это только, когда действие связано с edit_box, list_box, image_button, или неперекрывающим расположением слайдера. Следующая таблица показывает возможные значения.
Прикладное Имя
Вся информация потребности Дизайн-центра AutoCAD от довольного средства доступа, участвующего в его заказном режиме сохранена под этой ключом. Имя ключа было бы имя приложения, поскольку это появляется в заказном режиме.
Под ключом Application Name может быть следующие дополнительные клавиши:
§
Расширения
Это содержит список расширений что довольные поддержки средства доступа (типа .dwg). Каждый ключ под расширениями представляет одно расширение. Имя ключа - имя расширения.
§ Средство поиска
Этот ключ необязатен. Если приложение хочет участвовать в находящихся функциональных возможностях, то это требовано, чтобы заполнить этот ключ.
Следующие значения системного реестра демонстрируют ветви системного реестра приложений:
[HKEY_LOCAL_MACHINE\SOFTWARE\Autodesk\AutoCAD\R15.0\ACAD-x:
xxx\AutodeskApps\AcadDC\Applications\Autodesk\Finder\Drawings]
"LocalName"="Drawings"
"File Based Search"=dword:00000001
"Advanced Search"=dword:00000001
"Date Search"=dword:00000001
"Date Available"=dword:00000001
"Size Available"=dword:00000001
"Extension"=".dwg"
"Auto Append"=dword:00000001
[HKEY_LOCAL_MACHINE\SOFTWARE\Autodesk\AutoCAD\R15.0\ACAD-x:
xxx\AutodeskApps\AcadDC\Applications\Autodesk\Finder\Drawings
\Advanced Properties]
"Advanced Property1"="Block name"
"Advanced Property2"="Block and drawing description"
"Advanced Property3"="Attribute tag"
"Advanced Property4"="Attribute value"
[HKEY_LOCAL_MACHINE\SOFTWARE\Autodesk\AutoCAD\R15.0\ACAD-x:
xxx\AutodeskApps\AcadDC\Applications\Autodesk\Finder\Drawings
\Properties]
"Property1"="File Name"
"Property2"="Title"
"Property3"="Subject"
"Property4"="Author"
"Property5"="Keywords"
Прикладной Контекст
Короткий для “ прикладной контекст выполнения. ” См. “ Контекст Выполнения, Приложение ” на странице 421.
Прикладной контекст выполнения
Прикладной контекст выполнения отличен от контекста выполнения документа. Прикладной контекст выполнения обрабатывает цикл сообщения Windows.
и связанные объекты, которые являются
Полное выполнение программирует и связанные объекты, которые являются обычными к всем открытым документам, типа MFC класса CwinApp. Имеется один и только одно приложение в обращение выполнимой программы Windows.
A. Перемещение ADS программ к ObjectARX
Чтобы упростить перемещение приложений AutoCAD development system к среде программы ObjectARX, библиотека ADS была перенесена к среде программы ObjectARX. ObjectARX версия почти идентична версии ADS. Это приложение обеспечивает сравнение того, как программы загружены, и в ADS и ObjectARX, и включает типовую программу, которая была перенесена от ADS до ObjectARX.
§
Перемещающий к ObjectARX
§ Загрузка Приложений: ADS против ObjectARX
§ Формирование ADS Приложения в Среде Программы ObjectARX
§ Типовое ObjectARX-приложение
§ ObjectARX-эксклюзивный Тип данных
B. Программируемые Диалоговые окна
ObjectARX содержит набор функций, который все вместе вызван Программируемое Диалоговое окно (PDB) пакет. PDB функции определяют средство управления диалогового окна, функциональные возможности, и соединение к приложению.
Эта глава описывает, как использовать PDB. См. ObjectARX Ссылку для резюме и каталога функций, упомянутых в этой главе. Для руководящих принципов проекта диалогового окна, см. Руководство Настройки AutoCAD.
Для информации относительно использования см главы 8, “ MFC
Темы.”
§
Краткий обзор
§ Иерархическая структура
§ Определения и Объявления
§ Обработка Неперекрывающих расположений
Примера
Следующие функции орудий приложения примера, которые называются
Когда приложение загружено и разгруженный. Его функция инициализации добавляется
Две новых команды к AutoCAD: СОЗДАЙТЕ и ВЫПОЛНИТЬ ИТЕРАЦИИ. Это также инициализирует Новый класс AsdkMyClass и прибавляет это к ObjectARX иерархии с
AcrxBuildClassHierarchy () функция (. AsdkMyClass описан в “ Пример Заказного Объектного Класса ” на странице 338.)
// Функция инициализации, называемая от acrxEntryPoint ()
// Функция в течение kInitAppMsg случая{*регистра*} используется, чтобы прибавить команды
// К команде располагают в стеке и прибавлять классы к ACRX классу
// Иерархия.
//
void
initApp()
{
acedRegCmds->addCommand("ASDK_DICTIONARY_COMMANDS",
"ASDK_CREATE", "CREATE", ACRX_CMD_MODAL,
createDictionary);
acedRegCmds->addCommand("ASDK_DICTIONARY_COMMANDS",
"ASDK_ITERATE", "ITERATE", ACRX_CMD_MODAL, iterateDictionary);
AsdkMyClass::rxInit();
acrxBuildClassHierarchy();
}
// Функция очистки, называемая от acrxEntryPoint ()
// Функция в течение kUnloadAppMsg случая{*регистра*} удаляет это приложение
// Команда сходит с команды, располагают в стеке, и удаляет это приложение
// Заказные классы от ACRX иерархии классов во время выполнения.
//
void
unloadApp()
{
acedRegCmds->removeGroup("ASDK_DICTIONARY_COMMANDS");
// Удалить AsdkMyClass класс с ACRX времени выполнения
// Иерархия классов. Если это сделано, в то время как база данных
// Все еще активный, это должно вызвать все объекты{*цели*} класса
// AsdkMyClass, чтобы быть превращен в proxies.
//
DeleteAcRxClass (AsdkMyClass:: desc ());
}
Приложения ObjectArx с Динамически Связанным MFC
Привилегированный метод для формирования MFC-ОСНОВАННОГО Приложения ObjectArx состоит в том, чтобы использовать динамически связанные MFC библиотеки.
? — Приложения Списка
Перечисляет в настоящее время загруженные приложения ARX.
Приложения Загрузки: ADS против ObjectARX
Загрузка Приложения в среде программы ObjectARX более гибка чем в среде программы ADS. В среде программы ADS, приложение ADS
всегда остается в памяти.
И в ObjectARX и средах программы ADS, приложение может быть загружено автоматически, когда AutoCAD вызван, если прикладное имя перечислено в acad.rx или acad.ads, соответственно.
В среде программы ObjectARX, некоторые приложения, типа Выполняют, могут быть вызваны, когда одна из ее функций вызвана{*названа*}. Эта возможность сохраняет память для больших приложений подобно, выполняют, потому что это загружает приложение только в течение короткого времени в течение сессии рисунка.
ObjectARX и загрузка приложения ADS
отличен следующими способами:
§
когда рисунок закрыт или когда другой рисунок открыт, ObjectARX-приложения не разгружены. ObjectARX-приложение разгружено, когда AutoCAD закончен или когда приложение не имеет никаких иждивенцев и acedArxUnload () или эквивалентная функция вызвана{*названа*}.
§ В среде программы ObjectARX, рисунок присутствует, когда или kLoadDwg или kUnloadDwg сообщения или оба получены. Эти сообщения получены в случае цикл, когда или ADS или ObjectARX-приложение инициализирован или разгружен.
§ В среде программы ADS, рисунок присутствует, когда kInitAppMsg или kUnloadAppMsg получены.
ОБРАТИТЕ ВНИМАНИЕ В среде программы ObjectARX, не предположите, что рисунок присутствует, когда kInitAppMsg или kUnloadAppMsg получены.
§ В среде программы ObjectARX, вызовите (arxload) или acedArxLoad () чтобы загрузить приложения. В среде программы ADS, вызовите (xload) или ads_xload () чтобы загрузить приложения.
§ ObjectARX-приложения, перечисленные в acad.rx загружены автоматически, когда AutoCAD вызван, и в отличие от приложений ADS, приложения ObjectARX инициализированы прежде, чем рисунок присутствует и прежде, чем ADS или Визуальные среды LISP инициализирован. В среде программы ADS, коллега{*копия*} acad.rx - acad.ads. Поместите названия{*имена*} модулей прикладных программ в соответствующем файле, одно имя в линию.
Примечания относительно Расширенных Данных
Несколько функций ObjectARX обеспечиваются, чтобы обработать расширенные данные. Расширенные данные примитива следуют за нормальными данными определения примитива. Это иллюстрировано следующим рисунком, которое показывает схеме списка буфера результата для примитива, содержащего расширенные данные.
Расширенные данные примитива могут быть отысканы, вызывая acdbEntGetX (), который является подобным acdbEntGet (). AcdbEntGetX () функция отыскивает нормальные данные определения примитива и расширенные данные для приложений, указанных в acdbEntGetX () запрос.
ОБРАТИТЕ ВНИМАНИЕ, когда расширенные данные отысканы acdbEntGetX (), начало расширенных данных обозначено -3 кодом стража; -3 страж находится в буфере результатов, который предшествует первой 1001 группе. 1001 группа содержит прикладное имя первого отысканного приложения, как показано в рисунке.
Организация Расширенных Данных
Расширенные данные состоят из одного или большее количество 1001 групп, каждая из которых начинается с уникального прикладного имени. Прикладные названия - строковые значения. Расширенные группы данных возвратились acdbEntGetX () следуют за данными определения в порядке, в котором они сохранены в базе данных.
В пределах группы каждого приложения, содержания, значение, и организация данных определено приложением; AutoCAD поддерживает информацию, но не использует это. Коды Группы для расширенных данных находятся в 1000-1071 диапазоне, следующим образом:
Строка | 1000. Строки в расширенных данных могут быть до длиной 255 байтов (с 256-ым байтом, зарезервированным для нулевого символа). | ||
Прикладное имя | 1001 (также строковое значение). Прикладные названия могут быть до длиной 31 байтов (32-ой байт зарезервирован для нулевого символа) и должен твердо придержаться правил для названий таблицы идентификаторов (типа названий уровня). Прикладное имя может содержать символы, цифры, и специальные символы $ (долларовый признак), - (дефис), и _ (символ подчеркивания). Это не может содержать пробелы. Символы на имя преобразованы к верхнему регистру. |
Системы координат
Этот пример берет долю линии, определенную в модели, координирует и создает ее эквиваленты в координатах дисплея и глазе. Когда отображено, все линии наложатся.
void
AsdkCoordSamp::viewportDraw(AcGiViewportDraw* pV)
{
pV->subEntityTraits().setFillType(kAcGiFillAlways);
const int count = 3;
AcGePoint3d verts[count];
verts[0] = AcGePoint3d(0.0, 0.0, 0.0);
verts[1] = AcGePoint3d(1.0, 0.0, 0.0);
verts[2] = AcGePoint3d(1.0, 1.0, 0.0);
// Draw model space line segment.
//
pV->subEntityTraits().setColor(kBlue);
pV->geometry().polygon(count, verts);
// Compute the line’s representation in eye space.
//
AcGeMatrix3d mat;
pV->viewport().getModelToEyeTransform(mat);
for (int i = 0; i < count; i++) {
verts[i].x += 0.01;
verts[i].y += 0.01;
verts[i].z += 0.01;
verts[i].transformBy(mat);
}
// Display the eye coordinate equivalent of the
// model space polygon.
//
pV->subEntityTraits().setColor(kGreen);
pV->geometry().polygonEye(count, verts);
// Convert from eye to display coordinates.
//
for (i = 0; i < count; i++) {
verts[i].x += 0.01;
verts[i].y += 0.01;
verts[i].z += 0.01;
}
// Draw the display space equivalent of the
// model space polygon.
//
pV->subEntityTraits().setColor(kRed);
pV->geometry().polygonDc(count, verts);
}
Определение Невидимых линий для Объекта для Стандартного Дисплея
Этот пример отображает пирамиду, показывая передние грани в желтом и обратные грани в синем, чтобы дать Вам идею относительно видимых и скрытых граней пирамиды. Пример показывает применение{*обращение*} преобразования " модель к глазу " и затем перспективное преобразование. Это использует координаты глаза, чтобы рисовать примитив и использование показов isPerspective (), doPerspective (), getFrontandBackClipValues (), polylineDc (), polylineEye () и polyline().
Преобразовывать координату глаза выравнивают долю, чтобы отобразить пространство
1 если вид имеет планы отсечения в силе, зажим доля линии координаты xглаза к ним.
2 если перспектива включена, то, исполняет преобразование от координат глаза до перспективы.
Если вы используете polygonEye (), polygonDc (), polylineEye (), или polylineDc () функции AcGiViewportGeometry, Вы должны вызвать AcGiWorldGeometry:: setExtents () чтобы установить поле ограничения для примитива. Это позволит AutoCAD знать, сколько пространства примитив требует и используется в ZOOM Степени. SetExtents () функция обычно вызвана, когда примитив находится в мировых координатах, чтобы определить самое маленькое поле, которое будет соответствовать вокруг примитива в мировых координатах.
AsdkViewGeomSamp::AsdkViewGeomSamp() : mNumVerts(4)
{
mVerts[0] = AcGePoint3d(0.0, 0.0, 0.0);
mVerts[1] = AcGePoint3d(1.0, 0.0, 0.0);
mVerts[2] = AcGePoint3d(0.0, 1.0, 0.0);
mVerts[3] = AcGePoint3d(0.0, 0.0, 1.0);
}
Acad::ErrorStatus
AsdkViewGeomSamp::transformBy(const AcGeMatrix3d &xfm)
{
assertWriteEnabled();
for (Adesk::UInt32 i = 0; i < mNumVerts; i++) {
mVerts[i].transformBy(xfm);
}
return Acad::eOk;
}
Adesk::Boolean
AsdkViewGeomSamp::worldDraw(AcGiWorldDraw* pW)
{
// Draw a pyramid.
//
// If this is the regular AutoCAD DISPLAY mode...
//
if (pW->regenType() == kAcGiStandardDisplay) {
// From each viewport’s vantage point, figure out
// which sides of the pyramid are visible,
// then make the visible ones yellow and the hidden
// ones blue.
//
// Set the extents of the pyramid here because
// AcGiViewportGeometry’s polylineEye() doesn’t
// set extents.
//
for (Adesk::UInt32 i = 0; i < mNumVerts; i++) {
AcGePoint3d pt[2];
pt[0] = mVerts[i];
pt[1] = mVerts[(i + 1) % mNumVerts];
pW->geometry().setExtents(pt);
}
return Adesk::kFalse; // Call viewport draws.
}
// Otherwise, give HIDE, SHADE, RENDER, or proxy graphics
// a pyramid with filled faces.
//
const Adesk::UInt32 faceListSize = 16;
static Adesk::Int32 faceList[faceListSize] = {
3, 0, 1, 2,
3, 0, 2, 3,
3, 0, 3, 1,
3, 1, 2, 3
};
pW->geometry().shell(mNumVerts, mVerts, faceListSize, faceList);
return Adesk::kTrue; // Do not call viewportDraw.
}
void
AsdkViewGeomSamp::viewportDraw(AcGiViewportDraw* pV)
{
// For this viewport, draw a pyramid with yellow
// visible lines and blue hidden lines.
//
// Get this viewport’s net transform. This transform
// includes this entity’s block transforms and this
// viewport’s view transform; it does not include the
// perspective transform if we’re in perspective
// mode; that currently has to be applied separately
// when in perspective mode.
//
AcGeMatrix3d modelToEyeMat;
pV->viewport().getModelToEyeTransform(modelToEyeMat);
// Get the pyramid’s vertices.
//
AcGePoint3d A = mVerts[0];
AcGePoint3d B = mVerts[1];
AcGePoint3d C = mVerts[2];
AcGePoint3d D = mVerts[3];
// Convert them to the viewport’s eye coordinates.
//
A.transformBy(modelToEyeMat);
B.transformBy(modelToEyeMat);
C.transformBy(modelToEyeMat);
D.transformBy(modelToEyeMat);
// Save the eye coordinates.
//
AcGePoint3d AEye = A;
AcGePoint3d BEye = B;
AcGePoint3d CEye = C;
AcGePoint3d DEye = D;
// Perform the perspective transform if necessary.
//
if (pV->viewport().isPerspective()) {
pV->viewport().doPerspective(A);
pV->viewport().doPerspective(B);
pV->viewport().doPerspective(C);
pV->viewport().doPerspective(D);
}
// From that view, figure out which faces are
// facing the viewport and which are not.
//
int which_faces;
which_faces = ((C - A).crossProduct(B - A)).z > 0.0 ? 1 : 0;
which_faces |= ((D - A).crossProduct(C - A)).z > 0.0 ? 2 : 0;
which_faces |= ((B - A).crossProduct(D - A)).z > 0.0 ? 4 : 0;
which_faces |= ((B - D).crossProduct(C - D)).z > 0.0 ? 8 : 0;
// Those edges that meet between two faces that are
// facing away from the viewport will be hidden edges,
// so draw them blue; otherwise, they are visible
// edges. (This example is incomplete, as the test is
// indeterminate when the face is edge-on to the
// screen -- neither facing away nor toward the screen.)
// Draw the six edges connecting the vertices using eye
// coordinate geometry that can be clipped back and front.
//
AcGePoint3d verts[2];
Adesk::UInt16 color;
// AB
color = which_faces & 0x5 ? kYellow : kBlue;
pV->subEntityTraits().setColor(color);
verts[0] = AEye;
verts[1] = BEye;
pV->geometry().polylineEye(2, verts);
// AC
color = which_faces & 0x3 ? kYellow : kBlue;
pV->subEntityTraits().setColor(color);
verts[0] = AEye;
verts[1] = CEye;
pV->geometry().polylineEye(2, verts);
// AD
color = which_faces & 0x6 ? kYellow : kBlue;
pV->subEntityTraits().setColor(color);
verts[0] = AEye;
verts[1] = DEye;
pV->geometry().polylineEye(2, verts);
// CD
color = which_faces & 0xa ? kYellow : kBlue;
pV->subEntityTraits().setColor(color);
verts[0] = CEye;
verts[1] = DEye;
pV->geometry().polylineEye(2, verts);
// DB
color = which_faces & 0xc ? kYellow : kBlue;
pV->subEntityTraits().setColor(color);
verts[0] = DEye;
verts[1] = BEye;
pV->geometry().polylineEye(2, verts);
// BC
color = which_faces & 0x9 ? kYellow : kBlue;
pV->subEntityTraits().setColor(color);
verts[0] = BEye;
verts[1] = CEye;
pV->geometry().polylineEye(2, verts);
}
Получение Координат Окна
Этот пример показывает использование AcGiViewportDraw:: polylineDc () и AcGiViewport:: getViewportDcCorners () чтобы получить координаты дисплея области просмотра. Эта функция удобна, когда Вы рисуете графику, которые зависят от физического размещения области просмотра, типа значков и маркеров, которые изменяются с размером или границами области просмотра. Рисовавшая графика изменится с панорамированием и изменением масштаба изображения.
Пример рисует поле в правом верхнем углу области просмотра. Ширина поля и высота - всегда десятая часть самого короткого измерения области просмотра, и поле центрировано десятая часть самого короткого измерения области просмотра вниз и налево от правого верхнего угла области просмотра:
void
AsdkIconSamp::viewportDraw(AcGiViewportDraw* pV)
{
// Get the current viewport’s display coordinates.
//
AcGePoint2d lower_left, upper_right;
pV->viewport().getViewportDcCorners(lower_left, upper_right);
double xsize = upper_right.x - lower_left.x;
double ysize = upper_right.y - lower_left.y;
xsize /= 10.0;
ysize /= 10.0;
double xcenter = upper_right.x - xsize;
double ycenter = upper_right.y - ysize;
double half_xsize = xsize / 2.0;
double half_ysize = ysize / 2.0;
// Create a unit square.
//
const int num_verts = 5;
AcGePoint3d verts[num_verts];
for (int i = 0; i < num_verts; i++) {
verts[i].x = xcenter;
verts[i].y = ycenter;
verts[i].z = 0.0;
}
verts[0].x -= half_xsize;
verts[0].y += half_ysize;
verts[1].x += half_xsize;
verts[1].y += half_ysize;
verts[2].x += half_xsize;
verts[2].y -= half_ysize;
verts[3].x -= half_xsize;
verts[3].y -= half_ysize;
verts[4] = verts[0];
pV->subEntityTraits().setColor(kRed);
pV->geometry().polylineDc(num_verts, verts);
}
Вычисление Круга, чтобы Рисовать
Следующий пример рисует круг модуля, центрированный в начале координат. Точный рисовавший круг зависит от взгляда области просмотра круга. Цель состоит в том, чтобы рисовать круг с полилинией, которая имеет минимальное число заметных долей. С командой VPORTS, Вы можете создавать четыре области просмотра и затем нажимать на один и раскрывать на круге, затем нажимать на другой и копировать от этого. Когда Вы REGENALL, каждая область просмотра вычисляет ее собственный минимально сегментированное представление полилинии круга.
Это - то, как пример вычисляет необходимое число долей линии в полилинии. Сначала, учитывая круг данного радиуса, который центрирован в начале координат и расположен в XY плане, и дан вертикальную линию, которая пересекает X ось в радиусе - 0.5 пикселов, определять угол между X осью и долей линии, которая простирается от начала координат к сути, где вертикальная линия пересекает круг. Два pi разделенный этим углом обеспечивает минимальное число долей, необходимых полилинией, чтобы напомнить круг. Пользователь не будет способен дифференцировать индивидуальные доли линии, которые составляют круг, потому что визуальные различия - меньше чем пиксел.
Adesk::Boolean
AsdkTesselateSamp::worldDraw(AcGiWorldDraw *pW)
{
// Draw a red 1x1 drawing-unit square centered at the
// world coordinate origin and parallel to the XY-plane.
//
const Adesk::UInt32 num_pts = 5;
AcGePoint3d verts[num_pts];
verts[0] = verts[4] = AcGePoint3d(-0.5, -0.5, 0.0);
verts[1] = AcGePoint3d( 0.5, -0.5, 0.0);
verts[2] = AcGePoint3d( 0.5, 0.5, 0.0);
verts[3] = AcGePoint3d(-0.5, 0.5, 0.0);
pW->subEntityTraits().setColor(kRed);
pW->geometry().polyline(num_pts, verts);
// If regenType is kAcGiSaveWorldDrawForProxy, return
// Adesk::kTrue, otherwise return Adesk::kFalse to trigger
// calls to viewportDraw().
//
return (pW->regenType() == kAcGiSaveWorldDrawForProxy);
}
void
AsdkTesselateSamp::viewportDraw(AcGiViewportDraw *pV)
{
static double two_pi = atan(1.0) * 8.0;
// Get the number of pixels on the X- and Y-edges of
// a unit square centered at (0.0, 0.0, 0.0), in
// world coordinates.
//
AcGePoint3d center(0.0, 0.0, 0.0);
AcGePoint2d area;
pV->viewport().getNumPixelsInUnitSquare(center, area);
// If the area values are negative, then we are in
// perspective mode and the center is too close or
// in back of the viewport.
//
if (area.x > 0.0) {
// Print out the number of pixels along the
// Y-axis of the unit square used in
// getNumPixelsInUnitSquare.
//
AcGeVector3d norm(0.0, 0.0, 1.0);
AcGeVector3d dir(1.0, 0.0, 0.0);
char buf[100];
sprintf(buf, "%7.3lf", area.y);
pV->geometry().text(center, norm, dir, 1.0, 1.0, 0.0, buf);
// Draw a circle that depends on how big the circle
// is in the viewport. This requires
// figuring out the fewest number of segments needed
// by a polyline so that it doesn’t look segmented.
//
// The worldDraw() and viewportDraw() of
// an entity in a viewport are only called during a
// regen and not necessarily during a ZOOM or PAN.
// The reason is that a regen produces something
// akin to a very high resolution image internally,
// which AutoCAD can zoom in or pan around. That is,
// until you get too close to this image or any of
// its edges, at which point a regen is internally
// invoked for that viewport and a new internal
// image is created (ready to be mildly zoomed and
// panned).
//
double radius = 0.5;
double half_pixel_hgt = 2.0 / area.x; // In WCS
int num_segs = 8;
double angle = two_pi / num_segs;
if (half_pixel_hgt > radius / 2) {
// The circle is approximately the same or less
// than the size of a pixel. So, generate a very
// small octagon.
//
num_segs = 8;
} else {
// Given a circle centered at the origin of a
// given radius in the XY-plane, and given a
// vertical line that intersects the X-axis at
// ’radius - half a pixel’, what is the angle
// from the X-axis of a line segment from the
// origin to the point where the vertical line
// and the circle intersect? Two pi divided by
// this angle gives you a minimum number of
// segments needed by a polyline to look like
// a circle and not be able to differentiate
// the individual segments because the visual
// differences are less than the size of a
// pixel. (This is not the only way to figure
// this out but it’s sufficient.)
//
angle = acos((radius - 1.0 / (area.x / 2.0)) / radius);
double d_num_segs = two_pi / angle;
// Limit the number of segments from 8 to
// 128 and use whole numbers for
// this count.
//
if (d_num_segs < 8.0) {
num_segs = 8;
} else if (d_num_segs > 128.0) {
num_segs = 128;
} else {
num_segs = (int)d_num_segs;
}
}
// Calculate the vertices of the polyline from the
// start, around the circle, and back to the start
// to close the polyline.
//
angle = 0.0;
double angle_inc = two_pi / (double)num_segs;
AcGePoint3d* verts = new AcGePoint3d[num_segs + 1];
for (int i = 0; i <= num_segs; i++, angle += angle_inc)
{
verts[i].x = center.x + radius * cos(angle);
verts[i].y = center.y + radius * sin(angle);
verts[i].z = center.z;
}
pV->geometry().polyline(num_segs + 1, verts);
delete [] verts;
}
}
Использование Границ Обрезки в AcGi
ObjectARX позволяет Вам определять границу отсечения для геометрии, содержащейся в пределах таблиц перекрестных ссылок и блоков. Часть границы обрезки AcGi API позволяет составные объекты (блоки и объекты, которые ведут себя подобно блокам) чтобы выразить эти границы обрезки AcGi выполнению.
В течение worldDraw () или viewportDraw () любой drawable может определить многоугольную границу обрезки, чтобы отсечь ее графику. Следующие разделы описывают, как использовать эту особенность API.
Пример диалогового окна
Следующие разделы показывают, как создавать и отобразить типовое диалоговое окно.
Файл DCL определяет помеченный диалог “ Привет, мир ”, который содержит текстовое неперекрывающее расположение и единственную кнопку OK.
Пример формирования зависимостей в объекте
Следующий пример показывает, как Вы можете использовать реакторы, чтобы установить зависимости среди объектов базы данных. В этом примере, когда Вы изменяете одну линию, другие изменяются тоже.
class AsdkObjectToNotify : public AcDbObject
//
// AsdkObjectToNotify - customized AcDbObject for persistent
// reactor to notify.
//
{
public:
ACRX_DECLARE_MEMBERS(AsdkObjectToNotify);
AsdkObjectToNotify() {};
void eLinkage(AcDbObjectId i, double f=1.0)
{mId=i; mFactor=f; };
void modified(const AcDbObject*);
Acad::ErrorStatus dwgInFields(AcDbDwgFiler*);
Acad::ErrorStatus dwgOutFields(AcDbDwgFiler*) const;
Acad::ErrorStatus dxfInFields(AcDbDxfFiler*);
Acad::ErrorStatus dxfOutFields(AcDbDxfFiler*) const;
private:
AcDbObjectId mId;
double mFactor;
};
ACRX_DXF_DEFINE_MEMBERS(AsdkObjectToNotify, AcDbObject,
AcDb::kDHL_CURRENT, AcDb::kMReleaseCurrent,
0, ASDKOBJECTTONOTIFY, persreac);
// This function is called every time the line it’s
// "watching" is modified. When it’s called, it opens the
// other line of the pair and changes that line’s length to
// match the new length of the line that’s just been
// modified.
//
void
AsdkObjectToNotify::modified(const AcDbObject* pObj)
{
AcDbLine *pLine = AcDbLine::cast(pObj);
if (!pLine) {
const char* cstr = pObj->isA()->name();
acutPrintf("This is a %s.\n", cstr);
acutPrintf("I only work with lines. Sorry.\n");
return;
}
acutPrintf("\nReactor attached to %lx calling %lx.\n",
pLine->objectId(), mId);
// This open will fail during notification caused by a
// reactor being added to the entity or when this
// notification is in reaction to a change due to the
// other line’s reactor changing this line. This will
// properly prevent an infinite recursive loop
// between the two lines and their reactors.
//
AcDbLine *pLine2;
if (acdbOpenObject((AcDbObject*&)pLine2, mId, AcDb::kForWrite) == Acad::eOk)
{
// Get length of line entity we’re being notified
// has just been modified.
//
AcGePoint3d p = pLine->startPoint();
AcGePoint3d q = pLine->endPoint();
AcGeVector3d v = q-p;
double len = v.length();
// update other entity to match:
//
p = pLine2->startPoint();
q = pLine2->endPoint();
v = q-p;
v = len * mFactor * v.normal();
pLine2->setEndPoint(p+v);
pLine2->close();
}
}
// Files an object’s information in.
//
Acad::ErrorStatus
AsdkObjectToNotify::dwgInFields(AcDbDwgFiler* filer)
{
assertWriteEnabled();
AcDbObject::dwgInFields(filer);
filer->readItem(&mFactor);
filer->readItem((AcDbSoftPointerId*) &mId);
return filer->filerStatus();
}
// Files an object’s information out.
//
Acad::ErrorStatus
AsdkObjectToNotify::dwgOutFields(AcDbDwgFiler* filer) const
{
assertReadEnabled();
AcDbObject::dwgOutFields(filer);
filer->writeItem(mFactor);
filer->writeItem((AcDbSoftPointerId&)mId);
return filer->filerStatus();
}
// Files an object’ s information in from DXF and AutoLISP.
//
Acad::ErrorStatus
AsdkObjectToNotify::dxfInFields(AcDbDxfFiler* filer)
{
assertWriteEnabled();
Acad::ErrorStatus es;
if ((es = AcDbObject::dxfInFields(filer)) != Acad::eOk)
{
return es;
}
// Check if we’re at the right subclass data marker.
//
if(!filer->atSubclassData("AsdkObjectToNotify")) {
return Acad::eBadDxfSequence;
}
struct resbuf rbIn;
while (es == Acad::eOk) {
if ((es = filer->readItem(&rbIn)) == Acad::eOk) {
if (rbIn.restype == AcDb::kDxfReal) {
mFactor = rbIn.resval.rreal;
} else if (rbIn.restype == AcDb::kDxfSoftPointerId)
{
// ObjectIds are filed in as ads_names.
//
acdbGetObjectId(mId, rbIn.resval.rlname);
} else { // invalid group
return(filer->pushBackItem());
}
}
}
return filer->filerStatus();
}
// Files an object’s information out to DXF and AutoLISP.
//
Acad::ErrorStatus
AsdkObjectToNotify::dxfOutFields(AcDbDxfFiler* filer) const
{
assertReadEnabled();
AcDbObject::dxfOutFields(filer);
filer->writeItem(AcDb::kDxfSubclass, "AsdkObjectToNotify");
filer->writeItem(AcDb::kDxfReal, mFactor);
filer->writeItem(AcDb::kDxfSoftPointerId, mId);
return filer->filerStatus();
}
// Creates two lines and two AsdkObjectToNotify objects and
// ties them all together.
//
void
assocLines()
{
AcDbDatabase *pDb = acdbHostApplicationServices()->workingDatabase();
AcDbObjectId aId, bId;
AcDbLine *pLineA = new AcDbLine;
pLineA->setDatabaseDefaults(pDb);
pLineA->setStartPoint(AcGePoint3d(1, 1, 0));
pLineA->setEndPoint(AcGePoint3d(2, 1, 0));
addToModelSpace(aId, pLineA);
acutPrintf( "Line A is %lx from 1,1 to 2,1.\n",
pLineA->objectId());
AcDbLine *pLineB = new AcDbLine;
pLineB->setDatabaseDefaults(pDb);
pLineB->setStartPoint(AcGePoint3d(1, 2, 0));
pLineB->setEndPoint(AcGePoint3d(2, 2, 0));
addToModelSpace(bId, pLineB);
acutPrintf("Line B is %lx from 1,2 to 2,2.\n", pLineB->objectId());
// Open the named object dictionary, and check if there is
// an entry with the key "ASDK_DICT". If not, create a
// dictionary and add it.
//
AcDbDictionary *pNamedObj;
AcDbDictionary *pNameList;
pDb->getNamedObjectsDictionary(pNamedObj, AcDb::kForWrite);
if (pNamedObj->getAt("ASDK_DICT",
(AcDbObject*&)pNameList, AcDb::kForWrite)
== Acad::eKeyNotFound)
{
pNameList = new AcDbDictionary;
AcDbObjectId DictId;
pNamedObj->setAt("ASDK_DICT", pNameList, DictId);
}
pNamedObj->close();
// Create the AsdkObjectToNotify for line A.
//
AsdkObjectToNotify *pObj = new AsdkObjectToNotify();
pObj->eLinkage(bId);
AcDbObjectId objId;
if ((pNameList->getAt("object_to_notify_A", objId)) == Acad::eKeyNotFound)
{
pNameList->setAt("object_to_notify_A", pObj, objId);
pObj->close();
} else {
delete pObj;
acutPrintf("object_to_notify_A already exists\n");
}
// Set up persistent reactor link between line A
// and AsdkObjectToNotify.
//
pLineA->addPersistentReactor(objId);
pLineA->close();
// Create the AsdkObjectToNotify for line B.
//
pObj = new AsdkObjectToNotify();
pObj->eLinkage(aId);
if ((pNameList->getAt("object_to_notify_B", objId)) == Acad::eKeyNotFound)
{
pNameList->setAt("object_to_notify_B", pObj, objId);
pObj->close();
} else {
delete pObj;
acutPrintf("object_to_notify_B already exists\n");
}
pNameList->close();
// Set up persistent reactor link between line B
// and AsdkObjectToNotify.
//
pLineB->addPersistentReactor(objId);
pLineB->close();
}
// Adds an entity to model space, but does not close
// the entity.
//
void
addToModelSpace(AcDbObjectId &objId, AcDbEntity* pEntity)
{
AcDbBlockTable *pBlockTable;
AcDbBlockTableRecord *pSpaceRecord;
acdbHostApplicationServices()->workingDatabase()
->getSymbolTable(pBlockTable, AcDb::kForRead);
pBlockTable->getAt(ACDB_MODEL_SPACE, pSpaceRecord,
AcDb::kForWrite);
pBlockTable->close();
pSpaceRecord->appendAcDbEntity(objId, pEntity);
pSpaceRecord->close();
return;
}
// This is the initialization function called from acrxEntryPoint()
// during the kInitAppMsg case. This function is used to add
// commands to the command stack.
//
void
initApp()
{
acedRegCmds->addCommand("ASDK_ALINES", "ASDK_ALINES",
"ALINES", ACRX_CMD_MODAL, assocLines);
AsdkObjectToNotify::rxInit();
acrxBuildClassHierarchy();
}
// This is the clean-up function called from acrxEntryPoint() during
// the kUnloadAppMsg case. This function removes this application’s
// command set from the command stack.
//
void
unloadApp()
{
acedRegCmds->removeGroup("ASDK_ALINES");
// Remove the AsdkObjectToNotify class from the ACRX
// runtime class hierarchy. If this is done while the
// database is still active, it should cause all objects
// of class AsdkObjectToNotify to be turned into proxies.
//
deleteAcRxClass(AsdkObjectToNotify::desc());
}
// ObjectARX entry point
//
AcRx::AppRetCode
acrxEntryPoint(AcRx::AppMsgCode msg, void* appId)
{
switch (msg) {
case AcRx::kInitAppMsg:
acrxDynamicLinker->unlockApplication(appId);
acrxDynamicLinker->registerAppMDIAware(appId);
initApp();
break;
case AcRx::kUnloadAppMsg:
unloadApp();
}
return AcRx::kRetOK;
}
Пример функции ObjectARX
Следующая функция ObjectARX отображает пример:
int showalert()
{
int dcl_id, dbstatus;
ads_hdlg dbhello;
// Load the DCL file.
//
ads_load_dialog("hello.dcl", &dcl_id);
// Initialize the dialog.
//
if (ads_new_dialog("hello", dcl_id, NULLCB, &dbhello) != RTNORM) {
acdbFail("Unable to initialize dialog box called
\"hello\"\n");
return BAD; // Exit if this does not work
}
// Associate an action. End expression with a key, in this
// case, the OK button. End the dialog when OK is pressed.
//
ads_action_tile(dbhello, "accept", accept_OK);
// Display the dialog box.
//
ads_start_dialog(dbhello, &dbstatus);
ads_unload_dialog(dcl_id); // Unload the DCL file
return GOOD;
}
static void CALLB accept_OK(ads_callback_packet *cpkt)
{
// DLGOK == User pressed OK
//
ads_done_dialog(cpkt->dialog, DLGOK);
}
После ads_start_dialog () запрос, диалоговое окно остается активным, пока пользователь не выбирает неперекрывающее расположение (обычно кнопка). Функция ads_action_tile () вызвана, и это вызывает функцию повторного вызова. Параметры к функции повторного вызова включены в отдельную структуру пакета повторного вызова. Повторный вызов вызывает ads_done_dialog ().
Комплексное диалоговое окно требует, больше вызывает к ads_action_tile (), возможно другая инициализация вызывает, и вероятно большее количество ввода, обрабатывающего между ads_start_dialog () и ads_unload_dialog () вызывает. Полная последовательность запроса, тем не менее, остается тем же самый.
Пример Гиперсвязи
Следующая функция перечисляет гиперсвязи, связанные с примитивом и позволяет новым гиперсвязям быть добавленной в их месте. (Проверка ошибок не показывается.)
void AddHyperlink()
{
ads_name en;
ads_point pt;
AcDbEntity * pEnt;
AcDbObjectId pEntId;
// Prompt user to select entity.
acedEntSel("\nSelect an Entity: ", en, pt);
// Get Object id.
acdbGetObjectId(pEntId, en);
// Open object for write.
acdbOpenObject(pEnt, pEntId, AcDb::kForWrite);
// The hyperlink collection object is created inside
// of getHyperlinkCollection
// below. It is our responsibility to delete it.
AcDbHyperlinkCollection * pcHCL = NULL;
// Get the hyperlink collection associated with the entity.
ACRX_X_CALL(pEnt, AcDbEntityHyperlinkPE)->
getHyperlinkCollection(pEnt, pcHCL, false, true);
// If a hyperlink exists already, say so.
if (pcHCL->count() != 0)
{
AcDbHyperlink * pcHO;
acutPrintf("\nThe following hyperlink info already exists
on this entity:");
// Iterate through collection and print existing hyperlinks.
int i = 0;
for (i = 0; i < pcHCL->count(); i++)
{
// Get point to current hyperlink object.
pcHO = pcHCL->item(i);
acutPrintf("\nHyperlink name: %s", pcHO->name());
acutPrintf("\nHyperlink location: %s",
pcHO->subLocation());
acutPrintf("\nHyperlink description: %s",
pcHO->description());
}
acutPrintf("\n** All will be replaced.**");
// Remove existing hyperlinks from collection.
// RemoveAt will delete objects too.
for (i = pcHCL->count() - 1; i >= 0; i--)
{
pcHCL->removeAt(i);
}
}
// Get new hyperlinks for this entity.
for (;;)
{
acutPrintf("\nEnter null name, location, and description to
terminate input requests.");
// Prompt user for name and description.
char sName[100], sLocation[100], sDescription[100];
if (acedGetString(TRUE, "\nEnter hyperlink name: ", sName) != RTNORM)
acutPrintf("Invalid input\n");
if (acedGetString(TRUE, "\nEnter hyperlink location: ", sLocation) != RTNORM)
acutPrintf("Invalid input\n");
if (acedGetString(TRUE, "\nEnter hyperlink description: ", sDescription) != RTNORM)
acutPrintf("Invalid input\n");
// Add hyperlink or exit prompting.
if (strcmp(sName, "") || strcmp(sLocation, "") || strcmp(sDescription, ""))
pcHCL->addTail(sName, sDescription, sLocation);
else
break;
}
// Add these hyperlinks to the selected entity (opened above).
ACRX_X_CALL(pEnt, AcDbEntityHyperlinkPE)->
setHyperlinkCollection(pEnt, pcHCL);
// Delete the collection. The collection will delete all its
// contained hyperlink objects.
delete pcHCL;
// Close the object.
pEnt->close();
}
Пример Использования AcGi
Следующий пример иллюстрирует, как использовать AcGi. Сначала, это сохраняет текущие значения свойства примитива на цвет, linetype, и уровень. Тогда это изменяет номер цвета черты под-примитива к синему и рисует ломаную линию с тремя точками. Затем, это изменяет номер цвета черты подпримитива к текущему цвету уровня, изменяет значение linetype к DASHDOT, и рисует xline.
Типовой код включает две функции, getLinetypeFromString () и getLayerIdFromString (), которые позволяют Вам получать ID для linetype или уровня от строки.
ОБРАТИТЕ ВНИМАНИЕ Практически, эти функции могли бы также не спешить, использование в пределах worldDraw (), и объектных ID должно быть сохранено и использоваться непосредственно.
static Acad::ErrorStatus
getLinetypeIdFromString(const char* str, AcDbObjectId& id);
static Acad::ErrorStatus
getLayerIdFromString(const char* str, AcDbObjectId& id);
Adesk::Boolean
AsdkTraitsSamp::worldDraw(AcGiWorldDraw* pW)
{
// At this point, the current property traits are
// the entity’s property traits. If the current
// property traits are changed and you want to
// reapply the entity’s property traits, this is
// the place to save them.
//
Adesk::UInt16 entity_color
= pW->subEntityTraits().color();
AcDbObjectId entity_linetype
= pW->subEntityTraits().lineTypeId();
AcDbObjectId entity_layer
= pW->subEntityTraits().layerId();
// Override the current color and make it blue.
//
pW->subEntityTraits().setColor(kBlue);
// Draw a blue 3-point polyline.
//
int num_pts = 3;
AcGePoint3d *pVerts = new AcGePoint3d[num_pts];
pVerts[0] = AcGePoint3d(0.0, 0.0, 0);
pVerts[1] = AcGePoint3d(1.0, 0.0, 0);
pVerts[2] = AcGePoint3d(1.0, 1.0, 0);
pW->geometry().polyline(num_pts, pVerts);
// Force the current color to use current layer’s color.
//
pW->subEntityTraits().setColor(kColorByLayer);
// Force current line type to DASHDOT. If
// DASHDOT is not loaded, the current linetype
// will still be in effect.
//
AcDbObjectId dashdotId;
if (getLinetypeIdFromString("DASHDOT", dashdotId) == Acad::eOk)
{
pW->subEntityTraits().setLineType(dashdotId);
}
// Force current layer to "MY_LAYER". If
// "MY_LAYER" is not loaded, the current layer
// will still be in effect.
//
AcDbObjectId layerId;
if (getLayerIdFromString("MY_LAYER", layerId) == Acad::eOk)
{
pW->subEntityTraits().setLayer(layerId);
}
// Draw a DASHDOT xline in "MY_LAYER"’s color.
//
pW->geometry().xline(pVerts[0], pVerts[2]);
delete [] pVerts;
return Adesk::kTrue;
}
// A useful function that gets the linetype ID from the
// linetype’s name; the name must be in upper case.
//
static Acad::ErrorStatus
getLinetypeIdFromString(const char* str, AcDbObjectId& id)
{
Acad::ErrorStatus err;
// Get the table of currently loaded linetypes.
//
AcDbLinetypeTable *pLinetypeTable;
err = acdbHostApplicationServices()->workingDatabase()
->getSymbolTable(pLinetypeTable, AcDb::kForRead);
if (err != Acad::eOk)
return err;
// Get the ID of the linetype with the name that
// str contains.
//
err = pLinetypeTable->getAt(str, id, Adesk::kTrue);
pLinetypeTable->close();
return err;
}
// A useful function that gets the layer ID from the
// layer’s name; the layer name must be in upper case.
//
static Acad::ErrorStatus
getLayerIdFromString(const char* str, AcDbObjectId& id)
{
Acad::ErrorStatus err;
// Get the table of currently loaded layers.
//
AcDbLayerTable *pLayerTable;
err = acdbHostApplicationServices()->workingDatabase()
->getSymbolTable(pLayerTable, AcDb::kForRead);
if (err != Acad::eOk)
return err;
// Get the ID of the layer with the name that str
// contains.
//
err = pLayerTable->getAt(str, id, Adesk::kTrue);
pLayerTable->close();
return err;
}
Пример MDI-ЗНАЮЩЕГО приложения
Следующий пример показывает простое ObjectARX-приложение, которое MDI-ЗНАЕТ.
Это - код примера от “ Использование Реактора Базы данных ” на странице 399, с кодом, добавленным, чтобы делать приложение MDI-Aware. Новый код показывается в жирном начертании.
class AsdkDbReactor;
class AsdkDocReactor: public AcApDocManagerReactor
{
public:
virtual void documentToBeActivated(AcApDocument* pDoc);
virtual void documentCreated(AcApDocument* pDoc);
virtual void documentToBeDestroyed(AcApDocument* pDoc);
};
class AsdkPerDocData
{
friend class AsdkAppDocGlobals;
public:
AsdkPerDocData(AcApDocument* pDoc);
private:
AcApDocument* m_pDoc;
AsdkPerDocData* m_pNext;
long m_EntAcc; // Entity count
AsdkDbReactor* m_pDbr;// Pointer to database reactor
};
class AsdkAppDocGlobals
{
public:
AsdkAppDocGlobals(AcApDocument* pDoc);
void setGlobals(AcApDocument* pDoc);
void removeDocGlobals(AcApDocument *pDoc);
void removeAllDocGlobals(AsdkPerDocData* pTarget);
void unload();
long &entityCount();
void incrementEntityCount();
void decrementEntityCount();
AsdkDbReactor *dbReactor();
void setDbReactor(AsdkDbReactor *pDb);
private:
AsdkPerDocData *m_pHead;
AsdkPerDocData *m_pData;
AsdkDocReactor *m_pDocReactor;
};
AsdkAppDocGlobals *gpAsdkAppDocGlobals;
// Custom AcDbDatabaseReactor class for Database
// event notification.
//
class AsdkDbReactor : public AcDbDatabaseReactor
{
public:
virtual void objectAppended(const AcDbDatabase* dwg,
const AcDbObject* dbObj);
virtual void objectModified(const AcDbDatabase* dwg,
const AcDbObject* dbObj);
virtual void objectErased(const AcDbDatabase* dwg,
const AcDbObject* dbObj, Adesk::Boolean pErased);
};
// This is called whenever an object is added to the database.
//
void
AsdkDbReactor::objectAppended(const AcDbDatabase* db,
const AcDbObject* pObj)
{
printDbEvent(pObj, "objectAppended");
acutPrintf(" Db==%lx\n", (long) db);
gpAsdkAppDocGlobals->incrementEntityCount();
acutPrintf("Entity Count = %d\n",
gpAsdkAppDocGlobals->entityCount());
}
// This is called whenever an object in the database is modified.
//
void
AsdkDbReactor::objectModified(const AcDbDatabase* db, const AcDbObject* pObj)
{
printDbEvent(pObj, "objectModified");
acutPrintf(" Db==%lx\n", (long) db);
}
// This is called whenever an object is erased from the database.
//
void
AsdkDbReactor::objectErased(const AcDbDatabase* db, const AcDbObject* pObj, Adesk::Boolean pErased)
{
if (pErased)
{
printDbEvent(pObj, "objectErased");
gpAsdkAppDocGlobals->decrementEntityCount();
}
else
{
printDbEvent(pObj, "object(Un)erased");
gpAsdkAppDocGlobals->incrementEntityCount();
}
acutPrintf(" Db==%lx\n", (long) db);
acutPrintf("Entity Count = %d\n",
gpAsdkAppDocGlobals->entityCount());
}
// Prints the message passed in by pEvent; then
// proceeds to call printObj() to print the information about
// the object that triggered the notification.
//
void
printDbEvent(const AcDbObject* pObj, const char* pEvent)
{
acutPrintf(" Event: AcDbDatabaseReactor::%s ", pEvent);
printObj(pObj);
}
// Prints out the basic information about the object pointed
// to by pObj.
//
void
printObj(const AcDbObject* pObj)
{
if (pObj == NULL)
{
acutPrintf("(NULL)");
return;
}
AcDbHandle objHand;
char handbuf[17];
// Get the handle as a string.
//
pObj->getAcDbHandle(objHand);
objHand.getIntoAsciiBuffer(handbuf);
acutPrintf(
"\n (class==%s, handle==%s, id==%lx, db==%lx)",
pObj->isA()->name(), handbuf,
pObj->objectId().asOldId(), pObj->database());
}
// Document swapping functions
//
void
AsdkDocReactor::documentToBeActivated(AcApDocument *pDoc)
{
gpAsdkAppDocGlobals->setGlobals(pDoc);
}
void
AsdkDocReactor::documentCreated(AcApDocument *pDoc)
{
gpAsdkAppDocGlobals->setGlobals(pDoc);
}
void
AsdkDocReactor::documentToBeDestroyed(AcApDocument *pDoc)
{
gpAsdkAppDocGlobals->removeDocGlobals(pDoc);
}
AsdkPerDocData::AsdkPerDocData(AcApDocument *pDoc)
{
m_pDoc = pDoc;
m_pNext = NULL;
m_EntAcc = 0;
m_pDbr = NULL;
}
AsdkAppDocGlobals::AsdkAppDocGlobals(AcApDocument *pDoc)
{
m_pData = m_pHead = NULL;
m_pDocReactor = new AsdkDocReactor();
acDocManager->addReactor(m_pDocReactor);
}
// Iterate through the list until the documents’s global data is
// found. If it is not found, create a new set of document globals.
//
void
AsdkAppDocGlobals::setGlobals(AcApDocument *pDoc)
{
AsdkPerDocData *p_data = m_pHead, *prev_data = m_pHead;
while (p_data != NULL)
{
if (p_data->m_pDoc == pDoc)
{
m_pData = p_data;
break;
}
prev_data = p_data;
p_data = p_data->m_pNext;
}
if (p_data == NULL)
{
if (m_pHead == NULL)
m_pHead = m_pData = new AsdkPerDocData(pDoc);
else
prev_data->m_pNext = m_pData = new AsdkPerDocData(pDoc);
}
}
// Delete the globals associated with pDoc.
//
void
AsdkAppDocGlobals::removeDocGlobals(AcApDocument *pDoc)
{
AsdkPerDocData *p_data = m_pHead, *prev_data = m_pHead;
while (p_data != NULL)
{
if (p_data->m_pDoc == pDoc)
{
if (p_data == m_pHead)
m_pHead = p_data->m_pNext;
else
prev_data->m_pNext = p_data->m_pNext;
if (m_pData == p_data)
m_pData = m_pHead;
delete p_data;
break;
}
prev_data = p_data;
p_data = p_data->m_pNext;
}
}
// Delete all the doc globals in the list (recursively).
//
void
AsdkAppDocGlobals::removeAllDocGlobals(AsdkPerDocData *p_target)
{
if (p_target == NULL)
return;
if (p_target->m_pNext != NULL)
removeAllDocGlobals(p_target->m_pNext);
if (p_target->m_pDbr != NULL)
{
acdbHostApplicationServices()->
workingDatabase()->removeReactor(p_target->m_pDbr);
delete p_target->m_pDbr;
p_target->m_pDbr = NULL;
}
delete p_target;
}
// Application was unloaded - delete everything associated with this
// document.
//
void AsdkAppDocGlobals::unload()
{
acDocManager->removeReactor(m_pDocReactor);
delete m_pDocReactor;
removeAllDocGlobals(m_pHead);
m_pHead = m_pData = NULL;
}
long &
AsdkAppDocGlobals::entityCount()
{
return m_pData->m_EntAcc;
}
void
AsdkAppDocGlobals::incrementEntityCount()
{
m_pData->m_EntAcc++;
}
void
AsdkAppDocGlobals::decrementEntityCount()
{
m_pData->m_EntAcc--;
}
AsdkDbReactor *
AsdkAppDocGlobals::dbReactor()
{
return m_pData->m_pDbr;
}
void
AsdkAppDocGlobals::setDbReactor(AsdkDbReactor *pDb)
{
m_pData->m_pDbr = pDb;
}
// Adds a reactor to the database to monitor changes.
// This can be called multiple times without any ill
// effects because subsequent calls will be ignored.
//
void
watchDb()
{
AsdkDbReactor *pDbr;
if (gpAsdkAppDocGlobals->dbReactor() == NULL)
{
pDbr = new AsdkDbReactor();
gpAsdkAppDocGlobals->setDbReactor(pDbr);
acdbHostApplicationServices()->workingDatabase()->addReactor(pDbr);
acutPrintf(
" Added Database Reactor to "
"acdbHostApplicationServices()->workingDatabase().\n");
}
}
// Removes the database reactor.
//
void
clearReactors()
{
AsdkDbReactor *pDbr;
if ((pDbr = gpAsdkAppDocGlobals->dbReactor()) != NULL)
{
acdbHostApplicationServices()->workingDatabase()->removeReactor(pDbr);
delete pDbr;
gpAsdkAppDocGlobals->setDbReactor(NULL);
}
}
// ObjectARX entry point function
//
AcRx::AppRetCode
acrxEntryPoint(AcRx::AppMsgCode msg, void* appId)
{
switch (msg) {
case AcRx::kInitAppMsg:
acrxUnlockApplication(appId);
acrxRegisterAppMDIAware(appId);
gpAsdkAppDocGlobals = new AsdkAppDocGlobals(curDoc());
gpAsdkAppDocGlobals->setGlobals(curDoc());
acedRegCmds->addCommand("ASDK_NOTIFY_TEST",
"ASDK_WATCH",
"WATCH",
ACRX_CMD_TRANSPARENT,
watchDb);
acedRegCmds->addCommand("ASDK_NOTIFY_TEST",
"ASDK_CLEAR",
"CLEAR",
ACRX_CMD_TRANSPARENT,
clearReactors);
break;
case AcRx::kUnloadAppMsg:
if (gpAsdkAppDocGlobals != NULL)
{
gpAsdkAppDocGlobals->unload();
delete gpAsdkAppDocGlobals;
gpAsdkAppDocGlobals = NULL;
}
acedRegCmds->removeGroup("ASDK_NOTIFY_TEST");
break;
}
return AcRx::kRetOK;
}
Пример Операций Базы данных
Следующий пример показывает createDwg () подпрограмма, которая создает новую базу данных, получает образцовый пространственный блочный отчет{*запись*} таблицы, и создает два круга, которые добавлены, чтобы моделировать пространство{*пробел*}. Это использует AcDbDatabase:: saveAs () функция, чтобы сохранить{*экономить*} рисунок. Вторая подпрограмма, readDwg (), читает в сохраненном рисунке, открывает образцовый пространственный блочный отчет{*запись*} таблицы, и выполняет итерации через это, печать имена класса объектов, которые это содержит.
void
createDwg()
{
AcDbDatabase *pDb = new AcDbDatabase();
AcDbBlockTable *pBtbl;
pDb->getSymbolTable(pBtbl, AcDb::kForRead);
AcDbBlockTableRecord *pBtblRcd;
pBtbl->getAt(ACDB_MODEL_SPACE, pBtblRcd,
AcDb::kForWrite);
pBtbl->close();
AcDbCircle *pCir1 = new AcDbCircle(AcGePoint3d(1,1,1),
AcGeVector3d(0,0,1),
1.0),
*pCir2 = new AcDbCircle(AcGePoint3d(4,4,4),
AcGeVector3d(0,0,1),
2.0);
pBtblRcd->appendAcDbEntity(pCir1);
pCir1->close();
pBtblRcd->appendAcDbEntity(pCir2);
pCir2->close();
pBtblRcd->close();
// AcDbDatabase::saveAs() does not automatically
// append a DWG file extension, so it
// must be specified.
//
pDb->saveAs("test1.dwg");
delete pDb;
}
void
readDwg()
{
// Set constructor parameter to kFalse so that the
// database will be constructed empty. This way only
// what is read in will be in the database.
//
AcDbDatabase *pDb = new AcDbDatabase(Adesk::kFalse);
// The AcDbDatabase::readDwgFile() function
// automatically appends a DWG extension if it is not
// specified in the filename parameter.
//
pDb->readDwgFile("test1.dwg");
// Open the model space block table record.
//
AcDbBlockTable *pBlkTbl;
pDb->getSymbolTable(pBlkTbl, AcDb::kForRead);
AcDbBlockTableRecord *pBlkTblRcd;
pBlkTbl->getAt(ACDB_MODEL_SPACE, pBlkTblRcd,
AcDb::kForRead);
pBlkTbl->close();
AcDbBlockTableRecordIterator *pBlkTblRcdItr;
pBlkTblRcd->newIterator(pBlkTblRcdItr);
AcDbEntity *pEnt;
for (pBlkTblRcdItr->start(); !pBlkTblRcdItr->done(); pBlkTblRcdItr->step())
{
pBlkTblRcdItr->getEntity(pEnt,
AcDb::kForRead);
acutPrintf("classname: %s\n",
(pEnt->isA())->name());
pEnt->close();
}
pBlkTblRcd->close();
delete pBlkTblRcdItr;
delete pDb;
}
Пример отсечения границ
В следующем примере, граница обрезки помещена на стек границы обрезки прежде, чем что - нибудь рисует и вытолкнуто от снова, как только рисунок в этот объект полон:
Adesk::Boolean
MyObject::worldDraw(AcGiWorldDraw* pDraw)
{
AcGiWorldGeometry * pGeom = &pDraw->geometry();
pGeom->pushModelTransform(myTransform());
AcGiClipBoundary cb;
cb.m_bDrawBoundary= true;
cb.m_vNormal = AcGeVector3d::kZAxis;
cb.m_ptPoint = AcGePoint3d::kOrigin;
// Two points treated as a rectangle, three creates a triangle
cb.m_aptPoints.append(AcGePoint2d(0,0));
cb.m_aptPoints.append(AcGePoint2d(5,5));
// We are clipping in our own space
cb.m_xToClipSpace.setToIdentity();
cb.m_xInverseBlockRefXForm = myTransform().inverse();
// No Z clipping
cb.m_bClippingBack = cb.m_bClippingFront = false;
cb.m_dFrontClipZ = cb.m_dBackClipZ = 0.;
Adesk::Boolean bPopClipBoundary = pGeom->pushClipBoundary(&cb);
// Draw something
pGeom->circle(...);
pGeom->popModelTransform();
if(bPopClipBoundary){ pGeom->popClipBoundary(); }
return true; // world-only
}
Так как это отсечение - комплексная операция, некоторые AcGi
выполнение не могли бы поддерживать это полностью. В этом случае, AcGi выполнение может возвращать ложь от pushClipBoundary (), и Вы не должны вызвать popClipBoundary ().
Пример приложения, используя AcBr библиотеку
Типовое ObjectARX-приложение, использующее библиотеку контурных представлений включено в ObjectARX SDK
в objectarx\utils\brep\samples\brepsamp каталоге. Этот каталог включает Readme файл, который описывает типовое приложение и как использовать это.
Типовое приложение основано на интерактивном подходе, который не может быть лучший подход для всех приложений. Объекты выбраны, используя наборы выбора, resbufs, и GS маркеры, и переданы AcBr библиотеке, используя AcDbFullSubentPaths.
Пример расширения протокола
Этот пример расширения протокола разделен на три части:
§
Объявление и определение четырех классов расширения протокола: AsdkEntTemperature, AsdkDefaultTemperature, AsdkRegionTemperature и AsdkCircleTemperature.
§ выполнение energy() для команды ENERGY, которая позволяет пользователю выбирать примитив и затем, вычисляет температуру для того примитива.
§ ObjectARX модуль связывают с помощью интерфейса функции: initApp (), unloadApp () и acrxEntryPoint ().
// This is the AsdkEntTemperature protocol extension abstract base
// class. Notice that this is the lowest level that uses
// the ACRX macros.
//
class AsdkEntTemperature : public AcRxObject
{
public:
ACRX_DECLARE_MEMBERS(AsdkEntTemperature);
virtual double reflectedEnergy(AcDbEntity*) const = 0;
};
ACRX_NO_CONS_DEFINE_MEMBERS(AsdkEntTemperature, AcRxObject);
// This is the default implementation to be attached to AcDbEntity
// as a catch-all. This guarantees that this protocol extension will
// be found for any entity, so the search up the AcRxClass tree will
// not fail and abort AutoCAD.
//
class AsdkDefaultTemperature : public AsdkEntTemperature
{
public:
virtual double reflectedEnergy(AcDbEntity* pEnt) const;
};
double
AsdkDefaultTemperature::reflectedEnergy( AcDbEntity* pEnt) const
{
acutPrintf( "\nThis entity has no area, and no reflection.\n");
return -1.0;
}
// AsdkEntTemperature implementation for Regions
//
class AsdkRegionTemperature : public AsdkEntTemperature
{
public:
virtual double reflectedEnergy(AcDbEntity* pEnt) const;
};
double
AsdkRegionTemperature::reflectedEnergy( AcDbEntity* pEnt) const
{
AcDbRegion *pRegion = AcDbRegion::cast(pEnt);
if (pRegion == NULL)
acutPrintf("\nThe impossible has happened!");
// Compute the reflected energy as the region area multiplied
// by a dummy constant.
//
double retVal;
if (pRegion->getArea(retVal) != Acad::eOk)
return -1.0;
return retVal * 42.0;
}
// AsdkEntTemperature implementation for circles
//
class AsdkCircleTemperature : public AsdkEntTemperature
{
public:
virtual double reflectedEnergy(AcDbEntity* pEnt) const;
};
double
AsdkCircleTemperature::reflectedEnergy( AcDbEntity* pEnt) const
{
AcDbCircle *pCircle = AcDbCircle::cast(pEnt);
// Compute the reflected energy in a manner distinctly
// different than for AcDbRegion.
//
return pCircle->radius() * 6.21 * 42.0;
}
// This function has the user select an entity and then
// calls the reflectedEnergy() function in the protocol
// extension class attached to that entity’s class.
//
void
energy()
{
AcDbEntity *pEnt;
AcDbObjectId pEntId;
ads_name en;
ads_point pt;
if (acedEntSel("\nSelect an Entity: ", en, pt) != RTNORM)
{
acutPrintf("Nothing Selected\n");
return;
}
acdbGetObjectId(pEntId, en);
acdbOpenObject(pEnt, pEntId, AcDb::kForRead);
// call the protocol extension class’s method
//
double eTemp = ACRX_X_CALL(pEnt, AsdkEntTemperature)->reflectedEnergy(pEnt);
acutPrintf("\nEnergy == %f\n", eTemp);
pEnt->close();
}
// Pointers for protocol extension objects. These pointers
// are global so that they can be accessed during
// initialization and cleanup.
//
AsdkDefaultTemperature *pDefaultTemp;
AsdkRegionTemperature *pRegionTemp;
AsdkCircleTemperature *pCircleTemp;
// Initialization function called from acrxEntryPoint() during
// kInitAppMsg case. This function is used to add commands
// to the command stack and to add protocol extension
// objects to classes.
//
void
initApp()
{
acrxRegisterService("AsdkTemperature");
AsdkEntTemperature::rxInit();
acrxBuildClassHierarchy();
pDefaultTemp = new AsdkDefaultTemperature();
pRegionTemp = new AsdkRegionTemperature();
pCircleTemp = new AsdkCircleTemperature();
// Add the protocol extension objects to the appropriate
// AcRxClass objects.
//
AcDbEntity::desc()->addX(AsdkEntTemperature::desc(), pDefaultTemp);
AcDbRegion::desc()->addX(AsdkEntTemperature::desc(), pRegionTemp);
AcDbCircle::desc()->addX(AsdkEntTemperature::desc(), pCircleTemp);
acedRegCmds->addCommand("ASDK_TEMPERATURE_APP",
"ASDK_ENERGY", "ENERGY", ACRX_CMD_TRANSPARENT,
energy);
}
void
unloadApp()
{
delete acrxServiceDictionary->remove("AsdkTemperature");
acedRegCmds->removeGroup("ASDK_TEMPERATURE_APP");
// Remove protocol extension objects from the AcRxClass
// object tree. This must be done before removing the
// AsdkEntTemperature class from the ACRX runtime class
// hierarchy, so the AsdkEntTemperature::desc()
// still exists.
//
AcDbEntity::desc()->delX(AsdkEntTemperature::desc());
delete pDefaultTemp;
AcDbRegion::desc()->delX(AsdkEntTemperature::desc());
delete pRegionTemp;
AcDbCircle::desc()->delX(AsdkEntTemperature::desc());
delete pCircleTemp;
// Remove the AsdkEntTemperature class from the ARX
// runtime class hierarchy.
//
deleteAcRxClass(AsdkEntTemperature::desc());
}
AcRx::AppRetCode
acrxEntryPoint(AcRx::AppMsgCode msg, void* appId)
{
switch (msg) {
case AcRx::kInitAppMsg:
acrxDynamicLinker->unlockApplication(appId);
acrxDynamicLinker->registerAppMDIAware(appId);
initApp();
break;
case AcRx::kUnloadAppMsg:
unloadApp();
}
return AcRx::kRetOK;
}
Пример вложенных транзакций
Следующий пример включает три вложенных транзакции. Последовательность событий следует.
Создавать вложенные транзакции
1 Создают многоугольник и переносят это к базе данных.
2 Транзакция Начала 1:
Выбирают многоугольник и получают указатель на это. Откройте это для чтения.
Создают вытесненное твердое использование многоугольник.
Создают цилиндр в середине расширенного{*продленного*} многоугольника.
3 Транзакция Начала 2: Вычтите цилиндр от вытеснения (создает отверстие в середине твердых).
4 Транзакция Начала 3:
Сектор форма в половине по X/Z плану и перемещению это по X оси, так что Вы можете рассматривать эти две части.
Прерывают транзакцию? Ответ да.
5 Транзакции Начала 3 (снова): Сектор форма в половине по Y/Z плану и перемещению это по Y.
6 Конечных Транзакции 3.
7 Конечных Транзакции 2.
ОБРАТИТЕ ВНИМАНИЕ, прерываетесь ли Вы в этой точке, транзакции 2 и 3 оба отменены. Если Вы прерываете содержащую транзакцию, все вложенные транзакции прерваны, даже если они были успешно закончены.
8 Конечных Транзакции 1.
Следующее - код для этого примера:
void
transactCommand()
{
Adesk::Boolean interrupted;
Acad::ErrorStatus es = Acad::eOk;
AcDbObjectId savedCylinderId,savedExtrusionId;
// Create a poly and post it to the database.
//
acutPrintf("\nCreating a poly...Please supply the"
" required input.");
if ((es = createAndPostPoly()) != Acad::eOk)
return;
// Start a transaction.
//
AcTransaction *pTrans = actrTransactionManager->startTransaction();
assert(pTrans != NULL);
acutPrintf("\n\n###### Started transaction one." " ######\n");
// Select the poly and extrude it.
//
AcDbObject *pObj = NULL;
AsdkPoly *pPoly = NULL;
AcDb3dSolid *pSolid = NULL;
AcDbObjectId objId;
ads_name ename;
ads_point pickpt;
for (;;) {
switch (acedEntSel("\nSelect a polygon: ", ename, pickpt))
{
case RTNORM:
acdbGetObjectId(objId, ename);
if ((es = pTrans->getObject(pObj, objId, AcDb::kForRead)) != Acad::eOk)
{
acutPrintf("\nFailed to obtain an object"
" through transaction.");
actrTransactionManager->abortTransaction();
return;
}
assert(pObj != NULL);
pPoly = AsdkPoly::cast(pObj);
if (pPoly == NULL) {
acutPrintf("\nNot a polygon. Try again");
continue;
}
break;
case RTNONE:
case RTCAN:
actrTransactionManager->abortTransaction();
return;
default:
continue;
}
break;
}
// Now that a poly is created, convert it to a region
// and extrude it.
//
acutPrintf("\nWe will be extruding the poly.");
AcGePoint2d c2d = pPoly->center();
ads_point pt;
pt[0] = c2d[0]; pt[1] = c2d[1]; pt[2] = pPoly->elevation();
acdbEcs2Ucs(pt,pt,asDblArray(pPoly->normal()),Adesk::kFalse);
double height;
if (acedGetDist(pt, "\nEnter Extrusion height: ", &height) != RTNORM)
{
actrTransactionManager->abortTransaction();
return;
}
if ((es = extrudePoly(pPoly, height,savedExtrusionId)) != Acad::eOk) {
actrTransactionManager->abortTransaction();
return;
}
// Create a cylinder at the center of the polygon of
// the same height as the extruded poly.
//
double radius = (pPoly->startPoint() - pPoly->center()).length() * 0.5;
pSolid = new AcDb3dSolid;
assert(pSolid != NULL);
pSolid->createFrustum(height, radius, radius, radius);
AcGeMatrix3d mat(AcGeMatrix3d::translation(
pPoly->elevation()*pPoly->normal())*
AcGeMatrix3d::planeToWorld(pPoly->normal()));
pSolid->transformBy(mat);
// Move it up again by half the height along
// the normal.
//
AcGeVector3d x(1, 0, 0), y(0, 1, 0), z(0, 0, 1);
AcGePoint3d moveBy(pPoly->normal()[0] * height * 0.5,
pPoly->normal()[1] * height * 0.5,
pPoly->normal()[2] * height * 0.5);
mat.setCoordSystem(moveBy, x, y, z);
pSolid->transformBy(mat);
addToDb(pSolid, savedCylinderId);
actrTransactionManager->addNewlyCreatedDBRObject(pSolid);
pSolid->draw();
acutPrintf("\nCreated a cylinder at the center of"
" the poly.");
// Start another transaction. Ask the user to select
// the extruded solid followed by selecting the
// cylinder. Make a hole in the extruded solid by
// subtracting the cylinder from it.
//
pTrans = actrTransactionManager->startTransaction();
assert(pTrans != NULL);
acutPrintf("\n\n###### Started transaction two." " ######\n");
AcDb3dSolid *pExtrusion, *pCylinder;
if ((es = getASolid("\nSelect the extrusion: ", pTrans,
AcDb::kForWrite, savedExtrusionId, pExtrusion))
!= Acad::eOk)
{
actrTransactionManager->abortTransaction();
actrTransactionManager->abortTransaction();
return;
}
assert(pExtrusion != NULL);
if ((es = getASolid("\nSelect the cylinder: ", pTrans,
AcDb::kForWrite, savedCylinderId, pCylinder))
!= Acad::eOk)
{
actrTransactionManager->abortTransaction();
actrTransactionManager->abortTransaction();
return;
}
assert(pCylinder != NULL);
pExtrusion->booleanOper(AcDb::kBoolSubtract, pCylinder);
pExtrusion->draw();
acutPrintf("\nSubtracted the cylinder from the"
" extrusion.");
// At this point, the cylinder is a NULL solid. Erase it.
//
assert(pCylinder->isNull());
pCylinder->erase();
// Start another transaction and slice the resulting
// solid into two halves.
//
pTrans = actrTransactionManager->startTransaction();
assert(pTrans != NULL);
acutPrintf("\n\n##### Started transaction three."
" ######\n");
AcGeVector3d vec, normal;
AcGePoint3d sp,center;
pPoly->getStartPoint(sp);
pPoly->getCenter(center);
vec = sp - center;
normal = pPoly->normal().crossProduct(vec);
normal.normalize();
AcGePlane sectionPlane(center, normal);
AcDb3dSolid *pOtherHalf = NULL;
pExtrusion->getSlice(sectionPlane, Adesk::kTrue,
pOtherHalf);
assert(pOtherHalf != NULL);
// Move the other half three times the vector length
// along the vector.
//
moveBy.set(vec[0] * 3.0, vec[1] * 3.0, vec[2] * 3.0);
mat.setCoordSystem(moveBy, x, y, z);
pOtherHalf->transformBy(mat);
AcDbObjectId otherHalfId;
addToDb(pOtherHalf, otherHalfId);
actrTransactionManager->addNewlyCreatedDBRObject(pOtherHalf);
pOtherHalf->draw();
pExtrusion->draw();
acutPrintf("\ nSliced the resulting solid into half"
" and moved one piece.");
// Now abort transaction three, to return to the hole in
// the extrusion.
//
Adesk::Boolean yes = Adesk::kTrue;
if (getYOrN("\nLet’s abort transaction three, yes?"
" [Y] : ", Adesk::kTrue, yes,interrupted) == Acad::eOk
&& yes == Adesk::kTrue)
{
acutPrintf("\n\n$$$$$$ Aborting transaction"
" three. $$$$$$\n");
actrTransactionManager->abortTransaction();
acutPrintf("\nBack to the un-sliced solid.");
pExtrusion->draw();
char option[256];
acedGetKword("\nHit any key to continue.", option);
} else {
acutPrintf("\n\n>>>>>> Ending transaction three."
" <<<<<<\n");
actrTransactionManager->endTransaction();
}
// Start another transaction (three again). This time,
// slice the solid along a plane that is perpendicular
// to the plane we used last time: that is the slice
// we really wanted.
//
pTrans = actrTransactionManager->startTransaction();
assert(pTrans != NULL);
acutPrintf("\n\n##### Started transaction three."
" ######\n");
moveBy.set(normal[0] * 3.0, normal[1] * 3.0,
normal[2] * 3.0);
normal = vec;
normal.normalize();
sectionPlane.set(center, normal);
pOtherHalf = NULL;
pExtrusion->getSlice(sectionPlane, Adesk::kTrue,
pOtherHalf);
assert(pOtherHalf != NULL);
mat.setCoordSystem(moveBy, x, y, z);
pOtherHalf->transformBy(mat);
addToDb(pOtherHalf, otherHalfId);
actrTransactionManager
->addNewlyCreatedDBRObject(pOtherHalf);
pOtherHalf->draw();
pExtrusion->draw();
acutPrintf("\nSliced the resulting solid into half"
" along a plane");
acutPrintf("\ nperpendicular to the old one and moved"
" one piece.");
// Now, give the user the option to end all the transactions.
//
yes = Adesk::kFalse;
if (getYOrN("\nAbort transaction three? <No> : ",
Adesk::kFalse, yes,interrupted) == Acad::eOk
&& yes == Adesk::kTrue)
{
acutPrintf("\n\n$$$$$$ Aborting transaction"
" three. $$$$$$\n");
actrTransactionManager->abortTransaction();
acutPrintf("\nBack to the un-sliced solid.");
} else {
acutPrintf("\n\n>>>>>> Ending transaction three."
" <<<<<<\n");
actrTransactionManager->endTransaction();
}
yes = Adesk::kFalse;
if (getYOrN("\nAbort transaction two? <No> : ",
Adesk::kFalse, yes,interrupted) == Acad::eOk
&& yes == Adesk::kTrue)
{
acutPrintf("\n\n$$$$$$ Aborting transaction two."
" $$$$$$\n");
actrTransactionManager->abortTransaction();
acutPrintf("\nBack to separate extrusion and"
" cylinder.");
} else {
acutPrintf("\n\n>>>>>> Ending transaction two."
" <<<<<<\n");
actrTransactionManager->endTransaction();
}
yes = Adesk::kFalse;
if (getYOrN("\nAbort transaction one? <No> : ",
Adesk::kFalse, yes,interrupted) == Acad::eOk
&& yes == Adesk::kTrue)
{
acutPrintf("\n\n$$$$$$ Aborting transaction one."
" $$$$$$\n");
actrTransactionManager->abortTransaction();
acutPrintf("\nBack to just the Poly.");
} else {
actrTransactionManager->endTransaction();
acutPrintf("\n\n>>>>>> Ending transaction one."
" <<<<<<\n");
}
}
static Acad::ErrorStatus
createAndPostPoly()
{
int nSides = 0;
while (nSides < 3) {
acedInitGet(INP_NNEG, "");
switch (acedGetInt("\nEnter number of sides: ", &nSides))
{
case RTNORM:
if (nSides < 3)
acutPrintf("\nNeed at least 3 sides.");
break;
default:
return Acad::eInvalidInput;
}
}
ads_point center, startPt, normal;
if (acedGetPoint(NULL, "\nLocate center of polygon: ", center) != RTNORM)
{
return Acad::eInvalidInput;
}
startPt[0] = center[0];
startPt[1] = center[1];
startPt[2] = center[2];
while (asPnt3d(startPt) == asPnt3d(center)) {
switch (acedGetPoint(center, "\ nLocate start point of polygon: ", startPt)) {
case RTNORM:
if (asPnt3d(center) == asPnt3d(startPt))
acutPrintf("\nPick a point different"
" from the center.");
break;
default:
return Acad::eInvalidInput;
}
}
// Set the normal to the plane of the polygon to be
// the same as the Z direction of the current UCS,
// (0, 0, 1) since we also got the center and
// start point in the current UCS. (acedGetPoint()
// returns in the current UCS.)
normal[X] = 0.0;
normal[Y] = 0.0;
normal[Z] = 1.0;
acdbUcs2Wcs(normal, normal, Adesk::kTrue);
acdbUcs2Ecs(center, center, normal, Adesk::kFalse);
acdbUcs2Ecs(startPt, startPt, normal, Adesk::kFalse);
double elev = center[2];
AcGePoint2d cen = asPnt2d(center),
start = asPnt2d(startPt);
AcGeVector3d norm = asVec3d(normal);
AsdkPoly *pPoly = new AsdkPoly;
if (pPoly==NULL)
return Acad::eOutOfMemory;
Acad::ErrorStatus es;
if ((es=pPoly->set(cen, start, nSides, norm, "transactPoly",elev))!=Acad::eOk)
return es;
pPoly->setDatabaseDefaults( acdbHostApplicationServices()->workingDatabase());
postToDb(pPoly);
return Acad::eOk;
}
// Extrudes the poly to a given height.
//
static Acad::ErrorStatus
extrudePoly(AsdkPoly* pPoly, double height, AcDbObjectId& savedExtrusionId)
{
Acad::ErrorStatus es = Acad::eOk;
// Explode to a set of lines.
//
AcDbVoidPtrArray lines;
pPoly->explode(lines);
// Create a region from the set of lines.
//
AcDbVoidPtrArray regions;
AcDbRegion::createFromCurves(lines, regions);
assert(regions.length() == 1);
AcDbRegion *pRegion
= AcDbRegion::cast((AcRxObject*)regions[0]);
assert(pRegion != NULL);
// Extrude the region to create a solid.
//
AcDb3dSolid *pSolid = new AcDb3dSolid;
assert(pSolid != NULL);
pSolid->extrude(pRegion, height, 0.0);
for (int i = 0; i < lines.length(); i++) {
delete (AcRxObject*)lines[i];
}
for (i = 0; i < regions.length(); i++) {
delete (AcRxObject*)regions[i];
}
// Now we have a solid. Add it to database, then
// associate the solid with a transaction. After
// this, transaction management is in charge of
// maintaining it.
//
pSolid->setPropertiesFrom(pPoly);
addToDb(pSolid, savedExtrusionId);
actrTransactionManager->addNewlyCreatedDBRObject(pSolid);
pSolid->draw();
return Acad::eOk;
}
static Acad::ErrorStatus
getASolid(char* prompt,
AcTransaction* pTransaction,
AcDb::OpenMode mode,
AcDbObjectId checkWithThisId,
AcDb3dSolid*& pSolid)
{
AcDbObject *pObj = NULL;
AcDbObjectId objId;
ads_name ename;
ads_point pickpt;
for (;;) {
switch (acedEntSel(prompt, ename, pickpt)) {
case RTNORM:
AOK(acdbGetObjectId(objId, ename));
if (objId != checkWithThisId) {
acutPrintf("\n Select the proper"
" solid.");
continue;
}
AOK(pTransaction->getObject(pObj, objId, mode));
assert(pObj != NULL);
pSolid = AcDb3dSolid::cast(pObj);
if (pSolid == NULL) {
acutPrintf("\nNot a solid. Try again");
AOK(pObj->close());
continue;
}
break;
case RTNONE:
case RTCAN:
return Acad::eInvalidInput;
default:
continue;
}
break;
}
return Acad::eOk;
}
Пример высвечивание
Пример кода позже в этом разделе показывает, как высветить подпримитив.
Следующая процедура перечисляет основные шаги.
1 Получают GS маркер для выбранного примитива от набора выборов.
2 Передают GS маркер к классу примитива, который будет преобразован{*конвертирован*} к пути подпримитива, используя getSubentPathsAtGsMarker () функция. Определите тип подпримитива, вы заинтересованы (вершина, край, лицо).
3, как только Вы имеете путь к выбранному подпримитиву, вы готовы назвать подсветку () функцией, проходящей в правильном пути подпримитива.
Выбор Примитива
Для выбора, вы будете использовать комбинацию глобальных функций. Сначала, используйте acedSSGet () функция, чтобы получить набор выборов. Тогда, используйте acedSSNameX () функция, чтобы получить подпримитив GS маркер для выбранного примитива.
int acedSSGet(
const char *str,
const void *pt1,
const ads_point pt2,
const struct resbuf *entmask,
ads_name ss);
int acedSSNameX(
struct resbuf** rbpp,
const ads_name ss,
const longvi);
Преобразование GS Маркеры к Путям Подпримитива
Используйте getSubentPathsAtGsMarker () функция, чтобы получить подпримитив для GS маркера, возвращенного acedSSNameX () функция. Законченный синтаксис для этой функции
virtual Acad::ErrorStatus
AcDbEntity::getSubentPathsAtGsMarker(
AcDb::SubentType type,
int gsMark,
const AcGePoint3d& pickPoint,
const AcGeMatrix3d& viewXform,
int& numPaths,
AcDbFullSubentPath*& subentPaths
int numInserts = 0,
AcDbObjectId* entAndInsertStack = NULL) const;
Первый параметр к этой функции - тип подпримитива, вы заинтересованы (вершина, край, или лицо). В примере закодируют в “ Высвечивание{*увеличение яркости*} - tity, ” первый запрос к этой функции определяет kEdgeSubentType, потому что вы собираетесь высвечивать соответствующий край. Второй запрос к getSubentPathsAtGsMarker () функция определяет kFaceSubentType, потому что вы собираетесь высвечивать каждое лицо, связанное с выбранным подпримитивом.
PickPoint и viewXform параметры используются как дополнительный ввод для некоторых примитивов (типа mlines) когда GS маркер один не обеспечивает достаточно информации, чтобы возвратить пути подпримитива. В примере закодируют в “ Высвечивание{*увеличение яркости*} Подпримитива, ” они не используются.
NumInserts и entAndInsertStack параметры используются для вложенных вставок. И acedNEntSel () и acedNEntSelP () функции возвращают название{*имя*} примитива уровня листа, плюс стек вставок.
Высвечивание{*увеличение яркости*} Подпримитива
Как только вы получили путь подпримитива к выбранному примитиву, самая твердая{*самая трудная*} часть этого процесса закончена. Теперь, Вы нуждаетесь только в запросе подсветка () функция и проход в пути подпримитива. Если Вы вызываете{*называете*} подсветку () функция без любых параметров, значение по умолчанию должна высветить целый примитив.
Следующий типовой код иллюстрирует шаги, описанные для выбора примитива, получение пути подпримитива, и высвечивания{*увеличения яркости*} различных типов подпримитивов, связанных с GS
маркером. Этот код также иллюстрирует другую полезную функцию подпримитива:
virtual AcDbEntity*
AcDbEntity::subentPtr(const AcDbFullSubentPath& id) const;
Эта функция возвращает указатель на копию подпримитива, описанного указанным путем, который может тогда быть добавлен к базе данных (как показано в примере).
ПРИМЕЧАНИЕ ожидается, что Вы будете должны перегрузить функции getSubentPathsAtGsMarker (), getGsMarkersAtSubentPath () и subentPtr () когда, Вы создает новые подклассы AcDbEntity. Подсветка () функция, однако, осуществлена в AcDbEntity, выравнивают, и как ожидается, будет перегружен. Однако, если это перегружено, любое новое выполнение этой функции должно назвать AcDbEntity:: подсветкой () чтобы исполнить высвечивание{*увеличение яркости*}.
// Эта функция вызывает{*называет*} getObjectAndGsMarker ()
// чтобы получить объект ID твердых и его gsmarker. Это тогда вызывает
// highlightEdge (), highlightFaces (), и highlightAll () чтобы высветить выбранный
// край, все лица, окружающие тот край, и затем твердое целое.
//
void
highlightTest()
{
AcDbObjectId objId;
int marker;
if (getObjectAndGsMarker(objId, marker) != Acad::eOk)
return;
highlightEdge(objId, marker);
highlightFaces(objId, marker);
highlightAll(objId);
}
// This function uses acedSSGet() to let the user select a
// single entity. It then passes this selection set to
// acedSSNameX() to get the gsmarker. Finally, the entity name
// in the selection set is used to obtain the object ID of
// the selected entity.
//
Acad::ErrorStatus
getObjectAndGsMarker(AcDbObjectId& objId, int& marker)
{
ads_name sset;
if (acedSSGet("_:S", NULL, NULL, NULL, sset) != RTNORM) {
acutPrintf("\nacedSSGet has failed");
return Acad::eInvalidAdsName;
}
// Get the entity from the selection set and its
// subentity ID. This code assumes that the user
// selected only one item, a solid.
//
struct resbuf *pRb;
if (acedSSNameX(&pRb, sset, 0) != RTNORM) {
acedSSFree(sset);
return Acad::eAmbiguousOutput;
}
acedSSFree(sset);
// Walk the list to the third item, which is the selected
// entity’s entity name.
//
struct resbuf *pTemp;
int i;
for (i=1, pTemp = pRb;i<3;i++, pTemp = pTemp->rbnext)
{ ; }
ads_name ename;
ads_name_set(pTemp->resval.rlname, ename);
// Move on to the fourth list element, which is the gsmarker.
//
pTemp = pTemp->rbnext;
marker = pTemp->resval.rint;
acutRelRb(pRb);
acdbGetObjectId(objId, ename);
return Acad::eOk;
}
// This function accepts an object ID and a gsmarker.
// The object is opened, the gsmarker is used to get the
// AcDbFullSubentIdPath, which is then used to highlight
// and unhighlight the edge used to select the object.
// Next, the object’s subentPtr() function is used to get
// a copy of the edge. This copy is then added to the
// database. Finally, the object is closed.
//
void
highlightEdge(const AcDbObjectId& objId, const int marker)
{
char dummy[133]; // space for acedGetString pauses below
AcDbEntity *pEnt;
acdbOpenAcDbEntity(pEnt, objId, AcDb::kForRead);
// Get the subentity ID for the edge that is picked
//
AcGePoint3d pickpnt;
AcGeMatrix3d xform;
int numIds;
AcDbFullSubentPath *subentIds;
pEnt->getSubentPathsAtGsMarker(AcDb::kEdgeSubentType,
marker, pickpnt, xform, numIds, subentIds);
// At this point the subentId’s variable contains the
// address of an array of AcDbFullSubentPath objects.
// The array should be one element long, so the picked
// edge’s AcDbFullSubentPath is in subentIds[0].
//
// For objects with no edges (such as a sphere), the
// code to highlight an edge is meaningless and must
// be skipped.
//
if (numIds > 0) {
// Highlight the edge.
//
pEnt->highlight(subentIds[0]);
// Pause to let user see the effect.
//
acedGetString(0, "\npress <RETURN> to continue...",
dummy);
// Unhighlight the picked edge.
//
pEnt->unhighlight(subentIds[0]);
// Get a copy of the edge, and add it to the database.
//
AcDbEntity *pEntCpy = pEnt->subentPtr(subentIds[0]);
AcDbObjectId objId;
addToModelSpace(objId, pEntCpy);
}
delete []subentIds;
pEnt->close();
}
// This function accepts an object ID and a gsmarker.
// The object is opened, the gsmarker is used to get the
// AcDbFullSubentIdPath, which is then used to highlight
// and unhighlight faces that share the edge used to
// select the object. The object is then closed.
//
void
highlightFaces(const AcDbObjectId& objId, const int marker)
{
char dummy[133];
AcDbEntity *pEnt;
acdbOpenAcDbEntity(pEnt, objId, AcDb::kForRead);
// Get the subentIds for the faces.
//
AcGePoint3d pickpnt;
AcGeMatrix3d xform;
int numIds;
AcDbFullSubentPath *subentIds;
pEnt->getSubentPathsAtGsMarker(AcDb::kFaceSubentType,
marker, pickpnt, xform, numIds, subentIds);
// Walk the subentIds list, highlighting each face subentity.
//
for (int i = 0;i < numIds; i++) {
pEnt->highlight(subentIds[i]); // Highlight face.
// Pause to let the user see the effect.
//
acedGetString(0, "\npress <RETURN> to continue...",
dummy);
pEnt->unhighlight(subentIds[i]);
}
delete []subentIds;
pEnt->close();
}
// This function accepts an object ID. The object is opened,
// and its highlight() and unhighlight() functions are
// used with no parameters, to highlight and
// unhighlight the edge used to select the object. The
// object is then closed.
//
void
highlightAll(const AcDbObjectId& objId)
{
char dummy[133];
AcDbEntity *pEnt;
acdbOpenAcDbEntity(pEnt, objId, AcDb::kForRead);
// Highlight the whole solid.
//
pEnt->highlight();
// Pause to let user see the effect.
//
acedGetString(0, "\npress <RETURN> to continue...",
dummy);
pEnt->unhighlight();
pEnt->close();
}
Acad::ErrorStatus
addToModelSpace(AcDbObjectId &objId, AcDbEntity* pEntity)
{
AcDbBlockTable *pBlockTable;
AcDbBlockTableRecord *pSpaceRecord;
acdbHostApplicationServices()->workingDatabase()
->getSymbolTable(pBlockTable, AcDb::kForRead);
pBlockTable->getAt(ACDB_MODEL_SPACE, pSpaceRecord,
AcDb::kForWrite);
pSpaceRecord->appendAcDbEntity(objId, pEntity);
pBlockTable->close();
pEntity->close();
pSpaceRecord->close();
return Acad::eOk;
}
Пример Заказного Объектного Класса
Следующие разделы показывают заголовку и исходным файлам для класса пользователя, AsdkMyClass, который получен из AcDbObject. Этот класс сохраняет одиночное целочисленное значение, которое может быть установлено и делать запрос с его setData () и getData() функции. Это также осуществляет dwgInFields(), dwgOutFields(), dxfInFields(), и dxfOutFields () функции для записи в файл. Это записано в и читать от файла, так что его исходный файл использует макрокоманду ACRX_DXF_DEFINE_MEMBERS.
Пример заказного режима объектной привязки
Следующий пример демонстрирует создание заказных режимов объектной привязки:
#include "rxobject.h"
#include "ads.h"
#include "adslib.h"
#include "dbmain.h"
#include "dbents.h"
#include "dbosnap.h"
#include "acedinpt.h"
// Socket Osnap mode protocol extension class.
//
class AcmeSocketInfo : public AcDbCustomOsnapInfo {
public:
ACRX_DECLARE_MEMBERS(AcmeSocketInfo);
virtual Acad::ErrorStatus
getOsnapInfo(
AcDbEntity* pickedObject,
int gsSelectionMark,
const AcGePoint3d& pickPoint,
const AcGePoint3d& lastPoint,
const AcGeMatrix3d& viewXform,
AcArray<AcGePoint3d>& snapPoints,
AcArray<int>& geomIdsForPts,
AcArray<AcGeCurve3d>& snapCurves,
AcArray<int>& geomIdsForLines)
};
// This class is registered with AcRx, to be used by the host
// application to look up entity class-specific implementations.
//
ACRX_NO_CONS_DEFINE_MEMBERS(AcmeSocketInfo,AcDbCustomOsnapInfo);
Acad::ErrorStatus
AcmeSocketInfo::getOsnapInfo(
AcDbEntity*,
int,
const AcGePoint3d&,
const AcGePoint3d&,
const AcGePoint3d&,
AcArray<AcGePoint3d>& snapPoints,
AcArray<int>& geomIdsForPts,
AcArray<AcGeCurve3d>& snapCurves,
AcArray<int>& geomIdsForLines)
{
// Associate with AcDbEntity to define default behavior.
//
snapPoints.setLogicalLength(0);
geomIdsForPts.setLogicalLength(0);
snapLiness.setLogicalLength(0);
geomIdsForLines.setLogicalLength(0);
}
// Socket Osnap mode protocol extension object for AcDbLine.
//
class AcmeSocketForLine : public AcmeSocketInfo {
public:
virtual Acad::ErrorStatus
getOsnapInfo(
AcDbEntity* pickedObject,
int gsSelectionMark,
const AcGePoint3d& pickPoint,
const AcGePoint3d& lastPoint,
const AcGeMatrix3d& viewXform,
AcArray<AcGePoint3d>& snapPoints,
AcArray<int>& geomIdsForPts,
AcArray<AcGeCurve3d>& snapCurves,
// If this ASSERT fails, then the pixel size is really position-
// dependent.
//
ASSERT(!vportDrawContext->viewport()->isPerspective());
vportDrawContext->viewport()->getNumPixelsInUnitSquare(AcGePoint3d::kOrigin, pixelArea);
double halfGlyphSizeInDCS =
acdbCustomOsnapManager->osnapGlyphSize() * pixelArea.x / 2.0;
// Draw an asterisk with 4 segments.
//
sSegmentPoints[0].set(
mCurDCSLoc.x-halfGlyphSizeInDCS,
mCurDCSLoc.y-halfGlyphSizeInDCS, 0.0);
sSegmentPoints[1].set(
mCurDCSLoc.x+halfGlyphSizeInDCS,
mCurDCSLoc.y+halfGlyphSizeInDCS, 0.0);
vportDrawContext->geometry().polylineDc( 2, &(sSegmentPoints[0]));
sSegmentPoints[0].set(
mCurDCSLoc.x-halfGlyphSizeInDCS,
mCurDCSLoc.y+halfGlyphSizeInDCS, 0.0);
sSegmentPoints[1].set(
mCurDCSLoc.x+halfGlyphSizeInDCS,
mCurDCSLoc.y-halfGlyphSizeInDCS, 0.0);
vportDrawContext->geometry().polylineDc( 2, &(sSegmentPoints[0]));
sSegmentPoints[0].set(
mCurDCSLoc.x-halfGlyphSizeInDCS,
mCurDCSLoc.y, 0.0);
sSegmentPoints[1].set(
mCurDCSLoc.x+halfGlyphSizeInDCS,
mCurDCSLoc.y, 0.0);
vportDrawContext->geometry().polylineDc( 2, &(sSegmentPoints[0]));
sSegmentPoints[0].set(
mCurDCSLoc.x,
mCurDCSLoc.y-halfGlyphSizeInDCS, 0.0);
sSegmentPoints[1].set(
mCurDCSLoc.x,
mCurDCSLoc.y+halfGlyphSizeInDCS, 0.0);
vportDrawContext->geometry().polylineDc( 2, &(sSegmentPoints[0]));
};
AcmeSocketGlyph* pSocketGlyph = NULL;
// Master object for the socket custom Osnap mode.
//
class AcmeSocketMode : public AcDbCustomOsnapMode {
public:
virtual const char*
localModeString() const {return "SOCket"};
virtual const char*
globalModeString() const {return "SOCket"};
virtual const AcRxClass*
entityOsnapClass() const {return AcmeSocketInfo::desc());
virtual AcGiGlyph*
glyph() const {return pSocketGlyph;);
virtual const char*
tooltipString() const {return "Socket to Me?" };
};
static AcmeSocketMode* pSocketMode = NULL;
/* ================ ObjectARX application interface ============ */
AcRx::AppRetCode
acrxEntryPoint(AcRx::AppMsgCode msg, void*)
{
switch(msg) {
case AcRx::kInitAppMsg:
// Register the class.
//
AcmeSocketInfo::rxInit();
acrxBuildClassHierarchy();
pDefaultSocketInfo = new AcmeSocketInfo;
AcDbEntity::desc()->addX(AcmeSocketInfo::desc(),
pDefaultSocketInfo);
pSocketForLine = new AcmeSocketForLine;
AcDbLine::desc()->addX(AcmeSocketInfo::desc(), pSocketForLine);
//-- };
// Create the glyph object to be returned by the socket
// mode object.
//
pSocketGlyph = new AcmeSocketGlyph;
// Create and register the custom Osnap Mode
pSocketMode = new AcmeSocketMode;
acdbCustomOsnapManager->addCustomOsnapMode(pSocketMode);
// The SOCket Osnap mode is now plugged in and ready to use.
//
break;
case AcRx::kUnloadAppMsg:
// Clean up.
acdbCustomOsnapManager->removeCustomOsnapMode(pSocketMode);
delete pSocketMode;
// Unregister, then delete the protocol extension object.
//
AcDbEntity::desc()->delX(AcmeSocketInfo::desc());
delete pDefaultSocketInfo;
AcDbLine::desc()->delX(AcmeSocketInfo::desc());
delete pSocketForLine;
// Remove the protocol extension class definition.
//
acrxClassDictionary->remove("AcmeSocketInfo");
break;
default:
// Between the initialization and termination of the
// application, all registered objects will be directly
// invoked as needed. No commands or AutoLISP
// expressions are necessary.
//
break;
}
return AcRx::kRetOK;
}
Следующие ObjectARX примеры состоят из
Следующие ObjectARX примеры состоят из двух функций: createXrecord () и listXrecord (). Первая функция добавляет новый xrecord к словарю, добавляет словарь к словари имен объектов, и затем добавляет данные к xrecord. ListXrecord () функция открывает xrecord, получает его список данных, и посылает список, который будет напечатан.
void
createXrecord()
{
AcDbDictionary *pNamedobj, *pDict;
acdbHostApplicationServices()->workingDatabase()
->getNamedObjectsDictionary(pNamedobj, AcDb::kForWrite);
// Check to see if the dictionary we want to create is
// already present. If not, then create it and add
// it to the named object dictionary.
//
if (pNamedobj->getAt("ASDK_DICT", (AcDbObject*&) pDict,
AcDb::kForWrite) == Acad::eKeyNotFound)
{
pDict = new AcDbDictionary;
AcDbObjectId DictId;
pNamedobj->setAt("ASDK_DICT", pDict, DictId);
}
pNamedobj->close();
// Add a new xrecord to the ASDK_DICT dictionary.
//
AcDbXrecord *pXrec = new AcDbXrecord;
AcDbObjectId xrecObjId;
pDict->setAt("XREC1", pXrec, xrecObjId);
pDict->close();
// Create a resbuf list to add to the xrecord.
//
struct resbuf *pHead;
ads_point testpt = {1.0, 2.0, 0.0};
pHead = acutBuildList(AcDb::kDxfText,
"This is a test Xrecord list",
AcDb::kDxfXCoord, testpt,
AcDb::kDxfReal, 3.14159,
AcDb::kDxfAngle, 3.14159,
AcDb::kDxfColor, 1,
AcDb::kDxfInt16, 180,
0);
// Add the data list to the xrecord. Notice that this
// member function takes a reference to resbuf, NOT a
// pointer to resbuf, so you must dereference the
// pointer before sending it.
//
pXrec->setFromRbChain(*pHead);
acutRelRb(pHead);
pXrec->close();
}
// Gets the xrecord associated with the key XREC1 and
// lists out its contents by passing the resbuf list to the
// function printList.
//
void
listXrecord()
{
AcDbDictionary *pNamedobj;
acdbHostApplicationServices()->workingDatabase()
->getNamedObjectsDictionary(pNamedobj, AcDb::kForRead);
// Get the dictionary object associated with the key ASDK_DICT.
//
AcDbDictionary *pDict;
pNamedobj->getAt("ASDK_DICT", (AcDbObject*&)pDict,
AcDb::kForRead);
pNamedobj->close();
// Get the xrecord associated with the key XREC1.
//
AcDbXrecord *pXrec;
pDict->getAt("XREC1", (AcDbObject*&) pXrec,
AcDb::kForRead);
pDict->close();
struct resbuf *pRbList;
pXrec->rbChain(&pRbList);
pXrec->close();
printList(pRbList);
acutRelRb(pRbList);
}
Примеры Преобразования
AcGiViewport класс обеспечивает функции, которые дают Вам, обращаются к графическому конвейеру, позволяя Вам применить каждое преобразование явно и исполняют математику самостоятельно. Если Вы управляете примитивами в графическом конвейере самостоятельно, Вы используете различные формы AcGi многоугольника и полилинии в зависимости от того, где Вы находитесь в графическом конвейере.
AcGiViewportGeometry класс обеспечивает три формы для многоугольников и полилиний, в модели, глазе, и координатах дисплея. Обычно, Вы использовали бы polyline() и polygon () функции, которые требуют модельных координат.
Используйте polylineEye () и polygonEye () если Вы собираетесь работать с координатами глаза, как показано в Примерах 1 и 2. Используйте polygonDc () и polylineDc () если Вы работаете с координатами дисплея.
Следующие разделы содержат четыре примера. Первый пример рисует тот же самый примитив, используя модель, глаз, и координаты дисплея. Его основная цель состоит в том, чтобы демонстрировать, как применить каждое преобразование в графическом конвейере. Второй пример иллюстрирует работу к координатам глаза, чтобы определить переднюю сторону и невидимые поверхности пирамиды. Третий пример иллюстрирует работу к координатам дисплея, чтобы рисовать примитив в размере относительно размера текущего окна. Четвертый пример показывает, как определить полилинию с наименьшим количеством долей, который является визуально неразличимым от один с больше доли.
Примитив Linetype Масштаб
Когда примитив - первый instantiated, его масштаб linetype инициализирован к недопустимому значению. Когда примитив добавлен к базе данных, если масштаб linetype не был определен для примитива, это установлено в поток базы данных linetype значение масштаба. Это значение по умолчанию базы данных сохранено в CELTSCALE системной переменной.
Примитивы
С сетью и примитивами оболочки, Вы можете определить черты для граней, лиц, или вершины в дополнение к основной геометрии. Следующие секции иллюстрируют использование этих примитивов.
Принятие параметров в функциях обратного вызова
В функции повторного вызова, значение и данные отобранного неперекрывающего расположения пропускают как параметр пакета повторного вызова, как показано на определении функции повторного вызова более раннего примера:
static void CALLB accept_OK(ads_callback_packet *cpkt)
{
// DLGOK == User pressed OK.
//
ads_done_dialog(cpkt->dialog, DLGOK);
}
CALLB символ, который появляется перед именем функции, определен как пробел.
Это - просто маркер, чтобы делать это проще для Вас, чтобы расположить функции повторного вызова, когда Вы поддерживаете вашу программу. Однако, Вы должны всегда использовать это в случае, если это определено по-другому в будущем выпуске.
Предшествующий пример просто закрывает диалоговое окно, используя только один из параметров в пакете. Тип данных пакета повторного вызова определен следующей инструкцией:
typedef struct {
ads_hdlg dialog;
ads_htile tile;
char *value;
void *client_data;
int reason;
long x, y;
} ads_callback_packet;
Параметры прошли в пакете, имеют следующие цели:
!!!! Неперекрывающее расположение ßreà tile
dialog |
Метка диалогового окна. | ||
tile |
Метка отобранного неперекрывающего расположения. Вместо принятия ключа отобранного неперекрывающего расположения, пакет PDB передает функции повторного вызова метку неперекрывающего расположения (типа ads_htile). Вы используете метку, чтобы отыскать атрибуты неперекрывающего расположения, включая его ключ, вызывая функцию ads_get_attr_string () (. Ads_get_attr_string () функция не имеет копии AutoLISP.) | ||
value |
Строка, которая содержит значение отобранного неперекрывающего расположения. Пространство для этой строки управляется в соответствии с AutoCAD; обращайтесь с этим как только для чтения. Если Вы должны изменить значение неперекрывающего расположения, используйте ads_set_tile (). Если неперекрывающее расположение - список (или всплывающий список) и никакой элемент не отобран, строка значения пуста (" "). | ||
client_data |
Указатель на специфичные для приложения данные, который был инициализирован ads_client_data_tile (). Если не имеется никаких клиентских данных, это - NULL. | ||
reason |
Причина для повторного вызова. Это зависит, на ч действии пользователь брал. Его значение установлено для любого действия, но Вы должны осмотреть это только, когда действие связано с edit_box, list_box, image_button, или неперекрывающим расположением слайдера. | ||
x, y |
Когда пользователь выбирает кнопку изображения, они установлены в (X, Y) отобранные координаты. Координаты - координаты неперекрывающего расположения в пределах диапазона, который ads_dimensions_tile () возвратился бы для кнопки изображения. |
Например, чтобы отыскать ключ отобранного неперекрывающего расположения, функция повторного вызова могла включать следующий код:
char newtile[TILE_STR_LIMIT];
ads_get_attr_string(cpkt->tile, "key", newtile, TILE_STR_LIMIT);
При поиске строкового значения, убедитесь, что разместили пространство для строки. Этот пример определяет строковую длину, используя константу TILE_STR_LIMIT.
Ads_get_attr_string () функция может отыскивать другие атрибуты со значением таким же образом, что этот пример отыскивает ключ.
Принятие указения точки командой AutoCAD
Некоторые команды AutoCAD (типа TRIM, EXTEND и FILLET) требует, чтобы пользователи определили точку указки также как примитив. Чтобы передавать такие пары примитива и данных точки посредством acedCommand (), Вы должны определить имя примитива сначала и включать пару в RTLB и коды типа результата RTLE.
Следующий типовой кодовый фрагмент создает круг, центрированный в (5,5) и линию, которая простирается от (1,5) до (8,5); это предполагает, что круг и линия созданы в пустом рисунке. Это тогда использует точку указки с командой TRIM, чтобы урезать линию в крае круга. AcdbEntNext () функция находит следующий примитив в рисунке, и acdbEntLast () функция находит последний примитив в рисунке.
ads_point p1;
ads_name first, last;
acedCommand(RTSTR, "Circle", RTSTR, "5,5", RTSTR, "2", 0);
acedCommand(RTSTR, "Line", RTSTR, "1,5", RTSTR, "8,5", RTSTR, "", 0);
acdbEntNext(NULL, first); // Get circle.
acdbEntLast(last); // Get line.
// Set pick point.
p1[X] = 2.0;
p1[Y] = 5.0;
p1[Z] = 0.0;
acedCommand(RTSTR, "Trim", RTENAME, first, RTSTR, "",
RTLB, RTENAME, last, RTPOINT, p1, RTLE,
RTSTR, "", 0);
Приостановка ввода пользователя
Если команда AutoCAD происходит, и AutoCAD сталкивается с символом ПАУЗЫ как параметр к acedCommand () или acedCmd (), команда временно приостановлена, чтобы позволить прямой ввод пользователя, включая перемещение. Символ ПАУЗЫ состоит из строки, которая содержит одиночную наклонную черту влево. Это подобно наклонной черте влево механизм паузы, предусмотренный меню.
Следующий запрос к acedCommand () вызывает команду ZOOM и затем использует символ ПАУЗЫ так, чтобы пользователь мог выбирать одну из опций ZOOM.
result = acedCommand(RTSTR, "Zoom", RTSTR, PAUSE, RTNONE);
Следующий запрос начинает команду CIRCLE, устанавливает среднюю точку как (5,5), и затем делает паузу, чтобы позволить пользователю перетаскивать радиус круга на экране. Когда пользователь определяет выбранную точку (или вводит выбранный радиус), функциональные резюме, тянущий линию от (5,5) до (7,5).
result = acedCommand(RTSTR, "circle", RTSTR, "5,5",
RTSTR, PAUSE, RTSTR, "line", RTSTR, "5,5", RTSTR,
"7,5", RTSTR, "", 0);
Проблемы длинных транзакций для объектов пользователя
Длинные транзакции – метод борьбы против обычных проблем, следующих из объектов, которые не должны имитироваться, или меж-объектные ссылки, которые не обработаны.
Если LongTransactionManager (LTM) находит, что это должно имитировать, фильтрованный класс возражает против законченного длинную операционную отладку () или checkIn (), это прервет полную операцию. Если это находит AcDbSoftPointerId или AcDbHardPointerId, который не в имитации IdMap, это также прервется.
Приложения, которые должны предотвратить их объекты от включения как имитируется в длинных трудах, должны регистрировать те объекты, использующие AcApLongTransactionManager::addClassFilter () функция.
AcDbProxyEntity и AcDbProxyObject всегда фильтруются, так когда приложение - не, подарок{*настоящее*}, все его объекты будет фильтрован автоматически.
Wblock имитирующие маркеры{*дескрипторы*} все жесткие ссылки{*справочники*} указателя, но глубоко имитация не требует никакого типа ссылки{*справочников*}, которая будет отображена. Оба из этих типов имитации используются в длинных трудах, в зависимости от типа сделки, это. Если прикладные использования или этих типов ссылок{*справочников*}, или маркеров{*дескрипторов*} xdata, то его объекты будут отклонены от длинных трудов, если приложение берет дополнительные шаги, чтобы обработать ссылки{*справочники*}. Это означает, что, если приложение не загружено, то его объекты и ссылки{*справочники*} будут автоматически предотвращены от участия в длинных трудах, и любые данные должны сохраниться в его Отсутствие.
Используйте длинные операционные и глубокие уведомления аналога, чтобы прервать имитацию их объекта и ссылок{*справочников*}, и добавляться необходима ли имитация объекта или отображение. См. глубокую документацию уведомления аналога и выборки для получения дополнительной информации на этом.
Если объект с мягкой ссылкой{*справочниками*} указателя имитируется (или жесткая ссылка{*справочники*} указателя в глубоком аналоге), приложение должно удостовериться, что ссылка{*справочники*} ИДЕНТИФИКАТОР находится в IdMap, или как отображенная пара ИДЕНТИФИКАТОРА, или имитируемая пара ИДЕНТИФИКАТОРА. Отображения обычно используются, когда объекты обращаются{*относятся*} к некоторому общему{*обычному*} словарю, который приложение обслуживает{*поддерживает*} в пределах рисунка. В глубоком аналоге, отображение может состоять из IdPair, где key = value. В аналоге wblock между рисунками, IdPair отобразил бы словарь одной базы данных со словарем другой базы данных.
Ссылка{*справочники*} имитируется приложением, используя или deepClone () или wblockClone () от повторного вызова уведомления.
Взятие этих шагов гарантирует “ транзитивное замкнутое выражение. ”, чтобы гарантировать что, набор объектов, которые обращаются{*относятся*} к друг другу, может быть проверен, и затем проверен назад в снова, без того, чтобы ломать{*нарушить*} отношения объекта, все связанные объекты проверены вместе. Например, если любую границу или ассоциативная штриховка непосредственно пропускают в отладку (), код штриховки прибавляет, что вся граница возражает против списка объектов, которые будут проверены. Это - то, как это достигает транзитивного замкнутого выражения. Если бы это не случалось, LTM нашел бы мягкий указатель IDs штриховки на его границы. Если это нашло, что граничный объект, столь упомянутый отсутствовал от имитации, длинная сделка будет прервана. Если это не делал этого, даже если никакие изменения{*замены*} не были сделаны к проверенной штриховке, первоначальная штриховка будет терять ее ассоциативность на регистрации.
Иногда, имеются известные ссылки{*справочники*}, которые не должны быть решены. Одно местоположение было бы объект, который следит за всеми объектами, которые используют это. Например, блочные отчеты{*записи*} таблицы сохраняют список всех блочных ссылок{*справочников*}, которые используют их. Правильно только проверить одну из ссылок{*справочников*}, так что Вы должны позволить длинному операционному механизму знать, что остальная часть ссылок{*справочников*} не должна имитироваться. Имеются несколько путей, которыми это может быть сделано.
Имеются некоторые примеры:
§ если приложение знает, относительно, которые объекты упомянуты, но не будут имитироваться — в beginWblockObjects (), beginDeepClone (), или beginCheckOut () уведомление —, они могут прибавлять объект ID упомянутого объекта к IdMap для имитации. Рекомендуемый подход состоит в том, чтобы установить значение в NULL, и idPair как не имитируемый. Например idMap.assign (idPair (идентификатор, AcDbObjectId:: kNull, kFalse); Если объект должен имитироваться позже, idPair будет изменен{*заменен*} соответственно.
§ вышеупомянутое отображение мог также быть сделан изнутри wblockClone объекта () метод, если это уже отменяет.
§ если ссылка{*справочники*} - компонент данных объекта, который зарегистрирован из использования dwgOutFields (), тогда может быть возможно избежать длинного операционного испытания законности. Испытание сделано, регистрируя из ИДЕНТИФИКАТОРОВ, использующих тип kIdFiler регистратора. Чтобы избегать испытания, делайте не файл из ИДЕНТИФИКАТОРОВ, которые не должны имитироваться, в течение этого типа записи в файл. Однако, не проведите{*держите*} никакое монопольное использование ИДЕНТИФИКАТОРАМИ из этой записи в файл, или других особенностей, которые используют этого регистратора, подобно частичному сохраняют{*экономят*} и загружают, не может должным образом обрабатывать ваши объекты. Единственные безопасные ИДЕНТИФИКАТОРЫ, чтобы отказать от этого регистратора - объекты AcDbSoftPointerId и AcDbHardPointerId.
§ если ИДЕНТИФИКАТОР - фактически в постоянном реакторе, возможно найти это, используя реактор iterator. Имеется пример того, как объект словаря находит и прибавляет его ИДЕНТИФИКАТОР к IdMap в течение beginWblockClone () уведомление.
beginWblockClone(..., AcDbIdMapping& idMap)
{
...
AcDbDictionaryIterator* pIter = pDict->newIterator();
AcDbObject* pObj;
for ( ; !pIter->done(); pIter->next()) {
acdbOpenObject(pObj, pIter->objectId(), kForRead);
AcDbVoidPtrArray* pReactors = pObj->reactors();
void* pReactor;
AcDbObjectId rId;
MyReactor* pMyReactor;
if (pReactors) {
for (int i = 0; i < pReactors->length(); i++) {
pReactor = pReactors->at(i);
if (acdbIsPersistentReactor(pReactor)) {
rId = acdbPersistentReactorObjectId(pReactor);
if (acdbOpenObject(pMyReactor, rId, kForRead) == eOk) {
pMyReactor->close();
AcDbIdPair idPair(rId,
AcDbObjectId::kNull, kFalse);
idMap.assign(idPair);
}
}
}
}
pObj->close();
}
delete pIter;
pDict->close();
}
Прокси-объект для пользователя
Сообщение AutoCAD уведомляет пользователей относительно создания прокси-объектов. Пользователи могут читать относительно прокси-примитивов, использующих команду LIST. Они могут также сталкиваться с прокси-объектами из-за способа, которым примитивы отображены и путь, которым они отвечают на редактирование.
AutoCAD отображает сообщение немедленно после любой команды, которая заставляет прокси-объект быть созданной (например, DXFIN, XREF, ВСТАВЛЯТЬ). Сообщение включает число прокси-примитивов, созданных с визуальным представлением, число созданных прокси-примитивов, который не может быть отображен из-за отсутствия родительского приложения, и числа прокси-объектов, созданных без визуального представления.
Операция LIST, выполненная на прокси-объекте идентифицирует приложение, которое создавало заказной объект, и идентифицирует объект как являющийся или типа ACAD_PROXY_OBJECT или ACAD_PROXY_ENTITY.
Пользователи могут управлять дисплей прокси-объектов с PROXYSHOW
переменной системы, которая имеет следующие опции:
1 Ни один показанный
2 Графическое представление
3 Границы поля
Проверка ошибок
Когда Вы пишете в filer, Вы не должны исполнить промежуточную проверку ошибок. Как только с состоянием ошибки сталкиваются, filer возвращает то же самое состояние ошибки всем запросам записи, пока состояние не очищено регистратором.
Каждый класс filer имеет getFilerStatus () функция, которая возвращает его состояние.
Когда Вы считываете файл, Вы можете хотеть проверить состояние filer, если Вы полагаетесь на успех или неудачу для вашего следующего шага.
Прозрачный против Модальных Команд
Команда может быть или прозрачна или модальная. Прозрачная команда может быть вызвана, когда пользователь запрашивается относительно ввода. Модальная команда может быть вызвана только, когда AutoCAD переносит приглашение ко вводу команды и никакие другие команды, или программы в настоящее время активны. CommandFlags параметр к AcEdCommandStack:: addCommand () функция определяет ли
Новая команда - модальный (ACRX_CMD_MODAL) или прозрачный (ACRX_CMD_TRANSPARENT). CommandFlags параметр также определяет другие опции для команды. См. AcEdCommandStack в ObjectARX справочниках. Прозрачные команды могут быть вложены, только один уровень (то есть основная команда вызван, который вызывает одну прозрачную команду).
Если Вы создаете множественные команды, которые работают на общем{*обычном*} наборе глобальных объектов рассматривают, должны ли Вы делать их модальными так, чтобы они не Вмешайтесь друг с другом. Если такие столкновения - не проблема, делая новые команды прозрачные результаты в большей гибкости использования.
Путь Подпримитива
Путь подпримитива уникально идентифицирует подпримитив в пределах специфического примитива в рисунке. Этот путь, класса AcDbFullSubentPath, состоит из массива объекта IDs и объекта ID подпримитива:
{ AcDbObjectIdArray mObjectIds;
AcDbSubentId mSubentId;
}
Массив содержит объект IDs, которые определяют путь к “основному” примитиву.
Например, блочная ссылка{*справочники*} (примитив, что ссылки{*справочники*} блочный отчет{*запись*} таблицы) могли бы содержать два поля, каждый из типа AcDb3dSolid. Массив объектов ID содержит два входа: ИДЕНТИФИКАТОР блочной ссылки{*справочников*}, сопровождаемой ИДЕНТИФИКАТОРОМ основного примитива [InsertID, SolidID].
Второй элемент AcDbFullSubentPath - объект AcDbSubentId, который имеет тип подпримитива (вершина, край, или лицо) и индекс подпримитива в списке. Используйте тип функций AcDbSubentId () и index() чтобы обратиться к данным члена.
Используя предыдущий пример, второй край твердых будет иметь его AcDbFullSubentPath как
{(InsertID, solid1ID)
(kEdgeSubentType, 2)};
Если бы Вы имеете твердый только, AcDbFullSubentPath был бы следующим образом для первого
лица твердых.
{(solidID)
(kFaceSubentType, 1)};
Путь поиска файлов
Если Вы не определяете, путь поиска файлов, загружая функции типа arxload ищет приложение в каталогах, указанных путем библиотеки AutoCAD.
Путь библиотеки AutoCAD включает следующие каталоги в показанном порядке:
текущий каталог.
каталог, который содержит файл текущего рисунка.
каталоги, указанные путем поддержки (см. Руководство Настройки AutoCAD).
каталог, который содержит программные файлы AutoCAD.