CAD开发者社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

ObjectARX 开发指南

覆盖 wblockClone() 函数

2022-12-31 18:03| 发布者: admin| 查看: 458| 评论: 0|来自: AutoCAD

执行 wblock 克隆操作时,AutoCAD 将为新图形构造一个有效的数据库,其中包含命名对象字典、所有符号表和完整的标题变量集。以下代码近似于 的默认实现。列出的步骤对应于覆盖 deepClone() 函数部分中列出的步骤。wblockClone()

该函数使用类 AcDbWblockCloneFiler 的文件管理器,该文件管理器返回主对象的硬指针和硬所有者连接的列表。在调用这些子对象之前,您需要检查子对象的所有者。此时,您将执行以下两项操作之一:wblockClone()wblockClone()

  • 如果您是对象的所有者,请将子对象的所有者设置为您自己的克隆。
  • 如果您不是对象的所有者,请在函数调用中将克隆的数据库作为参数传入。此时,对象将追加到新数据库中,接收对象 ID,并放入 ID 映射中。此对象的 ID 映射条目将为字段指定 FALSE。pOwnerwBlockClone()isOwnerTranslated

ifis 设置为数据库,必须将克隆对象的所有者设置为与原始对象的所有者相同的所有者。然后,当 AutoCAD 转换引用时,它将更新对新数据库中克隆对象的所有者引用。pOwnerwblockClone()

确保克隆所有拥有对象非常重要。在 wblock 克隆期间,AutoCAD 始终克隆符号表、命名对象字典、模型空间和图纸空间(对于克隆上下文以外的上下文)。具有拥有对象的应用程序负责确保在必要时克隆这些对象。如果未克隆所属对象且未在 ID 映射中找到,则 wblock 克隆将中止。AcDb::kDcXrefBindAcDb::eOwnerNotSet

复制引用符号表记录的实体时,必须以对象的所有者身份传入数据库。例如,假设您正在调用一个球体对象。块表记录是此球体对象的硬所有者。球体对象包含对图层表的硬引用。wblockClone()

首先,在该阶段,使用默认元素创建新数据库并进行设置。下图显示了模型空间块表记录和图层表,因为它们与本主题相关。在此阶段发生的克隆始终发生在 wblock 操作期间。beginDeepClone()

在阶段,将克隆选择集,如下图所示。在此示例中,将克隆球体。beginWblock()

因为球体包含指向图层的硬指针 1 1 被克隆。

接下来,需要翻译指针以引用克隆的对象,如下图所示。通知指示此阶段的开始。beginDeepCloneXlation()

上图时的ID映射如下:beginDeepCloneXlation()

上图的ID映射

       

.KEY

价值

是克隆的

是主要

isOwnerXlated

BTR1

BTR2

SPH1

SPH2

LT1

LT2

TRUE

FALSE

*

LTR1

LTR2

TRUE

FALSE

FALSE**

* The layer table's owner is the database itself, so this entry is meaningless.

** During translation, this setting indicates that the layer will have its owner translated from LayerTable 1 to LayerTable 2 .

The wblock clone process is used for xref bind as well as wblock. The needs of both are very similar, but there are a few differences that require special attention when overriding the .wblockClone()

Wblock clones all selected entities. However, xref bind never clones entities that are in paper space. This leaves two things to consider when creating objects or entities, and using . First, at the beginning of any 's , check to see if the cloning context is and, if so, whether the entity is being cloned in paper space. If it is, then no cloning should be done and should return .AcDbHardPointerIdsAcDbEntitywblockClone()AcDb::kDcXrefBindwblockClone()Acad::eOk

If your custom class has any that can point to entities (such as we do with ), then the entities might be in paper space and will therefore not get cloned. In that event, the will be set to .AcDbHardPointerIdsAcDbGroupAcDbHardPointerIdsNULL

Wblock does not follow hard pointer references across databases. However, xref bind does this all the time. For example, an entity in an xref drawing can be on a VISRETAIN layer in the host drawing. So, if you implement your with a loop to check for subobjects, and the subobject's database is not the same as that of the object being cloned, you must skip the subobject if the cloning context is not . For example:wblockClone()AcDb::kDcXrefBind

if(pSubObject->database() != database()
    && idMap.deepCloneContext() != AcDb::kDcXrefBind)
{
    pSubObject->close();
    continue;
}

The following code shows overriding to implement it for a custom entity (). This function is invoked with the code shown in Editor Reactor Notification Functions.wblockClone()AsdkPoly

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.
    //
    AcDbObjectId pspace;
    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 && isPrimary) {
        pBTR->appendAcDbEntity(pClone);
    } else {
        pDb->addAcDbObject(pClonedObject);
   }
    // 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: Add the new information to the ID map. 
    // We must let the idMap entry know whether the clone's owner is
    // correct, or needs to be translated later.
    //
    idMap.assign(AcDbIdPair(objectId(), pClonedObject->objectId(),
        Adesk::kTrue,
        isPrim, (Adesk::Boolean)(pOwn != NULL) ));
    pClonedObject->setOwnerId((pOwn != NULL) ?
        pOwn->objectId() : ownerId());
    // Step 6:
    // 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 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;
}
Note: Remember that when the function is in the process of executing, the pointer references in the destination database have not yet been translated. The following code does not work correctly, because although it tries to open the model space block table record of the destination database, the model space block table record of the source database is opened instead. The untranslated reference in the destination database's block table is still referring to the model space of the source database.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();
 if (pDestDb == pSrcDb) {
 // It's a fastWblock, so we use the source objectId.
 destId = srcModelSpaceId;
 } else {
AcDbIdPair idPair;
idPair.setKey(srcModelSpaceId);
idMap.compute(idPair);
destId = idPair.value();
}
AcDbBlockTableRecord *pDestBTR;
acdbOpenAcDbObject((AcDbObject*&)pDestBTR,
destId, AcDb::kForRead, Adesk::kTrue);

路过

雷人

握手

鲜花

鸡蛋

最新评论

QQ|Archiver|CAD开发者社区 ( 苏ICP备2022047690号-1 )

GMT+8, 2024-5-19 15:27

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

返回顶部