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

ADN Club => ObjectARX => Тема начата: Paravozzz от 01-06-2018, 13:12:38

Название: Аналог команды _boundary.
Отправлено: Paravozzz от 01-06-2018, 13:12:38
Добрый день!
В AutoCad 2016 есть команда  _boundary. Есть ли методы в objectarx 2016, которые реализуют аналогичное создание контура при имеющемся наборе объектов и указании внутренней точки. Я хочу написать свой метод на с++, который бы позволял строить замкнутые контуры, но при этом не показывая никаких диалоговых окон как команда _boundary.
Название: Re: Аналог команды _boundary.
Отправлено: Николай Горлов от 01-06-2018, 14:57:57
для С# есть, для С++ увы ничего нет. но кто мешает тихонько запустить через acedCommand автокадовскую команду, передав ей все требуемые параметры?
например так:
Код - C++ [Выбрать]
  1. //-> отключаем вывод в окне левых акадовских строк              
  2.                 struct resbuf cmdechoVar;
  3.                 cmdechoVar.restype = RTSHORT;
  4.                 cmdechoVar.resval.rint = 0;
  5.                 acedSetVar(_T("cmdecho"), &cmdechoVar);
  6. //<-
  7.  
  8.                 ads_name mas; //если работаем по варианту 2, то пора бы заполнить mas данными и не забыть почиститься в конце функции
  9.                 AcGePoint3d p;
  10.                 AcGePoint3dArray points;
  11.                 ads_name plBoundary, plBoundaryPreviousStep;
  12.                 if ( acdbEntLast(plBoundaryPreviousStep) != RTNORM )
  13.                 {
  14.                         acutPrintf(_T("\nЧертеж пустой"));
  15.                         return;
  16.                 }
  17.                 AcDbObjectId id;
  18.                 AcDbEntity * pEnt;
  19.                 while(acedGetPoint(0,_T("\nВнутренняя точка: "),asDblArray(p)) == RTNORM)
  20.                 {
  21.                         points.append(p);
  22.                 }
  23.                 if(points.length() > 0)
  24.                 {
  25.                         for(int i= 0;i<points.length();i++)
  26.                         {
  27.                                 /*
  28.                                         предпочтительней второй вариант, т.к. первый постоянно выбирает видимые объекты экрана,
  29.                                         и если точек больше одной, то:
  30.                                                 - создаваемые контура будут включаться в последующие наборы, что может привести к неправильной работе команды
  31.                                                 - постоянный выбор всех видимых объектов тормозит комп.
  32.                                         так что если надо включить все видимые объекты, то лучше их сразу собрать в mas и потом просто подствалять в команду.
  33.                                 */
  34.                                 acedCommandS(
  35.                                         RTSTR, _T("_.-boundary"),
  36.                                         RTSTR, _T("_a"), // дополнительные данные
  37.                                         RTSTR, _T("_o"), // тип объекта для контура
  38.                                         RTSTR, _T("_p"), // создаваемый контур будет полилинией
  39.                                         RTSTR, _T("_i"), // нужно ли искать островки
  40.                                         RTSTR, _T("_y"), // нужно, либо _T("_n") - не нужно
  41.                                         RTSTR, _T("_b"), // объекты которые будут участвовать в формировании контуров
  42.                                         /*или 1*/RTSTR, _T("_e"), // это будут все видимые на экране объекты, либо вместо этого варианта выбрать /*2*/
  43.                                         /*или 2
  44.                                         RTSTR, _T("_n"), // новый набор
  45.                                         RTPICKS, mas, // указываем ads_name объектов, которые будут участвовать в команде
  46.                                         RTSTR, _T(""),// подтверждение
  47.                                         */
  48.                                         RTSTR,_T(""), // возвращаемся к указанию точки
  49.                                         RTPOINT, asDblArray(points.at(i)), // наша точка, указанная пользователем
  50.                                         RTSTR,_T(""), // подтверждаем ввод точки
  51.                                         RTNONE // конец команды
  52.                                         );
  53.  
  54.                                 acdbEntLast(plBoundary);
  55.                                 if (plBoundaryPreviousStep[0] == plBoundary[0] && plBoundaryPreviousStep[1] == plBoundary[1])
  56.                                 {
  57.                                         acutPrintf(_T("  %d-я точка поганая\n"),i+1);
  58.                                         continue;
  59.                                 }
  60.                                 // перекинули последний добавленный в акад объект
  61.                                 plBoundaryPreviousStep[0] = plBoundary[0];
  62.                                 plBoundaryPreviousStep[1] = plBoundary[1];
  63.  
  64.                                 ::acdbGetObjectId(id,plBoundary);
  65.                                 if(acdbOpenObject(pEnt,id,AcDb::kForWrite) == Acad::eOk)
  66.                                 {
  67.                                         pEnt->setColorIndex(i+1);
  68.                                         pEnt->downgradeOpen();
  69.                                         pEnt->draw();
  70.                                         pEnt->close();
  71.                                 }
  72.                         }
  73.                 }
  74.  
  75. //-> возвращаем все в зад :)
  76.                 cmdechoVar.resval.rint = 1;
  77.                 acedSetVar(_T("cmdecho"), &cmdechoVar);
  78. //<-

по коду, пользователь клацает точки пока не устанет, потом создаются контура. варианта создания 2:
первый - используя все видимые объекты чертежа
второй - предварительно сформировав массив объектов, которые будут участвовать в формировании контуров (в коде сам массив пустой, так что надо бы дописать пару строчек для формирования массива и в конце строчка для его удаления :) )
Название: Re: Аналог команды _boundary.
Отправлено: Paravozzz от 01-06-2018, 15:33:17
Николай Горлов, благодарю! Похоже это то что нужно.
Название: Re: Аналог команды _boundary.
Отправлено: Александр Ривилис от 01-06-2018, 16:42:22
для С# есть, для С++ увы ничего нет.
Кто сказал, что нет для C++? А это что:
Код - C++ [Выбрать]
  1. Acad::ErrorStatus acedTraceBoundary(
  2.     const AcGePoint3d& seedPoint,
  3.     bool detectIslands,
  4.     AcDbVoidPtrArray& resultingBoundarySet
  5. );
И она есть как минимум с AutoCAD 2011. В AutoCAD .NET она появилась на версию позже.
Название: Re: Аналог команды _boundary.
Отправлено: Debalance от 02-06-2018, 12:37:56
Кто сказал, что нет для C++? А это что:
В описании к acedTraceBoundary сказано:
Цитировать
To succeed, the entities that form the boundary to be found must be entirely visible on screen in the AutoCAD editor.
В то время как автор топика говорит об "имеющемся наборе объектов". Т.е., как я понимаю, набор должен быть входным параметром функции....
Название: Re: Аналог команды _boundary.
Отправлено: Александр Ривилис от 02-06-2018, 12:54:46
Debalance,
Я уточнял ответ Николая Горлова. Он имел в виду, что в .NET есть метод Editor.TraceBoundary, но забыл, что это просто обертка для acedTraceBoundary. Да, в команде _BOUNDARY больше возможностей, чем в  acedTraceBoundary, но в любом случае переданный набор примитивов должен быть полностью видим на экране.
Название: Re: Аналог команды _boundary.
Отправлено: Debalance от 02-06-2018, 13:01:46
... но в любом случае переданный набор примитивов должен быть полностью видим на экране.
Поясните: если объекты попали в набор, но частично не "влезли" на экран, то будет ли построен контур в этом случае?
Название: Re: Аналог команды _boundary.
Отправлено: Александр Ривилис от 02-06-2018, 13:31:08
Поясните: если объекты попали в набор, но частично не "влезли" на экран, то будет ли построен контур в этом случае?
В моих экспериментах контур не создавался. Но я проверял много лет назад. В любом случае поведение в таком случае недокументировано и результаты могут быть непредсказуемыми.
Название: Re: Аналог команды _boundary.
Отправлено: Debalance от 02-06-2018, 13:45:56
В моих экспериментах контур не создавался.
Т.е., резюмируя сказанное Вами, получается задача в обобщенном случае (точка + набор), средствами ObjectARX не имеет решения. Обязательное условие - полная видимость объектов...
Название: Re: Аналог команды _boundary.
Отправлено: Александр Ривилис от 02-06-2018, 14:05:05
Т.е., резюмируя сказанное Вами, получается задача в обобщенном случае (точка + набор), средствами ObjectARX не имеет решения. Обязательное условие - полная видимость объектов...
Если речь идёт о команде _BOUNDARY или функции acedTraceBoundary, то ответ - да. Но написать свою трассировку, используя средства ObjectARX и независящую от видимости примитивов на экране, вполне возможно. Я так понимаю, что ты именно это и сделал. Насколько я помню и в Map 3d есть аналогичная возможность.
Название: Re: Аналог команды _boundary.
Отправлено: Debalance от 02-06-2018, 14:17:44
Я так понимаю, что ты именно это и сделал.
Да, конечно  ::).

Просто на вопрос автор топика:
Есть ли методы в objectarx 2016, которые реализуют аналогичное создание контура при имеющемся наборе объектов и указании внутренней точки?
Отвечать (судя по всему) надо так: Для решения задачи в обобщённом случае - нет.
Название: Re: Аналог команды _boundary.
Отправлено: АлексЮстасу от 03-06-2018, 22:58:39
Off-Topic: показать
Для человека, который своими силами решил, и качественно решил эту задачу, выступаете крайне скромно.  :D
Название: Re: Аналог команды _boundary.
Отправлено: Paravozzz от 28-06-2019, 00:35:18
Код - C++ [Выбрать]
  1.                                 acdbEntLast(plBoundary);
  2.                                 if (plBoundaryPreviousStep[0] == plBoundary[0] && plBoundaryPreviousStep[1] == plBoundary[1])
  3.                                 {
  4.                                         acutPrintf(_T("  %d-я точка поганая\n"),i+1);
  5.                                         continue;
  6.                                 }
  7.                                 // перекинули последний добавленный в акад объект
  8.                                 plBoundaryPreviousStep[0] = plBoundary[0];
  9.                                 plBoundaryPreviousStep[1] = plBoundary[1];
Николай, вы не могли бы объяснить вот этот фрагмент кода? Я иду по варианту 2, передаю ads_name mas как набор из 4-х полилиний представляющих из себя нечто похожее на #. Далее указываю в центр этой #, т.е. в образованный замкнутый контур и нажимаю подтвердить. А на выходе получаю, что "1-я точка поганая"...

UPD: решил, но не понял как. При установленном флаге команды ACRX_CMD_SESSION, контур не рисует. Без него - работает отлично.
Где можно подробнее прочитать про ACRX_CMD_SESSION, ACRX_CMD_MODAL и т.п.?
Название: Re: Аналог команды _boundary.
Отправлено: Александр Ривилис от 28-06-2019, 12:54:29
При установленном флаге команды ACRX_CMD_SESSION, контур не рисует.
И не должен. Флаг ACRX_CMD_SESSION означает, что команда работает в контексте приложения. Во-первых, при установленном флаге ACRX_CMD_SESSION следует блокировать модифицируемый документ. Во-вторых, в большинстве случаев acedCommandS не работает в контексте приложения (например, когда установлен флаг ACRX_CMD_SESSION).
Название: Re: Аналог команды _boundary.
Отправлено: Debalance от 28-06-2019, 14:06:08
Paravozzz, прошёл год с публикации Вашего топика. Как продвинулась работа по созданию метода? Есть ли позитивные подвижки? Какова стабильность в работе? Интересует любая информация...
Название: Re: Аналог команды _boundary.
Отправлено: Paravozzz от 28-06-2019, 16:01:47
Paravozzz, прошёл год с публикации Вашего топика. Как продвинулась работа по созданию метода? Есть ли позитивные подвижки? Какова стабильность в работе? Интересует любая информация...
Debalance, с момента публикации топика работа не двигалась. Это хобби-проект, направленный на облегчение рутины по подсчету объемов. На данный момент приму алгоритм, предложенный Николаем.
Название: Re: Аналог команды _boundary.
Отправлено: Debalance от 28-06-2019, 16:06:26
... работа не двигалась.
Всё понял. Спасибо.
Название: Re: Аналог команды _boundary.
Отправлено: Алексей (IdeaSoft) от 27-07-2020, 13:23:53
А как сделать эту команду с помощью NET API?
для моего ограниченного перечня примитивов
Хочу вот типа такой функции (как некий частный случай)

Код - C# [Выбрать]
  1. // RectExt -  объект внешнего прямоугольника
  2. // ents - массив объектов внутри прямоугольника RectExt
  3. // Обязательное условие в том, что перечень ents мне заведомо известен и все
  4. // они находятся внутри этого прямоугольника
  5. public poliline myBound(polyline RectExt, Entity[] ents)
  6. {
  7.    polylie pl;
  8.    // Есть тут какой метод вызвать в API?
  9.   // pl = ......
  10.   return pl;
  11.  
  12. }
Название: Re: Аналог команды _boundary.
Отправлено: Алексей (IdeaSoft) от 27-07-2020, 13:52:07
Я обратил внимание на метод TraceBoundary

Код - C# [Выбрать]
  1. PromptPointResult ptResult = ed.GetPoint(ptOptions);
  2. DBObjectCollection collection =
  3.                    ed.TraceBoundary(ptResult.Value, true);

Но только как мне заставить сканировать
не всю модель, а передать нужный
мне перечень элементов?


Название: Re: Аналог команды _boundary.
Отправлено: Николай Горлов от 27-07-2020, 15:42:51
что для TraceBoundary для .NET, что для acedTraceBoundary для ObjectARX... контур создается при условии, что точка и примитивы, участвующие в работе полностью видимы на экране.
не пробовал, то может сработать... попробуй спрятать все примитивы, которые не попали в Entity[] ents. да, нужен полный пробег по базе, с проверкой вхождения текущего объекта в ents (хотя проще с AcDbObjectId) 
если объяснить на пальцах, то в ObjectARX это может выглядеть так:
Код - C++ [Выбрать]
  1. ...
  2. // перебор всех объектов чертежа (ну или листа, если в нем команда будет работать)
  3. if (!entIds.contains(curId))
  4. {
  5.    открыть AcDbEntity по curId для записи
  6.    AcDbEntity::setVisibility(AcDb::kInvisible);
  7.    AcDbEntity::recordGraphicsModified(Adesk::kTrue); // может будет работать и без этого
  8.    AcDbEntity::close();
  9.    сохранить curId в массив
  10. }
  11.  
  12. тут делаем границу
  13.  
  14. for (int i = 0; i < длинна массива спрятанных элементов; i++)
  15. {
  16.     открыть AcDbEntity по ID[i] для записи
  17.     AcDbEntity::setVisibility(AcDb::kVisible);
  18.     AcDbEntity::recordGraphicsModified(Adesk::kTrue); // может будет работать и без этого
  19.     AcDbEntity::close();
  20. }
  21. ...
  22.  
Название: Re: Аналог команды _boundary.
Отправлено: Алексей (IdeaSoft) от 27-07-2020, 16:05:19
спрятать все примитивы
Спрятать не совсем хороший метод. Бывает такие большие чертежи по 800 тысяч элементов.
А мне нужно всего то из этого несколько десятков элементов.
Алгоритм прятать их будет долго. И с точки зрения "юзабилити" это не совсем красиво.
Название: Re: Аналог команды _boundary.
Отправлено: Алексей (IdeaSoft) от 27-07-2020, 16:07:37
Единственное сто пока приходит на ум это в
рантайме сгенерить новый документ. Но генерация нового документа это тоже
не красиво с точки зрения юзабилити (кому нужны эти мелькания на экране)
Название: Re: Аналог команды _boundary.
Отправлено: Алексей (IdeaSoft) от 27-07-2020, 16:11:25
Попробовал сделать через

Код - C# [Выбрать]
  1. Editor ed = doc.Editor;
  2. ObjectI[] ids = ...
  3. ed.SetImpliedSelection(ids);
  4. ed.TraceBoundary(...)

Но все равно TraceBoundary отработал по полному перечню объектов модели.
Название: Re: Аналог команды _boundary.
Отправлено: Николай Горлов от 28-07-2020, 10:19:34
Ну хорошо, если прятать остальные объекты долго, можно ж клацнуть точку, потом перенести необходимые объекты куда-то за область чертежа (короче, за границы min или мах Extents), пересчитать точку, отзумироваться на объекты, получить свой новый контур, а потом всё это вернуть в исходные координаты (если пользователь моргнет в это время, то он даже не заметит мельтишения на экране :) ) и выставить предыдущую область видимости.