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

ADN Club => ObjectARX => Тема начата: Gennadiy от 06-02-2017, 16:32:23

Название: SubEntity
Отправлено: Gennadiy от 06-02-2017, 16:32:23
Добрый день.
Столкнулся с такой проблемой при разработке CustomEntity:

Есть собственный примитив MyEntity, который представляет собой контейнер примитивов MySubEntity AcDbVoidPtrArray entArr;
Мне нужно при выделении SubEntity получить его ручки чтобы изменять MySubEntity в соответствии с переопределенным в нем subMoveGripPointsAt.

Получаю ручки так

Код - C++ [Выбрать]
  1. Acad::ErrorStatus MyEntity::subGetGripPointsAtSubentPath(const AcDbFullSubentPath & path, AcDbGripDataPtrArray & grips, const double curViewUnitSize, const int gripSize, const AcGeVector3d & curViewDir, const int bitflags) const
  2. {
  3.         assertReadEnabled();
  4.         grips.setLogicalLength(0);
  5.  
  6.         long ind = path.subentId().index();
  7.         ((MySubEntity*)entArr[ind - 1])->getGripPoints(grips, curViewUnitSize, gripSize, curViewDir, bitflags);
  8.         AcGeMatrix3d mat = getMatrix();
  9.  
  10.         for (int i = 0; i < grips.length(); i++)
  11.         {
  12.                 AcDbGripData* gr = grips.at(i);
  13.                 gr->setGripPoint(AcGePoint3d(gr->gripPoint()).transformBy(mat));
  14.         }
  15.  
  16.         return (Acad::eOk);
  17. }

Метод работает нормально, ручки появляются.

Проблема в методе subMoveGripPointsAtSubentPaths, где gripAppData при перемещении одной ручки должен содержать только один элемент, а у меня там
появляется коллекция сразу всех ручек MySubEntity, и соответственно я не могу определить какая ручка перемещается.

Код - C++ [Выбрать]
  1. Acad::ErrorStatus MyEntity::subMoveGripPointsAtSubentPaths(const AcDbFullSubentPathArray & paths, const AcDbVoidPtrArray & gripAppData, const AcGeVector3d & offset, const int bitflags)
  2. {
  3.         assertWriteEnabled();
  4.  
  5.         if (1 > paths.length()) return Acad::eOk;
  6.  
  7.         long ind = paths.at(0).subentId().index();
  8.         ((MySubEntity*)entArr[ind - 1])->moveGripPointsAt(gripAppData, offset, bitflags);
  9.  
  10.         return (Acad::eOk);
  11.  
  12. }

Похожих примеров в сети я нигде не нашел, поэтому прошу помощи.
Название: Re: SubEntity
Отправлено: Александр Ривилис от 06-02-2017, 17:37:28
и соответственно я не могу определить какая ручка перемещается.
Можешь. Если переопределишь метод AcDbGripData::setHotGripFunc(GripOperationPtr pFunc), то сможешь выяснить какая именно ручка выбрана.
Название: Re: SubEntity
Отправлено: Александр Ривилис от 06-02-2017, 17:53:20
Кстати, можешь почитать еще и эту тему: http://adn-cis.org/forum/index.php?topic=2660.0
Название: Re: SubEntity
Отправлено: Gennadiy от 07-02-2017, 10:18:49
Можешь. Если переопределишь метод AcDbGripData::setHotGripFunc(GripOperationPtr pFunc), то сможешь выяснить какая именно ручка выбрана.

Не понял каким образом это сделать. Может быть я не верно сформулировал вопрос.
Индексы ручек MySubEntity у меня заданы, различить какая к чему относится я могу,
проблема в том что когда я перемещаю определенную ручку MySubEntity, в методе MyEntity::subMoveGripPointsAtSubentPaths(...
в коллекции AcDbVoidPtrArray & gripAppData находятся сразу все ручки, а не одна выбранная, как это происходит в методе основного примитива MyEntity::subMoveGripPointsAt(...

Название: Re: SubEntity
Отправлено: Gennadiy от 07-02-2017, 12:27:12
Пробовал добавить в метод MyEntity::subGetGripPointsAtSubentPath
Код - C++ [Выбрать]
  1. pGripData->setHotGripFunc(HotEditModGripfunc);

Функция HotEditModGripfunc срабатывает только в момент выбора SubEntity (через Ctrl + клик мышкой)
Когда выбираю конкретную ручку для перемещения SubEntity функция не срабатывает.
Название: Re: SubEntity
Отправлено: Александр Ривилис от 07-02-2017, 12:32:33
Мне кажется что ты перемудрил с subGetGripPointsAtSubentPath/subMoveGripPointsAtSubentPaths. Мне вообще непонятно зачем они тебе понадобились.
Название: Re: SubEntity
Отправлено: Gennadiy от 07-02-2017, 12:41:30
Мне кажется что ты перемудрил с subGetGripPointsAtSubentPath/subMoveGripPointsAtSubentPaths. Мне вообще непонятно зачем они тебе понадобились.

Идея такая: есть контейнер объектов (типа BlockReference), в него входит заранее не определенное кол-во объектов (Custom). Мне нужно чтобы я мог выбирать из контейнера нужный объект
(через Ctrl + клик мышкой) получать его ручки и редактировать ими этот объект. При этом основной объект имеет свою графику, которая тоже меняется при перемещении вложенного объекта.
Или, проще говоря, это должно выглядеть как если бы у BlockReference можно было бы выбирать ручки входящих в него примитивов, и редактировать их отдельно.

Главное, что выбрать вложенные примитивы у меня получается, и ручки у них появляются как положено, а вот редактировать отдельную ручку не получается, и Google не помогает в этом.
Название: Re: SubEntity
Отправлено: Александр Ривилис от 07-02-2017, 12:51:14
Я думаю, что и в этом случае можно было бы ограничится subGetGripPoints/subMoveGripPoints.
Впрочем рекомендую посмотреть пример samples\entity\SubEntity из ObjectARX SDK 2009.
Название: Re: SubEntity
Отправлено: Александр Ривилис от 07-02-2017, 12:55:46
Пробовал добавить в метод MyEntity::subGetGripPointsAtSubentPath
Код - C++ [Выбрать]

    pGripData->setHotGripFunc(HotEditModGripfunc);


Функция HotEditModGripfunc срабатывает только в момент выбора SubEntity (через Ctrl + клик мышкой)
Когда выбираю конкретную ручку для перемещения SubEntity функция не срабатывает.
А почему ты её добавлял в subGetGripPointsAtSubentPath, а не в subGetGripPoints ?
Название: Re: SubEntity
Отправлено: Gennadiy от 07-02-2017, 13:33:33
Я думаю, что и в этом случае можно было бы ограничится subGetGripPoints/subMoveGripPoints.
А как тогда получить ручки вложенного примитива? Ведь subGetGripPoints получает ручки только основного примитива.

Впрочем рекомендую посмотреть пример samples\entity\SubEntity из ObjectARX SDK 2009
Этот пример я и брал за основу, там тоже используется getGripPointsAtSubentPath/moveGripPointsAtSubentPaths
Только у меня VS2012 и VS2015, под них мне не удалось собрать проект чтобы пощупать как работает при отладке.

А почему ты её добавлял в subGetGripPointsAtSubentPath, а не в subGetGripPoints ?
Именно потому что subGetGripPoints дает только ручки основного примитива. (Пробовал pGripData->setHotGripFunc(HotEditModGripfunc) вставить в метод subGetGripPoints вложенного
MySubEntity - тоже не срабатывает)
Название: Re: SubEntity
Отправлено: Александр Ривилис от 07-02-2017, 14:11:48
Этот пример я и брал за основу, там тоже используется getGripPointsAtSubentPath/moveGripPointsAtSubentPaths
Только ты не обратил внимание, что используется совсем не так, как у тебя. Разберись как следует с этим примером.
Название: Re: SubEntity
Отправлено: Александр Ривилис от 07-02-2017, 14:19:51
Кстати, эта тема тоже может быть полезной: http://adn-cis.org/forum/index.php?topic=2660.0
Название: Re: SubEntity
Отправлено: Александр Ривилис от 07-02-2017, 14:27:12
А как тогда получить ручки вложенного примитива? Ведь subGetGripPoints получает ручки только основного примитива.
Может получать и ручки вложенного примитива. Тем более, что это твой же примитив. при вызове subGetGripPoints возвращаешь ручки основного примитива и проходишься по всем подпримитивам и добавляешь их ручки к общему массиву.
Название: Re: SubEntity
Отправлено: Gennadiy от 07-02-2017, 14:39:50
Может получать и ручки вложенного примитива. Тем более, что это твой же примитив. при вызове subGetGripPoints возвращаешь ручки основного примитива и проходишься по всем подпримитивам и добавляешь их ручки к общему массиву.

Тогда при выборе объекта у меня будет каша из всех ручек (возможно с одинаковыми индексами). Это очень усложнит работу.

Только ты не обратил внимание, что используется совсем не так, как у тебя

Буду пока разбираться с этим примером. (Хорошо если смогу скомпилировать под VS2015)
Название: Re: SubEntity
Отправлено: Александр Ривилис от 07-02-2017, 15:27:11
Я подготовил этот пример, адаптированный под AutoCAD 2017 и VS 2015. Проверил что он нормально загружается и вроде даже работает в AutoCAD 2017. Пробуй.
Название: Re: SubEntity
Отправлено: Gennadiy от 07-02-2017, 15:46:54
Не удается открыть проект SliderCrankUi (не обнаружен PathsToSdk.props)
Название: Re: SubEntity
Отправлено: Александр Ривилис от 07-02-2017, 16:14:51
Исправил: http://adn-cis.org/assets/gallery/AutoCAD/SubEntity2017.zip
Название: Re: SubEntity
Отправлено: Александр Ривилис от 07-02-2017, 18:11:42
Не удается открыть проект SliderCrankUi (не обнаружен PathsToSdk.props)
Сейчас нормально?
Название: Re: SubEntity
Отправлено: Gennadiy от 07-02-2017, 18:15:21
Да, сейчас нормально компилируется. Завтра внимательней посмотрю в чем отличия от моего и отпишусь.
Название: Re: SubEntity
Отправлено: Gennadiy от 08-02-2017, 09:02:19
Изучил работу приведенного выше примера. В нем такая же проблема, как и у меня.
В описании проекта readme.txt сказано:
Цитировать
2) Type the command "AddSliderCrank":
        ...
   b) On selecting the sub-entity, the sub-entity grips will appear on the screen. For example, if the connecting rod is selected, two grip points along the rod will be enabled. Using these grip points, the position of the         connecting rod can be modified at both ends.
(При выборе подобъекта, ручки подобъекта появятся на экране. Например, если соединительный стержень будет выбран, две ручки вдоль стержня будут включены. Используя эти ручки, позиция соединительного стержня может быть изменена в обоих концах.)

На самом деле какая бы ручка соединительного стержня не была выбрана, перемещается только один конец стержня. Это происходит потому, что (как и у меня)
при перемещении ручки в методе subMoveGripPointsAtSubentPaths в коллекции AcDbVoidPtrArray &gripAppData находятся сразу все ручки подобъекта (в данном случае у LINK две ручки)
и в коде
Код - C++ [Выбрать]
  1.  int *idx = (int*)gripAppData.at(0);
выбирается всегда первая (т.к. по идее в gripAppData и должна быть всего одна выбранная ручка)

Похоже что это баг AutoCAD 2017. (Может быть в примере под AutoCAD 2009 работало все нормально?)
В других версиях пока нет возможности проверить.
Название: Re: SubEntity
Отправлено: Александр Ривилис от 08-02-2017, 14:07:16
Я проверил в AutoCAD 2008 - там поведение другое:



И в gripAppData только один элемент.
Название: Re: SubEntity
Отправлено: Александр Ривилис от 08-02-2017, 16:20:06
Придумал для 2017 "костыль":
Название: Re: SubEntity
Отправлено: Gennadiy от 08-02-2017, 18:02:35
Скомпилировал упрощенный вариант примера для AutoCAD 2014, там тоже работает как в AutoCAD 2017.
Видимо поэтому этот пример и перестали добавлять в ObjectARX SDK после версии 2009.

Придумал для 2017 "костыль":

А в чем секрет, если не секрет?
Название: Re: SubEntity
Отправлено: Александр Ривилис от 08-02-2017, 18:10:17
А в чем секрет, если не секрет?
Особенность при работе этого кода в том, что при выборе subentity (т.е. через CTRL+клик) его ручки становятся все выделенными (красными). Как от этого избавиться я не знаю и по логике такого быть не должно (и в AutoCAD 2008 такого нет). Но если кликнуть по одной из ручек c Shift'ом (когда она становится синенькой), то в gripAppData только одна ручка. Попробуй так. Я еще и код слегка модифицировал:

Извините, вам запрещён просмотр содержимого спойлеров.
Название: Re: SubEntity
Отправлено: Gennadiy от 08-02-2017, 18:25:37
Да, пожалуй это единственно приемлемый выход для меня. Спасибо за подсказку, а то я уже загрустил.
Название: Re: SubEntity
Отправлено: Александр Ривилис от 08-02-2017, 18:51:57
Еще немного исправил код:
Извините, вам запрещён просмотр содержимого спойлеров.

Теперь работает не только в визуальном стиле 2D-каркас:

Название: Re: SubEntity
Отправлено: Gennadiy от 09-02-2017, 14:30:04
Заметил ещё одну важную деталь.
Для конкретного случая в примере все верно, но если предположить, что
ручек в подобъекте будет более двух (скажем N), то в методе subMoveGripPointsAtSubentPaths  в коллекции gripAppData будет не одна, а N - 1 ручка
и, соответственно, код будет прерываться на проверке
Код - C++ [Выбрать]
  1.         int n = gripAppData.length();
  2.         if (n > 1) return Acad::eOk;

Лучше эту проверку убрать, а в методе subGetGripPointsAtSubentPath вернуть как было в первом варианте
Код - C++ [Выбрать]
  1. Acad::ErrorStatus AsdkSliderCrank::subGetGripPointsAtSubentPath(const AcDbFullSubentPath & path, AcDbGripDataPtrArray & grips, const double  curViewUnitSize, const int  gripSize, const AcGeVector3d & curViewDir, const int  bitflags) const
  2. {
  3.     assertReadEnabled();
  4.     idxGrips = -1;
  5.     ...
  6. }

Вообще, интересно было бы узнать мнение разработчиков, почему при выборе subentity его ручки становятся все выделенными (красными).
Может быть у них для этого предусмотрено как сделать их не выделенными, или это всё-таки БАГ?
Название: Re: SubEntity
Отправлено: Александр Ривилис от 09-02-2017, 14:57:19
Заметил ещё одну важную деталь.
Для конкретного случая в примере все верно, но если предположить, что
ручек в подобъекте будет более двух (скажем N), то в методе subMoveGripPointsAtSubentPaths  в коллекции gripAppData будет не одна, а N - 1 ручка
и, соответственно, код будет прерываться на проверке
В любом случае должна быть выбрана только одна ручка и в этом случае в gripAppData будет одна ручка. Другое дело, что я не пробовал на > 2 ручках как их следует выбирать.

Лучше эту проверку убрать, а в методе subGetGripPointsAtSubentPath вернуть как было в первом варианте
Этот вариант не работает на визуальных стилях кроме 2D-каркас, так как в нём этот метод вызывается однократно, а в остальных дважды.

Вообще, интересно было бы узнать мнение разработчиков, почему при выборе subentity его ручки становятся все выделенными (красными).
Может быть у них для этого предусмотрено как сделать их не выделенными, или это всё-таки БАГ?
Отправлю. Думаю, что будут разбираться они с этим долго, так как такое использование крайне редкое.
Название: Re: SubEntity
Отправлено: Gennadiy от 09-02-2017, 15:23:07
Однако, если взять, к примеру, полилинию, то при выделении её сегмента как SubEntity (кликом мыши + Сtrl) ручки сегмента синие (не активные) и все работает как надо.
Значит решение где-то есть! Какой механизм выбора ручек используется там?
Название: Re: SubEntity
Отправлено: Александр Ривилис от 09-02-2017, 15:53:21
Значит решение где-то есть! Какой механизм выбора ручек используется там?
Думаю, что там используется совсем другой механизм. Ведь там нет никакого реального SubEntity.
Название: Re: SubEntity
Отправлено: Gennadiy от 09-02-2017, 16:07:59
Думаю, что там используется совсем другой механизм.
Интересно было бы знать какой?
Ведь там нет никакого реального SubEntity.
Так и в рассматриваемом примере, тоже вроде бы реальных SubEntity нет, они вычисляются и рисуются в subWorldDraw?
В любом случае, ведь получают ручки через Сtrl + клик, а значит срабатывает subGetGripPointsAtSubentPath, затем что-то делает их неактивными (синими)
и дальше можно перемещать по одной.

Название: Re: SubEntity
Отправлено: Александр Ривилис от 14-02-2017, 23:51:12
Наконец-то смог собраться с духом и отправить запрос в ADN DevHelp. По результатам отпишусь.
Название: Re: SubEntity
Отправлено: Александр Ривилис от 23-02-2017, 14:49:15
Первый блин со стороны ADN DevHelp оказался комом. :) Они предложили вариант с установкой setGripOpStatFunc(GripOpStatus), а в GripOpStatus запоминать какая ручка начинает "работать" при kGripStart:
Код - C++ [Выбрать]
  1. //change 1
  2. void GripOpStatus(AcDbGripData* pThis, const AcDbObjectId& entId, AcDbGripOperations::GripStatus stat)
  3. {
  4.   if (stat == AcDbGripOperations::GripStatus::kGripStart)
  5.     idxGrips = *(int *)pThis->appData();
  6. }
  7.  
Увы, но это не помогло, так как при выборе любой из ручек этот метод вызывается дважды со значениями обеих ручек. Причем порядок вызова не зависит от того, какая ручка была выбрана.
Это хорошо видно на этом видео:



Так что будем ждать другие варианты решения.
Название: Re: SubEntity
Отправлено: Александр Ривилис от 07-03-2017, 14:41:39
А вот эта подсказка похоже работает. Это конечно не идеальный вариант, но...
Код - C++ [Выбрать]
  1. void GripOpStatus(AcDbGripData* pThis, const AcDbObjectId& entId, AcDbGripOperations::GripStatus stat)
  2. {
  3.   if (stat == AcDbGripOperations::GripStatus::kGripStart)
  4.   {
  5.     //get the user click point through
  6.     struct resbuf ptRb;
  7.     acedGetVar(ACRX_T("LASTPOINT"), &ptRb);
  8.     AcGePoint3d pt = pThis->gripPoint();
  9.     AcGePoint3d pt1(ptRb.resval.rpoint[0], ptRb.resval.rpoint[1], ptRb.resval.rpoint[2]);
  10.     acdbUcs2Wcs(ptRb.resval.rpoint, asDblArray(pt1), false);
  11.  
  12.     if (pt.isEqualTo(pt1) == true)
  13.     {
  14.       idxGrips = *(int *)pThis->appData();
  15.     }
  16.   }
  17. }
Смысл в получении последней указанной точки (системная переменная LASTPOINT - она хранится в ПСК (UCS)) и сравнении её с точкой ручки. В моих тестах всё работает адекватно. Единственное неудобство - обе ручки остаются выделенными.
Название: Re: SubEntity
Отправлено: Gennadiy от 07-03-2017, 15:17:36
У меня в проекте тоже всё работает адекватно.
За не имением лучшего этот вариант можно считать решением задачи.
Название: Re: SubEntity
Отправлено: Александр Ривилис от 07-03-2017, 15:21:42
За не имением лучшего этот вариант можно считать решением задачи.
Не-не-не! Пока еще своё слово не сказала Engineering Team - мы ждём от неё информацию. Вот когда они скажут, что это единственный способ, а появление выбранных ручек при Ctrl+click - это баг, для которого нет решения - вот тогда можно будет остановиться.