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

03/03/2014

Получение вложенных примитивов под апертурой курсора с использованием .NET API

Некоторое время назад мне был задан вопрос членом ADN о возможности в реальном времени получать примитивы внутри апертуры курсора, когда пользователь перемещает указатель мыши. Вопрос не сложен пока нас не интересуют вложенные объекты, такие как объекты во внешних ссылках…

Получение примитивов в указанной точке к сожалению не обеспечивается управляемым (.NET) кодом, так что нам придется использовать P/Invoke, и в частности, функции acedSSGet and acedSSName с опцией “:N”. Но эти функции не возвращают вложенные примитивы, а основные как например вставку блока. Чтобы получить вложенные примитивы требуется вызов acedSSNameX, и сигнатура этого метода не слишком интуитивна для вызова P/Invoking из .Net: требуется объявление структуры “resbuf”, а также кусок небезопасного кода для того чтобы сопоставить управляемые и неуправляемые объекты.

При выполнении команды “UnderCursorNested” будут напечатаны в командной строке все примитивы, которые попадут в апертуру курсора и будут вставкой блока на слое “0”, и любые вложенные примитивы, содержащиеся в этой вставке блока, в том числе и примитивы из внешней ссылки. Эти условия для фильтрации можно при необходимости изменить.. 

Код - C#: [Выделить]
  1. class ArxImports
  2. {
  3.     public struct ads_name
  4.     {
  5.         public IntPtr a;
  6.         public IntPtr b;
  7.     };
  8.  
  9.     [StructLayout(LayoutKind.Sequential, Size = 32)]
  10.     public struct resbuf { }
  11.  
  12.     [DllImport("acad.exe",
  13.         CallingConvention = CallingConvention.Cdecl,
  14.         CharSet = CharSet.Unicode,
  15.         ExactSpelling = true)]
  16.     public static extern PromptStatus acedSSGet(
  17.         string str, IntPtr pt1, IntPtr pt2,
  18.         IntPtr filter, out ads_name ss);
  19.  
  20.     [DllImport("acad.exe",
  21.         CallingConvention = CallingConvention.Cdecl,
  22.         CharSet = CharSet.Unicode,
  23.         ExactSpelling = true)]
  24.     public static extern PromptStatus acedSSFree(ref ads_name ss);
  25.  
  26.     [DllImport("acad.exe",
  27.         CallingConvention = CallingConvention.Cdecl,
  28.         CharSet = CharSet.Unicode,
  29.         ExactSpelling = true)]
  30.     public static extern PromptStatus acedSSLength(
  31.         ref ads_name ss, out int len);
  32.  
  33.     [DllImport("acad.exe",
  34.         CallingConvention = CallingConvention.Cdecl,
  35.         CharSet = CharSet.Unicode,
  36.         ExactSpelling = true)]
  37.     public unsafe static extern PromptStatus acedSSNameX(
  38.         resbuf** rbpp, ref ads_name ss, int i);
  39. }
  40.  
  41. class CursorDetectCls
  42. {
  43.     Editor _ed;
  44.  
  45.     [CommandMethod("UnderCursorNested")]
  46.     public void UnderCursorNested()
  47.     {
  48.         _ed = Application.DocumentManager.MdiActiveDocument.Editor;
  49.  
  50.         // Подписываемся на событие PointMonitor
  51.         _ed.PointMonitor +=
  52.             new PointMonitorEventHandler(PointMonitorMulti);
  53.     }
  54.  
  55.     void PointMonitorMulti(object sender, PointMonitorEventArgs e)
  56.     {
  57.         // Фильтруем только вставки блоков (INSERT)
  58.         // на слое "0"
  59.         ResultBuffer resbuf = new ResultBuffer(
  60.             new TypedValue(-4, "<and"),
  61.             new TypedValue(0, "INSERT"),
  62.             new TypedValue(8, "0"),
  63.             new TypedValue(-4, "and>"));
  64.  
  65.         ObjectId[] ids = FindAtPointNested(
  66.             _ed.Document,
  67.             e.Context.RawPoint,
  68.             true,
  69.             resbuf.UnmanagedObject);
  70.  
  71.         // Печатаем результаты в командную строку
  72.         foreach (ObjectId id in ids)
  73.         {
  74.             _ed.WriteMessage("\n - Entity: {0} [Id:{1}]",
  75.                 id.ObjectClass.Name,
  76.                 id.ToString());
  77.         }
  78.     }
  79.  
  80.     // Возвращаем ObjectIds примитивов в указанном месте
  81.     // Включаем вложенные в блок примитивы
  82.     static public ObjectId[] FindAtPointNested(
  83.         Document doc,
  84.         Point3d worldPoint,
  85.         bool selectAll,
  86.         IntPtr filter)
  87.     {
  88.         System.Collections.Generic.List<ObjectId> ids =
  89.             new System.Collections.Generic.List<ObjectId>();
  90.  
  91.         Matrix3d wcs2ucs =
  92.             doc.Editor.CurrentUserCoordinateSystem.Inverse();
  93.  
  94.         Point3d ucsPoint = worldPoint.TransformBy(wcs2ucs);
  95.  
  96.         string arg = selectAll ? "_:E:N" : "_:N";
  97.  
  98.         IntPtr ptrPoint = Marshal.UnsafeAddrOfPinnedArrayElement(
  99.             worldPoint.ToArray(), 0);
  100.  
  101.         ArxImports.ads_name sset;
  102.  
  103.         PromptStatus prGetResult = ArxImports.acedSSGet(
  104.             arg, ptrPoint, IntPtr.Zero, filter, out sset);
  105.  
  106.         int len;
  107.         ArxImports.acedSSLength(ref sset, out len);
  108.  
  109.         // Нужно воспользоваться небезопасным кодом для использования указателей *
  110.         unsafe
  111.         {
  112.             for (int i = 0; i < len; ++i)
  113.             {
  114.                 ArxImports.resbuf rb = new ArxImports.resbuf();
  115.  
  116.                 ArxImports.resbuf* pRb = &rb;
  117.  
  118.                 if (ArxImports.acedSSNameX(&pRb, ref sset, i) !=
  119.                     PromptStatus.OK)
  120.                     continue;
  121.  
  122.                 // Создаем управляемый ResultBuffer из наших структур resbuf
  123.                 using (ResultBuffer rbMng = DisposableWrapper.Create(
  124.                     typeof(ResultBuffer),
  125.                     (IntPtr)pRb,
  126.                     true) as ResultBuffer)
  127.                 {
  128.                     foreach (TypedValue tpVal in rbMng)
  129.                     {
  130.                         // Пропускаем если это не ObjectId
  131.                         if (tpVal.TypeCode != 5006) //RTENAME
  132.                             continue;
  133.  
  134.                         ObjectId id = (ObjectId)tpVal.Value;
  135.  
  136.                         if (id != null)
  137.                             ids.Add(id);
  138.                     }
  139.                 }
  140.             }
  141.         }
  142.  
  143.         ArxImports.acedSSFree(ref sset);
  144.  
  145.         return ids.ToArray();
  146.     }
  147. }

 

Источник: http://adndevblog.typepad.com/autocad/2012/04/retrieving-nested-entities-under-cursor-aperture-using-net-api.html

Обсуждение: http://adn-cis.org/forum/index.php?topic=584

Опубликовано 03.03.2014