Сообщество программистов Autodesk в СНГ

ADN Club => ObjectARX => Тема начата: Debalance от 23-11-2019, 21:33:16

Название: Некорректное разбиение блока
Отправлено: Debalance от 23-11-2019, 21:33:16
Попался мне один блок (см. вложение), который не получается корректно разбить программным способом. При этом, используя стандартный инструмент в AutoCAD - проблем не возникает. Для разбиения использовал следующий классический кусок кода:
Код - C++ [Выбрать]
  1. int SSret = acedSSGet(L":$:K", promptSS, &KeywordsString.at(0), NULL, ssname1);
  2. if(SSret == RTNORM)
  3. {
  4.         Adesk::Int32 TmpLength;
  5.         acedSSLength(ssname1, &TmpLength);
  6.         if (TmpLength > 0)
  7.         {
  8.                 AcDbObjectId ObjectId;
  9.                 ads_name ename;
  10.                 for (Adesk::Int32 i = 0; i < TmpLength; i++)
  11.                 {
  12.                         acedSSName(ssname1, i, ename);
  13.                         acdbGetObjectId(ObjectId, ename);
  14.                         AcDbObject *pObj;
  15.                         if (acdbOpenObject(pObj, ObjectId, AcDb::kForRead) == Acad::eOk)
  16.                         {
  17.                                 AcDbVoidPtrArray pSet;
  18.                                 Acad::ErrorStatus es;
  19.                                 AcDbEntity *pEnt = AcDbEntity::cast(pObj);
  20.                                 es = pEnt->explode(pSet);
  21.                                 if (es != Acad::eNotApplicable)
  22.                                 {
  23.                                         if (!pSet.isEmpty())
  24.                                         {
  25.                                                 OutputDebugStringW((boost::wformat(L"\npSet Size: %d, es: %d") % pSet.logicalLength() % es).str().c_str());
  26.                                                 for (int i = 0; i < pSet.logicalLength(); i++)
  27.                                                 {
  28.                                                         AcRxObject *TmpObject = static_cast<AcRxObject*>(pSet.at(i));
  29.                                                         if (TmpObject != NULL)
  30.                                                         {
  31.                                                                 OutputDebugStringW((boost::wformat(L"\nObject: %s") % TmpObject->isA()->name()).str().c_str());
  32.                                                         }
  33.                                                 }
  34.                                                 pSet.setLogicalLength(0);
  35.                                         }
  36.                                 }
  37.                                 pObj->close();
  38.                         }
  39.                 }
  40.         }
  41. }
  42.  
В результате работы кода pEnt->explode(pSet) возвращает eCannotScaleNonUniformly. При этом в набор pSet не попадает ни одна из вставок блоков, которые присутствуют внутри исходного блока.
Как "причесать" код, чтобы работал как стандартный инструмент?
Название: Re: Некорректное разбиение блока
Отправлено: Александр Ривилис от 23-11-2019, 21:39:29
Стандартный метод explode не расчленяет блоки, у которых масштабные коэффициенты по X, Y, и Z отличны:

(https://live.staticflickr.com/65535/49111499451_7c82de16d1_o.png)

Название: Re: Некорректное разбиение блока
Отправлено: Debalance от 23-11-2019, 21:40:27
Стандартный метод explode не расчленяет блоки, у которых масштабные коэффициенты по X, Y, и Z отличны.
Откуда эта информация? Практика этого примера показывает, что как раз расчленяет. Игнорируются только вставки блоков. Какое в итоге возможно решение?
Название: Re: Некорректное разбиение блока
Отправлено: Александр Ривилис от 23-11-2019, 22:43:59
Стандартный метод explode не расчленяет блоки, у которых масштабные коэффициенты по X, Y, и Z отличны.
Откуда эта информация? Практика этого примера показывает, что как раз расчленяет. Игнорируются только вставки блоков. Какое в итоге возможно решение?
Попробуй:
Код - C++ [Выбрать]
  1. static void MyGroupExpl() {
  2.   Acad::ErrorStatus es;
  3.   ads_name en; ads_point p;
  4.   if (acedEntSel(L"\nВыберите вставку блока", en, p) == RTNORM)
  5.   {
  6.  
  7.     AcDbObjectId eid; acdbGetObjectId(eid, en);
  8.     AcDbObjectPointer<AcDbBlockReference> pBref(eid, AcDb::kForRead);
  9.     es = pBref->explodeToOwnerSpace();
  10.     if (es == Acad::eOk)
  11.     {
  12.       if (pBref->upgradeOpen() == Acad::eOk)
  13.         pBref->erase();
  14.  
  15.     } else {
  16.       acutPrintf(L"\npBref->explodeToOwnerSpace()=%s", acadErrorStatusText(es));
  17.     }
  18.   }
  19. }
Работает с NUS-блоками, которые не содержат 3D-тела.
Название: Re: Некорректное разбиение блока
Отправлено: Debalance от 24-11-2019, 12:21:37
es = pBref->explodeToOwnerSpace();
Я так понимаю, что этот метод добавляет объекты в базу. А мне бы этого не хотелось, т.к. полученные примитивы используются для временных вычислений, при этом сам взорванный блок должен сохраниться. Такое решение возможно?

Кстати в описании к методу написано:
Цитировать
The AcDbBlockReference must be in a database and must be uniformly scaled.
Подходит ли это для нашего случая?
Название: Re: Некорректное разбиение блока
Отправлено: Debalance от 24-11-2019, 13:24:44
Попробуй:
Но надо признать - код работает. Хотя это не совсем то, что нужно мне...

Возможен, конечно следующий подход, с использованием данного кода:
1. Копирование вставки блока в другую запись.
2. Расчленение.
3. Получение всех примитивов во временной записи.
4. Необходимые математические вычисления.
5. Удаление объектов и самой временной записи.
Но как-то всё это через ЗДЦ.
Название: Re: Некорректное разбиение блока
Отправлено: Александр Ривилис от 24-11-2019, 13:25:20
Кстати в описании к методу написано:
Цитировать

    The AcDbBlockReference must be in a database and must be uniformly scaled.

Подходит ли это для нашего случая?
На твоём блоке работает прекрасно.
Я так понимаю, что этот метод добавляет объекты в базу. А мне бы этого не хотелось, т.к. полученные примитивы используются для временных вычислений, при этом сам взорванный блок должен сохраниться. Такое решение возможно?
Ну если ты сам перепишешь метод explode(), чтобы он работал так как тебе нужно, то - да. Иначе пользуйся тем что есть. Потом можешь удалить добавленные в базу примитивы.
Название: Re: Некорректное разбиение блока
Отправлено: Debalance от 24-11-2019, 13:33:42
Потом можешь удалить добавленные в базу примитивы...
... и добавить клон первоначальной вставки.
Название: Re: Некорректное разбиение блока
Отправлено: Александр Ривилис от 24-11-2019, 13:34:34
... и добавить клон первоначальной вставки.
Зачем? Оригинал никуда не девается. Я его в своём коде удалил для наглядности.
Название: Re: Некорректное разбиение блока
Отправлено: Debalance от 24-11-2019, 13:35:51
Я его в своём коде удалил.
Ок, пропустил... :)
Название: Re: Некорректное разбиение блока
Отправлено: Александр Ривилис от 25-11-2019, 17:11:14
А вот тебе вариант (очень упрощенный, но с твоим блоком работает) расчленение блока без добавления в базу:

Код - C++ [Выбрать]
  1. static AcDbVoidPtrArray ExplodeNusBlockReference(AcDbBlockReference *pBref)
  2. {
  3.   AcDbVoidPtrArray ents;
  4.   Acad::ErrorStatus es = pBref->explode(ents);
  5.   if (es == Acad::eCannotScaleNonUniformly)
  6.   {
  7.     AcDbBlockTableRecordPointer pBtr(pBref->blockTableRecord(), AcDb::kForRead);
  8.     AcGeMatrix3d mat = pBref->blockTransform();
  9.     AcGeScale3d scale3d = pBref->scaleFactors();
  10.     AcDbBlockTableRecordIterator *pIter = NULL;   pBtr->newIterator(pIter);
  11.     if (pIter)
  12.     {
  13.       for (pIter->start(); !pIter->done(); pIter->step())
  14.       {
  15.         AcDbObjectId eid;
  16.         if (pIter->getEntityId(eid) == Acad::eOk && eid.objectClass() == AcDbBlockReference::desc())
  17.         {
  18.           AcDbObjectPointer<AcDbBlockReference> pBrefSub(eid, AcDb::kForRead);
  19.           AcDbBlockReference * pBrefSubClone = (AcDbBlockReference *)pBrefSub->clone();
  20.           // Переносим
  21.           AcGePoint3d p = pBrefSubClone->position();
  22.           pBrefSubClone->setPosition(p.transformBy(mat));
  23.           // Масштабируем
  24.           AcGeScale3d scale3dSub = pBrefSubClone->scaleFactors();
  25.           scale3dSub.postMultBy(scale3d);
  26.           pBrefSubClone->setScaleFactors(scale3dSub);
  27.           // Поворачиваем
  28.           pBrefSubClone->setRotation(pBrefSubClone->rotation() + pBref->rotation());
  29.           ents.append(pBrefSubClone);
  30.         }
  31.       }
  32.       delete pIter;
  33.     }
  34.   }
  35.   return ents;
  36. }

Для проверки кода:

Код - C++ [Выбрать]
  1. static void MyGroupExpl1() {
  2.   ads_name en; ads_point p;
  3.   if (acedEntSel(L"\nВыберите вставку блока: ", en, p) == RTNORM)
  4.   {
  5.     AcDbObjectId eid; acdbGetObjectId(eid, en);
  6.     AcDbObjectPointer<AcDbBlockReference> pBref(eid, AcDb::kForRead);
  7.     if (pBref.openStatus() == Acad::eWrongObjectType)
  8.       return;
  9.     AcDbVoidPtrArray ents = ExplodeNusBlockReference(pBref);
  10.     if (pBref->upgradeOpen() == Acad::eOk)
  11.       pBref->erase(); // Удаляем оригинал вставки блока
  12.     for (int i = 0; i < ents.length(); i++)
  13.     {
  14.       postToDwgAndClose((AcDbEntity *)(ents[i]));
  15.     }
  16.   }
  17. }
Название: Re: Некорректное разбиение блока
Отправлено: Debalance от 25-11-2019, 23:43:31
А вот тебе вариант (очень упрощенный, но с твоим блоком работает) расчленение блока без добавления в базу:
Спасибо. Идею понял. А какие возможны "подводные камни" на прочих блоках при использовании данного упрощённого варианта?
Название: Re: Некорректное разбиение блока
Отправлено: Александр Ривилис от 25-11-2019, 23:46:22
А какие возможны "подводные камни" на прочих блоках при использовании данного упрощённого варианта?
1. С 3Dsolid внутри блоков работать не будет, точнее будет их пропускать.
2. С масштабированием и перемещением вроде всё в порядке, а вот с вращением я не уверен на 100%.
3. Если у вложенного блока есть атрибуты, то они будут игнорироваться.
4. И т.д.
Название: Re: Некорректное разбиение блока
Отправлено: Debalance от 05-11-2020, 15:08:30
Успешно использовал метод explodeToOwnerSpace для последовательного разбиения родительского блока и вложенного в него блоков, пока не столкнулся с блоками, созданными на основе ассоциативных массивов. Именно вложенные ассоциативные массивы разбиваются некорректно - (перемещаются и поворачиваются в процессе разбивки). Как исправить данную ситуацию?
Название: Re: Некорректное разбиение блока
Отправлено: Александр Ривилис от 05-11-2020, 15:11:49
Как исправить данную ситуацию?
Ответ очевиден - или не пользоваться этим методом или лезть в начинку этих блоков и перемещать и поворачивать их как нужно.
Название: Re: Некорректное разбиение блока
Отправлено: Debalance от 05-11-2020, 15:18:04
... не пользоваться этим методом ...
Вы намекаете на написание собственного метода? Или есть готовое решение?
Название: Re: Некорректное разбиение блока
Отправлено: Александр Ривилис от 05-11-2020, 15:19:18
... не пользоваться этим методом ...
Вы намекаете на написание собственного метода? Или есть готовое решение?
Намекаю.
Название: Re: Некорректное разбиение блока
Отправлено: Debalance от 05-11-2020, 15:28:48
А в чём особенность этих ассоциативных массивов? Почему они капризничают?
Название: Re: Некорректное разбиение блока
Отправлено: Александр Ривилис от 05-11-2020, 15:37:27
А в чём особенность этих ассоциативных массивов? Почему они капризничают?
Это не они капризничают, а просто их какие-то особенности не учли при написании метода explodeToOwnerSpace.
Название: Re: Некорректное разбиение блока
Отправлено: Debalance от 05-11-2020, 20:39:09
Создал свой метод на основе deepCloneObjects + трансформации. Результат абсолютно идентичный работе explodeToOwnerSpace - все вложенные в блок массивы при таком расчленении разбегаются. Обычные вложенные блоки разбиваются корректно. Вопрос: возможно надо влезать куда-то в AcDbAssocArrayActionBody и брать оттуда какие-то трансформации? Оговорка: я оперирую массивом как AcDbBlockReference.
Название: Re: Некорректное разбиение блока
Отправлено: Александр Ривилис от 05-11-2020, 23:10:30
Debalance,
А это тебе не подходит: AcDbAssocArrayActionBody::explode() ?
Название: Re: Некорректное разбиение блока
Отправлено: Александр Ривилис от 05-11-2020, 23:21:21
Вопрос: возможно надо влезать куда-то в AcDbAssocArrayActionBody и брать оттуда какие-то трансформации?
AcDbAssocArrayItem::getTransform возвращают матрицы трансформации.
Название: Re: Некорректное разбиение блока
Отправлено: Debalance от 06-11-2020, 12:41:39
Тут какая-то забавная фигня получается. Короче проблему я, вроде, решил. Получается, что сам ассоциативный массив разбивается корректно. Некорректно разбивается материнский блок, содержащий данный массив. Причём некорректность разбиения выражается исключительно в смещении вставки блока ассоциативного массива (все остальные объекты внутри материнского блока разбиваются корректно). Собственно решение заключается в следующем: матрицу преобразования (полученную из материнского блока) надо инвертировать при трансформации ассоциативного массива. А для всех остальных объектов нужна исходная неинверсная матрица. С чем это связано - для меня загадка, но это факт.
Название: Re: Некорректное разбиение блока
Отправлено: Debalance от 06-11-2020, 12:58:11
Вот такой кусок код получается:

Код - C++ [Выбрать]
  1. //Исходный материнский блок
  2. AcDbBlockReference* pBref = AcDbBlockReference::cast(pRx);
  3. AcDbBlockTableRecordPointer pRecord2(pBref->blockTableRecord(), AcDb::kForRead);
  4. if (pRecord2.openStatus() == Acad::eOk)
  5. {
  6.         // Получаем матрицу
  7.         AcGeMatrix3d xform = pBref->blockTransform();
  8.         AcGePoint3d pt3dBlkOrigin = pRecord2->origin();
  9.         // Учитываем возможность ненулевой базовой точки блока (Block Definition)
  10.         if (pt3dBlkOrigin != AcGePoint3d::kOrigin)
  11.         {
  12.                 AcGeVector3d xformOrigin = xform.translation();
  13.                 AcGeVector3d vectorOrigin(pt3dBlkOrigin.x, pt3dBlkOrigin.y, pt3dBlkOrigin.x);
  14.                 vectorOrigin.transformBy(xform);
  15.                 xformOrigin -= vectorOrigin;
  16.                 xform.setTranslation(xformOrigin);
  17.         }
  18.  
  19.         // Получаем инверсную матрицу
  20.         AcGeMatrix3d xform2 = xform;
  21.         xform2.invert();
  22.  
  23.         // Считываем объекты из материнского блока
  24.         AcDbObjectIdArray  objList2;
  25.         AcDbBlockTableRecordIterator* TmpIter2 = NULL;
  26.         pRecord2->newIterator(TmpIter2);
  27.         if (TmpIter2)
  28.         {
  29.                 TmpIter2->start();
  30.                 while (!TmpIter2->done())
  31.                 {
  32.                         AcDbObjectId TmpId;
  33.                         if (TmpIter2->getEntityId(TmpId) == Acad::eOk)
  34.                         {
  35.                                 objList2.append(TmpId);
  36.                         }
  37.                         TmpIter2->step();
  38.                 }
  39.         }
  40.         pRecord2.close();
  41.  
  42.         // Выполняем клонирование
  43.         AcDbIdMapping idMap2;
  44.         CurDB->deepCloneObjects(objList2, InputBtrId, idMap2);
  45.  
  46.         // Делаем трансформации
  47.         AcDbIdMappingIter iter(idMap2);
  48.         for (iter.start(); !iter.done(); iter.next())
  49.         {
  50.                 AcDbIdPair idPair;
  51.                 iter.getMap(idPair);
  52.                 if (idPair.isCloned())
  53.                 {
  54.                         AcDbObjectPointer<AcDbEntity> pClone(idPair.value(), AcDb::kForRead);
  55.                         if (pClone.openStatus() == Acad::eOk)
  56.                         {
  57.                                 if (AcDbAssocArrayActionBody::isAssociativeArray(pClone))
  58.                                 {
  59.                                         // Если объект массив - применяем инверсную матрицу
  60.                                         AcDbAssocArrayActionBody* pArrayActionBody = NULL;
  61.                                         AcDbObjectId actionBodyId = AcDbAssocArrayActionBody::getControllingActionBody(pClone, NULL);
  62.                                         if ((acdbOpenAcDbObject((AcDbObject*&)pArrayActionBody, actionBodyId, AcDb::kForWrite)) == Acad::eOk)
  63.                                         {
  64.                                                 pArrayActionBody->transformBy(xform2);
  65.                                                 pArrayActionBody->close();
  66.                                         }
  67.                                 }
  68.                                 else
  69.                                 {
  70.                                         // Для всех остальных случаев применяем исходную матрицу
  71.                                         if (pClone->upgradeOpen() == Acad::eOk)
  72.                                         {
  73.                                                 pClone->transformBy(xform);
  74.                                         }
  75.                                 }
  76.                         }
  77.                 }
  78.         }
  79. }
Название: Re: Некорректное разбиение блока
Отправлено: Александр Ривилис от 06-11-2020, 13:10:25
Debalance,
Странно конечно, хотя возможно какая-то логика в этом есть.