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

ADN Club => AutoCAD .NET API => Тема начата: Алексей Терно от 09-03-2017, 22:12:43

Название: ObjectARX -> .NET функции map_dwgtrimobj
Отправлено: Алексей Терно от 09-03-2017, 22:12:43
Имеется непреодалимое желание использовать функцию map_dwgtrimobj (Map 3D API), которая доступна только на C++. Если будет создана библиотека на С++, реализующая эту функцию, и ее потом подключить к проекту .NET, большие потери времени будут? Предполагается использовать эту функцию много раз в цикле.
Название: Re: ObjectARX -> .NET
Отправлено: Александр Ривилис от 09-03-2017, 22:14:44
Потери будут только при передаче параметров и возврате результатов (если она что-то возвращает).
Название: Re: ObjectARX -> .NET
Отправлено: Алексей Терно от 09-03-2017, 22:17:23
Т.е. это будет практически то же самое, что и "родные" функции .NET?
Название: Re: ObjectARX -> .NET
Отправлено: Александр Ривилис от 09-03-2017, 22:19:57
Ну точно оценить я не берусь, но помню что существенные потери были при передача очень больших список (десятки и сотни тысяч элементов). Всё что меньше было несущественно.
Название: Re: ObjectARX -> .NET
Отправлено: Александр Ривилис от 09-03-2017, 22:40:00
Я посмотрел описание этой функции:
Код - C++ [Выбрать]
  1. /*
  2.  *  Trims linear objects which cross boundary edge(s)
  3.  *
  4.  *  Parameters:
  5.  *  ssclip      - selection set of objects to clip;
  6.  *  boundary    - boundary entity name of list of points.
  7.  *  inorout     - 1 = cut inside boundary
  8.  *                0 = cut outside boundary
  9.  *  skiptopo    - 1 = Skip objects referenced by a topology
  10.  *                0 = Clip objects referenced by a topology
  11.  *  keepod      - 1 = Retains all object data on any clipped object
  12.  *                0 = Drops object data on any clipped object
  13.  *  bitflag     - 0 = Delete any non edge objects within or on clip boundary
  14.  *                1 = Ignore any non edge objects within or on clip boundary
  15.  *                2 = Reference the insertion point of any non edge objects
  16.  *                    within or on clip boundary
  17.  *  ssreturn    - selection set of trimmed objects or Nil in case of error
  18.  *
  19.  *  Returns RTNORM or RTERROR
  20.  */
  21.     int map_dwgTrimObj (ads_name ssclip,
  22.                         ads_name boundary,
  23.                         short inorout,
  24.                         short skiptopo,
  25.                         short keepod,
  26.                         short bitflag,
  27.                         ads_name ssreturn);
Теоретически её можно вызвать через P/Invoke. Не знаю почему http://adndevblog.typepad.com/infrastructure/2014/03/using-maptrim-in-net-application.html пишут, что нужно делать через командные методы.
Название: Re: ObjectARX -> .NET
Отправлено: Александр Ривилис от 10-03-2017, 01:59:37
Всё оказалось еще проще и еще интересней. Параллельно с map_dwgTrimObj в ObjectARX для Map 3D есть еще и одноимённая функция, экспортируемая из arx в lisp, которою можно легко использовать из .NET напрямую при помощи Application.Invoke. Так как количество данных здесь минимальное и все потери времени на самой работе функции map_dwgTrimObj, то мы воспользуемся именно lisp-функцией. Код будет выглядеть так:

Код - C# [Выбрать]
  1. using Autodesk.AutoCAD.Runtime;
  2. using Autodesk.AutoCAD.ApplicationServices;
  3. using Autodesk.AutoCAD.DatabaseServices;
  4. using Autodesk.AutoCAD.EditorInput;
  5.  
  6. [assembly: CommandClass(typeof(Rivilis.Commands))]
  7.  
  8. namespace Rivilis
  9. {
  10.   public class Commands
  11.   {
  12.     [CommandMethod("MapTrimmer")]
  13.     public void MyCommand()
  14.     {
  15.       Document doc = Application.DocumentManager.MdiActiveDocument;
  16.       if (doc == null) return;
  17.       Editor ed = doc.Editor;
  18.       PromptSelectionOptions prSel = new PromptSelectionOptions();
  19.       prSel.MessageForAdding = "Выберите обрезаемые объекты";
  20.       PromptSelectionResult rsSel = ed.GetSelection(prSel);
  21.       if (rsSel.Status != PromptStatus.OK || rsSel.Value.Count == 0)
  22.         return;
  23.       PromptEntityResult rsEnt = ed.GetEntity("Выберите контур обрезки");
  24.       if (rsEnt.Status != PromptStatus.OK)
  25.         return;
  26.       SelectionSet ss = Map_DwgTrimobj(rsSel.Value, rsEnt.ObjectId, 1, 1, 1, 0);
  27.       if (ss != null)
  28.       {
  29.         ed.WriteMessage("\nВ наборе {0} объектов.", ss.Count);
  30.         ss.Dispose();
  31.       }
  32.       else
  33.       {
  34.         ed.WriteMessage("\nОшибка обрезки!!!");
  35.       }
  36.     }
  37.     static public SelectionSet Map_DwgTrimobj(
  38.       SelectionSet ss,
  39.       ObjectId boundary,
  40.       int inorout,  // 1 - оставлять внутреннюю часть, 0 - оставлять наружную
  41.       int skiptopo, // 1 - пропускать объекты с ссылками на топологию, 0 - обрезать
  42.       int keepod,   // 1 - сохранять объектные данные обрезаемых объектов, 0 - удалять объектные данные
  43.       int bitflag   // 0 - удалять все объекты, которые не имеют ребер, находящиеся внутри или на границе
  44.                     // 1 - игнорировать все объекты не имеющие ребер, находящиеся внутри или на границе
  45.                     // 2 - в зависимости от того есть ли точка вставки у объектов не имеющих ребер
  46.       )
  47.     {
  48.       SelectionSet ssout = null;
  49.       // Готовим данные для Application.Invoke
  50.       ResultBuffer resbuf = new ResultBuffer();
  51.       resbuf.Add(new TypedValue((int)LispDataType.Text, "map_dwgtrimobj"));
  52.       resbuf.Add(new TypedValue((int)LispDataType.SelectionSet, ss));
  53.       resbuf.Add(new TypedValue((int)LispDataType.ObjectId, boundary));
  54.       resbuf.Add(new TypedValue((int)LispDataType.Int16, inorout));
  55.       resbuf.Add(new TypedValue((int)LispDataType.Int16, skiptopo));
  56.       resbuf.Add(new TypedValue((int)LispDataType.Int16, keepod));
  57.       resbuf.Add(new TypedValue((int)LispDataType.Int16, bitflag));
  58.       ResultBuffer result = null;
  59.       try
  60.       {
  61.         result = Application.Invoke(resbuf);
  62.       }
  63.       catch { }
  64.       // Освобождаем буфер
  65.       resbuf.Dispose();
  66.       if (result != null)
  67.       {
  68.         TypedValue[] tpArr = result.AsArray();
  69.         if (tpArr[0].TypeCode == (int) LispDataType.SelectionSet)
  70.           ssout = tpArr[0].Value as SelectionSet;
  71.         result.Dispose();
  72.       }
  73.       return ssout;
  74.     }
  75.   }
  76. }

Ну и результат работы проверим в Civil 3d 2016:

Название: Re: ObjectARX -> .NET
Отправлено: Алексей Терно от 10-03-2017, 08:35:23
Александр, это то, что надо!!! Спасибо огромное!  :)
Название: Re: ObjectARX -> .NET
Отправлено: Александр Ривилис от 10-03-2017, 13:07:30
Александр, это то, что надо!!! Спасибо огромное!  :)
Я и не сомневался :) Интересно почему за столько лет никто не предложил такое простое решение? Думаю оформить это всё в виде статьи на сайт.
Название: Re: ObjectARX -> .NET функции map_dwgtrimobj
Отправлено: Алексей Терно от 18-05-2017, 22:13:01
Начал изощряться с этой функцией и пошли странные результаты.
Исходные данные:
набор выбранных обрезаемых объектов - SelectionSet ss
коллекция границ - List<ObjectId> ids (первая границы внешняя, остальные внутренние).
Вот исходные объекты - сетка и границы:
(https://s18.postimg.org/s6syjlu2d/2017-05-18_22-02-49.png) (https://postimg.org/image/s6syjlu2d/)

Надо получить вот такое:
(https://s18.postimg.org/3t7clpnbp/2017-05-18_22-04-23.png) (https://postimg.org/image/3t7clpnbp/)

Имеется вот такой код:
Код - C# [Выбрать]
  1. for (int i = 0; i < ids.Count; i++)
  2. {
  3.     if (i == 0)
  4.     {
  5.         //первый - внешний контур
  6.         ss = TrimObjects(ss, ids[i], 1, 1, 1, 0);
  7.     }
  8.     else
  9.     {
  10.         //остальные - внутренние
  11.         ss = TrimObjects(ss, ids[i], 0, 1, 1, 0);
  12.     }
  13. }

И в результате получаю вот такую картину:
(https://s13.postimg.org/sfd7nurdf/2017-05-18_21-56-46.png) (https://postimg.org/image/sfd7nurdf/)

Начал разбираться в  последовательных операциях и вот, что получил:
после первой обрезки:
(https://s18.postimg.org/cduwc0qid/2017-05-18_21-57-11.png) (https://postimg.org/image/cduwc0qid/)

после второй:
(https://s13.postimg.org/airodhwpf/2017-05-18_21-57-34.png) (https://postimg.org/image/airodhwpf/)

после третьей:
(https://s1.postimg.org/lgm3foq8b/2017-05-18_21-57-44.png) (https://postimg.org/image/lgm3foq8b/)


После второй обрезки остаются объекты только те, которые пересекают контур. Причем, если выбирать эти контуры по отдельности, то команда отрабатывает на ура.
Это ошибка функции map_dwgtrimobj?
Название: Re: ObjectARX -> .NET функции map_dwgtrimobj
Отправлено: Александр Ривилис от 19-05-2017, 01:00:06
Думаю, что после первой обрезки объекты в ss становятся недействительными. Во всяком случае часть из них, которая обрезалась. Подумай над этим.
Название: Re: ObjectARX -> .NET функции map_dwgtrimobj
Отправлено: Алексей Терно от 19-05-2017, 01:09:18
Думаю, что после первой обрезки объекты в ss становятся недействительными. Во всяком случае часть из них, которая обрезалась. Подумай над этим.

Исходя из самой функции, это не так, поскольку она возвращает новый набор объектов, уже обрезанных.
Название: Re: ObjectARX -> .NET функции map_dwgtrimobj
Отправлено: Александр Ривилис от 19-05-2017, 01:15:25
Ну поэкспериментируй еще с параметрами.
Если ничего не получится, то останется списать на баг. Тем более, что функция недокументированная и похоже никем не востребованная.
Название: Re: ObjectARX -> .NET функции map_dwgtrimobj
Отправлено: Алексей Терно от 19-05-2017, 01:18:57
Я уже пробовал различные комбинации параметров - мою проблему не решили.
Если эта функция никем не востребована, то и исправлять баг никто не будет, а значит, что и рапортовать нет смысла :(
Подумаю, как это можно обойти. Как придумаю, отпишусть здесь.
Название: Re: ObjectARX -> .NET функции map_dwgtrimobj
Отправлено: German от 19-05-2017, 10:22:31
Причем, если выбирать эти контуры по отдельности, то команда отрабатывает на ура.
Если вместо первой обрезки сразу запустить только одну вторую, что на выходе?
Если корректная дырка, значит и функция корректна.
Название: Re: ObjectARX -> .NET функции map_dwgtrimobj
Отправлено: Александр Ривилис от 19-05-2017, 10:28:49
Исходя из самой функции, это не так, поскольку она возвращает новый набор объектов, уже обрезанных.
Но тебе же нужны не только обрезанные, но и те, которые не обрезались для дальнейших обрезок.
Название: Re: ObjectARX -> .NET функции map_dwgtrimobj
Отправлено: Алексей Терно от 19-05-2017, 11:03:34
Вот оно - оказывается эта функция возвращает только обрезанные объекта, а не все, как я предполагал ранее!!!
Название: Re: ObjectARX -> .NET функции map_dwgtrimobj
Отправлено: Алексей Терно от 19-05-2017, 11:14:42
Достаточно сделать новую выборку по всем обрезаемым объектам перед каждой обрезкой:
Код - C# [Выбрать]
  1. for (int i = 0; i < ids.Count; i++)
  2. {
  3.     if (i == 0)
  4.     {
  5.         //первый - внешний контур
  6.         ss = ed.SelectAll(filter).Value;
  7.         TrimObjects(ss, ids[i], 1, 1, 1, 0);
  8.     }
  9.     else
  10.     {
  11.         //остальные - внутренние
  12.         ss = ed.SelectAll(filter).Value;
  13.         TrimObjects(ss, ids[i], 0, 1, 1, 0);
  14.     }
  15. }
Название: Re: ObjectARX -> .NET функции map_dwgtrimobj
Отправлено: Александр Ривилис от 19-05-2017, 16:05:36
Вот оно - оказывается эта функция возвращает только обрезанные объекта, а не все, как я предполагал ранее!!!
Ха. Читать нужно описание. Об этом четко сказано:
*  ssreturn    - selection set of trimmed objects or Nil in case of error
Название: Re: ObjectARX -> .NET функции map_dwgtrimobj
Отправлено: Алексей Терно от 19-05-2017, 16:07:19
Ха. Читать нужно описание.
Читать описание - это слишком легкий путь )))