acedEntSel и варианты окончания команды

Автор Тема: acedEntSel и варианты окончания команды  (Прочитано 4041 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Тема содержит сообщение с Решением. Нажмите здесь чтобы посмотреть его.

Оффлайн Николай ГорловАвтор темы

  • ADN
  • *
  • Сообщений: 238
  • Карма: 34
День добрый. Пользователи натыкали носом в одну преинтереснейшую ситуацию. Если коротко, то есть команда, которая позволяет выбрав штриховку, создать автоматом под ней заливку (штриховку SOLID) требуемого цвета.
Итак, начальные данные. На экране есть какая-либо штриховка. Допустим это будет ассоциативная штриховка ANGLE (имя не важно, важно то, что она ассоциативная).
Вот код для тестирования, который воспроизводит глючок:
Код - C++ [Выбрать]
  1. ads_name ent;
  2. ads_point pt;
  3. AcDbObjectId objId;
  4. AcDbObjectIdArray  objList;
  5. AcDbIdMapping idMap;
  6.  
  7. uint16_t solidcolor = 5;
  8.  
  9. while (TRUE)
  10. {
  11.         if (acedEntSel(_T("\nВыбери штриховку: "), ent, pt) != RTNORM)
  12.                 return;
  13.  
  14.         acdbGetObjectId(objId, ent);
  15.         AcDbObjectPointer<AcDbHatch> pHatch(objId, AcDb::kForRead);
  16.         if (pHatch.openStatus() != Acad::eOk)
  17.                 continue;
  18.  
  19.         objList.append(objId);// добавляем ID штриховки для deepCloneObjects
  20.         acdbCurDwg()->deepCloneObjects(objList, acdbCurDwg()->currentSpaceId(), idMap);
  21.         objList.removeLast();// чистим перышки
  22.  
  23.         pHatch->upgradeOpen();
  24.         pHatch->setPattern(AcDbHatch::kPreDefined, _T("SOLID"));
  25.         pHatch->setColorIndex(solidcolor);
  26. }

Что делает код думаю объяснять не нужно :)
А сам глючок заключается в следующем:
1. Запускаем команду, выбираем штриховку, нажимаем ESC. Получаем две ассоциативные штриховки
2. Запускаем команду, выбираем штриховку, нажимаем Enter. Получаем новую штриховку НЕ ассоциативной

Вот как-то так :) Если цикл убрать и оставить только один пробег, то получается новая штриховка НЕ ассоциативная, т.е. только выход по ESC из команды оставляет новую штриховку ассоциативной, а хотелось бы, чтоб ассоциативность сохранялась всегда (если она была изначально).

Да, я тестировал в 2016x64, пользователи в 2010x86. Думаю та же картина и в 2019, но не проверял.

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Николай Горлов,
Ты кого угодно можешь запутать. :) Похоже ни acedEntSel, ни цикл тут не причем. В любом случае это побочный эффект, связанный с тем, что часть обработки штриховки выполняется после завершения команды.
После дублирования штриховки и изменения её патерна нужно указать всем примитивам границы, что они ассоциативны со штриховкой:
http://adn-cis.org/assocziativnost-acdbhatch.html
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Николай Горлов,
Попробуй вот такой код:
Код - C++ [Выбрать]
  1. static void RivilisCloneHatch() {
  2.   ads_name ent;
  3.   ads_point pt;
  4.   AcDbObjectId objId;
  5.   AcDbObjectIdArray  objList;
  6.   AcDbIdMapping idMap;
  7.  
  8.   uint16_t solidcolor = 5;
  9.  
  10.   while (TRUE)
  11.   {
  12.     if (acedEntSel(ACRX_T("\nВыбери штриховку: "), ent, pt) != RTNORM)
  13.       return;
  14.  
  15.     acdbGetObjectId(objId, ent);
  16.     AcDbObjectPointer<AcDbHatch> pHatch(objId, AcDb::kForRead);
  17.     if (pHatch.openStatus() != Acad::eOk)
  18.       continue;
  19.     bool bAssoc = pHatch->associative();
  20.     AcDbObjectIdArray idsAssoc;
  21.     if (bAssoc)  pHatch->getAssocObjIds(idsAssoc);
  22.  
  23.     objList.append(objId);
  24.     // Добавляем границы
  25.     if (bAssoc)
  26.       for (int i = 0; i < idsAssoc.length(); i++)
  27.         objList.append(idsAssoc[i]);
  28.     acdbCurDwg()->deepCloneObjects(objList, acdbCurDwg()->currentSpaceId(), idMap, false);
  29.     objList.setLogicalLength(0);
  30.     pHatch->close();
  31.  
  32.     AcDbIdPair pair; pair.setKey(objId);
  33.     idMap.compute(pair);
  34.  
  35.     AcDbObjectId newHatchId(pair.value());
  36.     AcDbObjectPointer<AcDbHatch> pHatchNew(newHatchId, AcDb::kForWrite);
  37.     if (pHatchNew.openStatus() != Acad::eOk)
  38.       continue;
  39.     if (bAssoc)
  40.     {
  41.       AcDbObjectIdArray idsAssoc;
  42.       pHatchNew->getAssocObjIds(idsAssoc);
  43.       for (int i = 0; i < idsAssoc.length(); i++)
  44.       {
  45.         AcDbEntityPointer pBoundary(idsAssoc[i], AcDb::kForWrite);
  46.         if (pBoundary.openStatus() == Acad::eOk)
  47.         {
  48.           pBoundary->addPersistentReactor(newHatchId);
  49.         }
  50.       }
  51.     }
  52.     pHatchNew->setPattern(AcDbHatch::kPreDefined, ACRX_T("SOLID"));
  53.     pHatchNew->setColorIndex(solidcolor);
  54.   }
  55. }
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Николай ГорловАвтор темы

  • ADN
  • *
  • Сообщений: 238
  • Карма: 34
код попробовал. не то :), т.к. у каждой штриховки свой контур получается, но то лечится без каких-то особых заморочек. вопрос не в этом.

по поводу темы вопроса... эт да, погорячился. вечерком посидел дома, подумал... сейчас бы я назвал её слегка иначе, но можно было б получить взрыв мозга, переваривая настоящую тему вопроса :)

теперь по сути. скопировать объект внутри чертежа сам на себя можно несколькими способами:
- ent->clone(), а потом добавить в бд
- ent->copyFrom(), посути то же самое что и clone()
- ent->deepClone(), который не рекомендуется самим автодеском ибо даже они не знают, что получится на выходе :)
- AcDbDatabase::deepCloneObjects(), который гарантирует сохранение всех жестких и мягких :) указателей

вот собственно из соображений лени и простоты кода, кто-то у нас выбрал лет 20 назад вариант с deepCloneObjects.

по поводу сохранения штриховки ассоциативной без всяких заморочек нужно просто выйти из нашей команды по ESC (из цикла).
и вот это как-раз и наталкивает на невеселые мысли. в чем собственно говоря отличие выхода из цикла по ECS или по Enter, если вцелом вся команда завершается одинаково?

я не поленился и нарисовал два контура со штриховками (каждая штриховка ассоциативна со своим контуром). провел тот же эксперимент. в итоге, если после создания двух новых штриховок нажать ECS, штриховки ассоциативные, если Enter - нет.
ну а теперь по поводу невеселых мыслей. такое чувство, что автокад сам создает транзакцию перед началом команды, и нажатие на ESC внутри команды не дает этой самой "внешней" транзакции коробить созданные объекты.

и вот мы подобрались к вопросу :). это тянет на глюк или я как-то не так понимаю механизм работы функции AcDbDatabase::deepCloneObjects()?


Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
это тянет на глюк или я как-то не так понимаю механизм работы функции AcDbDatabase::deepCloneObjects()?
AcDbDatabase::deepCloneObjects() в таком виде неприменимо для ассоциативной штриховки. Для того, чтобы штриховка стала ассоциативной одного копирования при помощи deepCloneObjects недостаточно, т.к. нужна связь граница->штриховка (связь штриховка->граница будет автоматически при копировании). Т.е. граница должна узнать о появлении второй штриховки, с которой она должна ассоциироваться. Для этой цели в коде pBoundary->addPersistentReactor(newHatchId); Но (и вот этом похоже баг) AutoCAD в нормальном состоянии считает, что контур может ассоциироваться только с одной штриховкой, и в противном случае ассоциацию убирает. В случае ESC похоже эта проверка не происходит. Т.е. баг есть в любом случае, но случай когда работает с ESC скорее фича, чем баг. Но если я прав и контур может ассоциироваться только с одной штриховкой (т.е. так задумано), то возможны какие побочные эффекты. Я бы рекомендовал не пользоваться такой фичей и дублировать контура.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
ну а теперь по поводу невеселых мыслей. такое чувство, что автокад сам создает транзакцию перед началом команды, и нажатие на ESC внутри команды не дает этой самой "внешней" транзакции коробить созданные объекты.
Там работает реактор и видимо после ESC он не вызывается.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Отмечено как Решение Николай Горлов 14-02-2019, 16:41:09

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Николай Горлов,
Тот случай, когда метод с clone лучше, хотя и не столь лаконичный:
Код - C++ [Выбрать]
  1. static void RivilisCloneHatch1() {
  2.   ads_name ent;
  3.   ads_point pt;
  4.   AcDbObjectId objId;
  5.   AcDbObjectIdArray  objList;
  6.   AcDbIdMapping idMap;
  7.  
  8.   uint16_t solidcolor = 5;
  9.   AcDbObjectIdArray idsAssoc;
  10.  
  11.   while (TRUE)
  12.   {
  13.     if (acedEntSel(ACRX_T("\nВыбери штриховку: "), ent, pt) != RTNORM)
  14.       return;
  15.     acdbGetObjectId(objId, ent);
  16.     AcDbObjectPointer<AcDbHatch> pHatch(objId, AcDb::kForRead);
  17.     if (pHatch.openStatus() != Acad::eOk)
  18.       continue;
  19.  
  20.     AcDbHatch *pHatchNew = AcDbHatch::cast(pHatch->clone());
  21.  
  22.     AcDbObjectId newHatchId = postToDwgWithId(pHatchNew);
  23.  
  24.     if (pHatchNew->associative())
  25.     {
  26.       AcDbObjectIdArray idsAssoc;
  27.       pHatchNew->getAssocObjIds(idsAssoc);
  28.       for (int i = 0; i < idsAssoc.length(); i++)
  29.       {
  30.         AcDbEntityPointer pBoundary(idsAssoc[i], AcDb::kForWrite);
  31.         if (pBoundary.openStatus() == Acad::eOk)
  32.         {
  33.           if (!pBoundary->hasPersistentReactor(newHatchId))
  34.             pBoundary->addPersistentReactor(newHatchId);
  35.         }
  36.       }
  37.     }
  38.     pHatchNew->setPattern(AcDbHatch::kPreDefined, ACRX_T("SOLID"));
  39.     pHatchNew->setColorIndex(solidcolor);
  40.     pHatchNew->close();
  41.   }
  42. }
Думаю, что код метода postToDwgWithId тебе не нужен.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Николай ГорловАвтор темы

  • ADN
  • *
  • Сообщений: 238
  • Карма: 34
Тот случай, когда метод с clone лучше, хотя и не столь лаконичный:
та он не просто лучше - он единственный (ну, если не создавать объект вручную по характеристикам исходного :) ). deepCloneObjects творит что-то интересное со штриховкой.
все попытки пристыковать новую штриховку к контуру заканчиваются ничем
 
Код - C++ [Выбрать]
  1. if (pHatchNew->associative())
  2. {
  3.         for (int i = 0; i < idsContour.length(); i++)
  4.         {
  5.                 AcDbObjectPointer<AcDbEntity> pContour(idsContour.at(i), AcDb::kForWrite);
  6.                 if (pContour.openStatus() == Acad::eOk)
  7.                 {
  8.                         if (!pContour->hasPersistentReactor(newHatchId))
  9.                                 acedAlert(_T("изначально контур и новая штриховка не связаны"));
  10.                         else
  11.                                 acedAlert(_T("deepCloneObjects автоматом привязал контур к штриховке"));
  12.  
  13.  
  14.                         pContour->addPersistentReactor(newHatchId);
  15.  
  16.                         if (!pContour->hasPersistentReactor(newHatchId))
  17.                                 acedAlert(_T("косяк однако"));
  18.                         else
  19.                                 acedAlert(_T("новая штриховка успешно привязана к контуру"));
  20.                 }
  21.         }
  22. }
  23.  
выдает сообщение "изначально контур и новая штриховка не связаны", потом "новая штриховка успешно привязана к контуру"
нажимаю Enter и в ArxDbg в реакторах контура висит только одна штриховка. но если нажать ESC - две штриховки :)
у clone() таких проблем не наблюдается, всё работает как надо. так что пересмотрю ка я на днях весь код на предмет deepCloneObjects :):):) и начну потихоньку избавляться от него.

в очередной раз спасибо за помощь :)