Тема: копирование объектов и ссылок

Здравствуйте!
Есть объетк(от AcDBEntity) в котором хранятся ссылки на другие объекты. Например (стена и набор проемов, набор)

class OarxWall:public AcDbEntity {
AcDbObjectIdArray winList; // список проемов
};

Можно ли сделать, так чтобы при копировании примитива (стена) и объектов на которые он ссылается через AcDbObjectIdArray(проемы) обновлялись ссылки во вновь скопированном примитиве(новая скопированная стена), т.е. вектор ссылок инициировался идентификаторами вновь скопированных объектов(новые скопированные проемы)???
Большое спасибо, Алексеев

Re: копирование объектов и ссылок

Можно, но боюсь придется это делать руками - упорно и усердно.
Т.е. отслеживать какие объекты от каких откопировались, т.е. надо получить список скопированных объектов и при этом знать от каких объектов они откопированы. Если эта информация известна то алгоритм тривиален. -
Пробегаем по массиву winList - для каждой ссылки смотрим - если среди откопированных объектов есть объект который был откопирован от того на который ссылается ссылка, то меням ссылку со старого на новый. Если нет, то удалям из списка. Т.е. если скопировали стену без окна, то ссылку надо удалить.
Проблема в том, чтобы получить список скопированных объектов и при этом знать от кого они откопированы. Эта другая решабельная задача, если надо можно обсудить как.

Re: копирование объектов и ссылок

Здравстуйте, большое спасибо за отзыв на мой вопрос.
Списка скопированных объектов нет, как отследить с какого примитива снята копия я, честно говоря, не знаю.
А вот, deepclone, например или AcDbHardPointerId/AcDbSoftPointerId в этой ситуации не могут быть полезны???
Большое спасибо, Алексеев

Re: копирование объектов и ссылок

Нет, не помогут. Они сделаны для того чтобы следить за целостностью при копировании из документа в документ. И за целостностью при удалении одного из объектов. Т.е. если проем убили, то ссылка из стены на проем должна умереть и т.п.
Отслеживание того с какого примитива снята копия - вещь запутанная во всех отношениях. Я про это на форуме писал много раз.
1) Используется DataBase реактор, AcEdInputContextReactor в сочетании. - для нахождения скопированных объектов.
2) От кого откопировали к сожалению я смог сделать только для своих примитивов (не автокадовских). Переопределяется метод getTransformedCopy(...) и для копируемого объекта проставляется переменная от кого он копируется. Типа pEnt->copyFrom = this->objId; в таком духе.
В сочетании 1 и 2 получаем список скопированных объектов с инфрмацией от кого они скопированы.
Но это на словах все просто... Повторюсь что придется париться очень долго т.к. куча нюансов - копирование из чертежа в чертеж и т.п. Два раза вместо одного в определенных случааях приходит событие в DataBaseReactor о добавлении объекта при копировании и т.п. Короче плакать хочется. Я на это убил в общей сложности наверное месяца два.

Re: копирование объектов и ссылок

Отслеживание того с какого примитива снята копия - вещь запутанная во всех отношениях.

Полностью согласен, скажу больше, вещь вредная и ненужная.
Есть способ, который и должен реализовываться в таких случаях:
(взято из недр autodesk.autocad.objectarx)
мне помогло :)

You could try embedding an AcDbBlockReference entity in your custom entity.
This is not a trivial operation, but it would give you essentially what you
want.  I'm not sure how well this will work with an AcDbBlockReference
because AcDbBlockReference is a complex entity (i.e. it can have
subentities - AcDbAttributes), but if you don't add any attributes to your
embedded blockReference it might work ok.
Here's a document I wrote about this back during AutoCAD2000 development
when I put in the dxf support for this mechanism (this was supposed to go
into the ARX documenation, but I don't see it in there):
There are essentially two ways to embed an object in another object, either
the encapsulating object has a data member that is an actual embedded
object, or the encapsulating object has a pointer to an object (that object
is then considered to be embedded).  In either case the encapsulating object
is responsible for the allocation/deallocation of the embedded object and
the encapsulating object must also forward all calls to the embedded
object's methods since AutoCAD will know nothing about the embedded object.
So, to display the embedded object, the encapsulating object's worldDraw
must make a call to the embedded object's worldDraw.
For getGripPoints()/getStretchPoints() you will need to call the embedded
object's method last so that it's points will end up with indices higher
than those of the encapsulating entity.  Then, in
moveGripPointsAt()/moveStretchPointsAt() if any of the indices passed in are
above the range of those of the encapsulating entity, you should subtract
off the highest index value of the encapsulating entity and pass the result
into the embedded object's moveGripPointsAt()/moveStretchPointsAt(). For
example here's the pertinent code from a version of the ARXLAB's jblob
sample updated to have an embedded AcDbCircle entity:
Acad::ErrorStatus
Jblob::getGripPoints(
    AcGePoint3dArray& gripPoints,
    AcDbIntArray& osnapMasks,
    AcDbIntArray& geomIds) const
{
    assertReadEnabled();
    gripPoints.append(mp);
    gripPoints.append(mp + 0.5 * (mpblob - mp));
    gripPoints.append(mpblob);
    AcGeVector3d xoff(mrblob, 0, 0);
    AcGeVector3d yoff(0, mrblob, 0);
    gripPoints.append(mpblob + xoff);
    gripPoints.append(mpblob + yoff);
    gripPoints.append(mpblob - xoff);
    gripPoints.append(mpblob - yoff);
    return circle.getGripPoints(gripPoints, osnapMasks, geomIds);
}
Acad::ErrorStatus
Jblob::moveGripPointsAt(
    const AcDbIntArray& indices,
    const AcGeVector3d& offset)
{
    AcGePoint3d oldquad, newquad;
    assertWriteEnabled();
    AcDbIntArray circleIndices;
    for (int i = 0; i < indices.length(); i++) {
        int idx = indices[i];
        switch(idx) {
        case 0:
            mp += offset;
            continue; // stretch begin point
        case 1:
            mp += offset;
            mpblob += offset;
            continue; // move
        case 2:
            mpblob += offset;
            continue; // stretch blob center
        // stretch blob radius:
        //
        case 3:
            oldquad = mpblob + AcGeVector3d(mrblob, 0, 0);
            break;
        case 4:
            oldquad = mpblob + AcGeVector3d(0, mrblob, 0);
            break;
        case 5:
            oldquad = mpblob - AcGeVector3d(mrblob, 0, 0);
            break;
        case 6:
            oldquad = mpblob - AcGeVector3d(0, mrblob, 0);
            break;
        default:
            if (idx > 6)
                circleIndices.append(idx - 7);
            continue;
        }
        newquad = oldquad + offset;
        mrblob = newquad.distanceTo(mpblob);
    }
    if (circleIndices.length() > 0)
        return circle.moveGripPointsAt(circleIndices, offset);
    else
        return Acad::eOk;
}
For filing purposes, the encapsulating object must call the embedded
object's dwgOutFields/dwgInFields/dxfOutFields/dxfInFields methods from
within the its own such methds.
For dwg filing, the call to the embedded object's dwgOutFields/dwgInFields
methods may occur at any point in the encapsulating object's dwgOutFields
(after the call to the base class's method of course), just make sure that
the call occurs in the same place in both dwgOutFields() and dwgInFields().
For example, here's the pertinent code from the updated jblob sample
program:
Acad::ErrorStatus
Jblob::dwgInFields(AcDbDwgFiler* filer)
{
    assertWriteEnabled();
    AcDbEntity::dwgInFields(filer);
    filer->readItem(&mp);
    filer->readItem(&mpblob);
    filer->readItem(&mrblob);
    filer->readItem(&mnormal);
    return circle.dwgInFields(filer);
}
Acad::ErrorStatus
Jblob::dwgOutFields(AcDbDwgFiler* filer) const
{
    assertReadEnabled();
    AcDbEntity::dwgOutFields(filer);
    filer->writeItem(mp);
    filer->writeItem(mpblob);
    filer->writeItem(mrblob);
    filer->writeItem(mnormal);
    return circle.dwgOutFields(filer);
}
For DXF filing, the embedded object must be filed out/in after all the data
of the encapsulating object has been filed out/in so the call to the
embedded object's dxfOutFields/dxfInFields method should come last in the
encapsulating object's dxfOutFields/dxfInFields methods code.  There is also
a need for some sort of separator between the encapsulating object's data
and the subsequent embedded object's data.  This separator must be similar
in function to the groups 0 or 100 in that it must cause the filer to stop
reading data.  The normal DXF group code 0 cannot be used because DXF
proxies use the group code 0 to tell when to stop reading data.  The group
code 100 could have been used, but that might have caused unnecessary
confusion when manually reading a DXF file and there was a need to
distinguish when an embedded object was about to be written out in order to
do some internal bookkeeping.  So, a new DXF group code 101 has been
introduced.
The new AcDb::DxfCode enum value for DXF group code 101 is:
 AcDb::kDxfEmbeddedObjectStart.
The data string "Embedded Object" will be automatically written out by the
filer for this DXF group code.
There are also two new methods on the AcDbDxfFiler class:
virtual Acad::ErrorStatus
AcDbDxfFiler::writeEmbeddedObjectStart();
The implementation of this function should be (and is in the ARX internal
filers) to write out the AcDb::DxfCode of AcDb::kDxfEmbeddedObjectStart (DXF
group code 101) and suppress the writing out of the AcDb::DxfCode of
AcDb::kDxfStart (DXF group code 0) for the embedded object that follows.  A
string data value of "Embedded Object" should always be (and is in the ARX
internal filers) written out for the AcDb::kDxfEmbeddedObjectStart group
code.
This method must be called in the encapsulating object's dxfOutFields()
method just before calling the dxfOutFields() method of the embedded object.
If multiple embedded objects are involved, then this method must be called
just before each embedded object's dxfOutFields() method is called.  This
method should  only be called in the dxfOutFields() of the encapsulating
object.
The base class implementation of this method is to abort the program, so it
must be overridden in derived classes on which the method is expected to be
called.
Returns Acad::eOk.
virtual Adesk::Boolean
AcDbDxfFiler::atEmbeddedObjectStart();
The implementation of this function should be (and is in the ARX internal
filers) to test to see if the filer is currently pointing to an item with an
AcDb::DxfCode of AcDb::kDxfEmbeddedObjectStart.  If this is the case,  then
the filer moves the file pointer to the next item and returns Adesk::kTrue.
Otherwise the file pointer is left alone and Adesk::kFalse is returned.
The base class implementation of this method is to abort the program, so it
must be overridden in derived classes on which the method is expected to be
called.
To demonstrate how these new methods are used, here's the pertinent code
from an updated jblob sample program:
Acad::ErrorStatus
Jblob::dxfInFields(AcDbDxfFiler* filer)
{
    assertWriteEnabled();
    struct resbuf rb;
    Acad::ErrorStatus es = AcDbEntity::dxfInFields(filer);
    if (es != Acad::eOk) {
        return es;
    }
    if (!filer->atSubclassData(kClassName)) {
        return Acad::eBadDxfSequence;
    }
    mnormal = AcGeVector3d(0, 0, 1); // set default value:
    while (es == Acad::eOk) {
        if ((es = filer->readItem(&rb)) == Acad::eOk) {
            switch(rb.restype) {
            case AcDb::kDxfXCoord:
                mp.set(rb.resval.rpoint[X], rb.resval.rpoint[Y],
                    rb.resval.rpoint[Z]);
                break;
            case AcDb::kDxfXCoord+1:
                mpblob.set(rb.resval.rpoint[X], rb.resval.rpoint[Y],
                    rb.resval.rpoint[Z]);
                break;
            case AcDb::kDxfReal:
                mrblob = rb.resval.rreal;
                break;
            case AcDb::kDxfNormalX:
                mnormal.set(rb.resval.rpoint[X], rb.resval.rpoint[Y],
                    rb.resval.rpoint[Z]);
            }
        }
    }
    if (filer->atEmbeddedObjectStart())
        return circle.dxfInFields(filer);
    else {
        filer->setError(Acad::eMissingDxfField,
            "missing expected embeddedObject marker");
        return filer->filerStatus();
    }
}
Acad::ErrorStatus
Jblob::dxfOutFields(AcDbDxfFiler* filer) const
{
    assertReadEnabled();
    AcDbEntity::dxfOutFields(filer);
    filer->writeItem(AcDb::kDxfSubclass, kClassName);
    filer->writeItem(AcDb::kDxfXCoord, mp);
    filer->writeItem(AcDb::kDxfXCoord + 1, mpblob);
    filer->writeItem(AcDb::kDxfReal, mrblob);
    if (filer->includesDefaultValues()
        || mnormal != AcGeVector3d(0,0,1))
    {
        filer->writeItem(AcDb::kDxfNormalX, mnormal);
    }
    filer->writeEmbeddedObjectStart();
    return circle.dxfOutFields(filer);
}
Here's what the output will look like in a DXF file (the 310 group data
strings were shortened for readability):
0
JBLOB
  5
52
330
19
100
AcDbEntity
  8
0
 92
      256
310
00010000040000003C0000000600000002000000...
310
000000000000000000000000000000F03F700000...
310
0000
100
Jblob
 10
4.026791
 20
3.172968
 30
0.0
 11
5.916743
 21
5.299622
 31
0.0
 40
1.458724
101
Embedded Object
100
AcDbEntity
100
AcDbCircle
 10
5.916743
 20
5.299622
 30
0.0
 40
0.729362
"lado" <lado@cospec.co.kr> wrote in message
news:9882F084408928ED5A3875EAA53672BA@in.WebX.maYIadrTaRb...
> ...Thank you for your reply again...
>
> > because it's used by our internal code to add entities.  But, you still
> > should not try to use it to add an entity to a database.
>
> Ahh... the expression 'internally used' in the ARX reference that I hate
too
> much...
>
> > In other words, you cannot do what you are trying to do.
>
> Yes... I understand... It's so sad to me...
> 'MyDoor' custom class derives other class 'MyEntity'(should do..)... so,
> deriving AcDbBlockReference is impossible... (as you see, Multiple
> Inheritance also impossible..)
> I'd better give it up...
>
>

Re: копирование объектов и ссылок

Еле дочитал, до конца еще не совсем въехал, но что-то мне говорит, что не для всех случаев эта концепция применима. Более того про это же написано в самом конце.
Это можно отнести к недостаткам библиотеки ObjectARX, что при копировании одного объекта от другого об этом не приходит никаких сообщений по факту. Есть какие-то методы, но все они реакторы самих объектов и т.п. и т.д.
Например принцип Undo вообще гнило проработан. Т.е. если Вам надо следить за структурой ссылок между объектами, то кроме Undo - Вам вообще ничего не приходит. Приходится делать рефрешь по всему состоянию системы... и еще много недоработок библиотеки.