Получение вложенных примитивов под апертурой курсора с использованием .NET API
Некоторое время назад мне был задан вопрос членом ADN о возможности в реальном времени получать примитивы внутри апертуры курсора, когда пользователь перемещает указатель мыши. Вопрос не сложен пока нас не интересуют вложенные объекты, такие как объекты во внешних ссылках…
Получение примитивов в указанной точке к сожалению не обеспечивается управляемым (.NET) кодом, так что нам придется использовать P/Invoke, и в частности, функции acedSSGet and acedSSName с опцией “:N”. Но эти функции не возвращают вложенные примитивы, а основные как например вставку блока. Чтобы получить вложенные примитивы требуется вызов acedSSNameX, и сигнатура этого метода не слишком интуитивна для вызова P/Invoking из .Net: требуется объявление структуры “resbuf”, а также кусок небезопасного кода для того чтобы сопоставить управляемые и неуправляемые объекты.
При выполнении команды “UnderCursorNested” будут напечатаны в командной строке все примитивы, которые попадут в апертуру курсора и будут вставкой блока на слое “0”, и любые вложенные примитивы, содержащиеся в этой вставке блока, в том числе и примитивы из внешней ссылки. Эти условия для фильтрации можно при необходимости изменить..
- class ArxImports
- {
- public struct ads_name
- {
- public IntPtr a;
- public IntPtr b;
- };
- [StructLayout(LayoutKind.Sequential, Size = 32)]
- public struct resbuf { }
- [DllImport("acad.exe",
- CallingConvention = CallingConvention.Cdecl,
- CharSet = CharSet.Unicode,
- ExactSpelling = true)]
- public static extern PromptStatus acedSSGet(
- string str, IntPtr pt1, IntPtr pt2,
- IntPtr filter, out ads_name ss);
- [DllImport("acad.exe",
- CallingConvention = CallingConvention.Cdecl,
- CharSet = CharSet.Unicode,
- ExactSpelling = true)]
- public static extern PromptStatus acedSSFree(ref ads_name ss);
- [DllImport("acad.exe",
- CallingConvention = CallingConvention.Cdecl,
- CharSet = CharSet.Unicode,
- ExactSpelling = true)]
- public static extern PromptStatus acedSSLength(
- ref ads_name ss, out int len);
- [DllImport("acad.exe",
- CallingConvention = CallingConvention.Cdecl,
- CharSet = CharSet.Unicode,
- ExactSpelling = true)]
- public unsafe static extern PromptStatus acedSSNameX(
- resbuf** rbpp, ref ads_name ss, int i);
- }
- class CursorDetectCls
- {
- Editor _ed;
- [CommandMethod("UnderCursorNested")]
- public void UnderCursorNested()
- {
- _ed = Application.DocumentManager.MdiActiveDocument.Editor;
- // Подписываемся на событие PointMonitor
- _ed.PointMonitor +=
- new PointMonitorEventHandler(PointMonitorMulti);
- }
- void PointMonitorMulti(object sender, PointMonitorEventArgs e)
- {
- // Фильтруем только вставки блоков (INSERT)
- // на слое "0"
- ResultBuffer resbuf = new ResultBuffer(
- new TypedValue(-4, "<and"),
- new TypedValue(0, "INSERT"),
- new TypedValue(8, "0"),
- new TypedValue(-4, "and>"));
- ObjectId[] ids = FindAtPointNested(
- _ed.Document,
- e.Context.RawPoint,
- true,
- resbuf.UnmanagedObject);
- // Печатаем результаты в командную строку
- foreach (ObjectId id in ids)
- {
- _ed.WriteMessage("\n - Entity: {0} [Id:{1}]",
- id.ObjectClass.Name,
- id.ToString());
- }
- }
- // Возвращаем ObjectIds примитивов в указанном месте
- // Включаем вложенные в блок примитивы
- static public ObjectId[] FindAtPointNested(
- Document doc,
- Point3d worldPoint,
- bool selectAll,
- IntPtr filter)
- {
- System.Collections.Generic.List<ObjectId> ids =
- new System.Collections.Generic.List<ObjectId>();
- Matrix3d wcs2ucs =
- doc.Editor.CurrentUserCoordinateSystem.Inverse();
- Point3d ucsPoint = worldPoint.TransformBy(wcs2ucs);
- string arg = selectAll ? "_:E:N" : "_:N";
- IntPtr ptrPoint = Marshal.UnsafeAddrOfPinnedArrayElement(
- worldPoint.ToArray(), 0);
- ArxImports.ads_name sset;
- PromptStatus prGetResult = ArxImports.acedSSGet(
- arg, ptrPoint, IntPtr.Zero, filter, out sset);
- int len;
- ArxImports.acedSSLength(ref sset, out len);
- // Нужно воспользоваться небезопасным кодом для использования указателей *
- unsafe
- {
- for (int i = 0; i < len; ++i)
- {
- ArxImports.resbuf rb = new ArxImports.resbuf();
- ArxImports.resbuf* pRb = &rb;
- if (ArxImports.acedSSNameX(&pRb, ref sset, i) !=
- PromptStatus.OK)
- continue;
- // Создаем управляемый ResultBuffer из наших структур resbuf
- using (ResultBuffer rbMng = DisposableWrapper.Create(
- typeof(ResultBuffer),
- (IntPtr)pRb,
- true) as ResultBuffer)
- {
- foreach (TypedValue tpVal in rbMng)
- {
- // Пропускаем если это не ObjectId
- if (tpVal.TypeCode != 5006) //RTENAME
- continue;
- ObjectId id = (ObjectId)tpVal.Value;
- if (id != null)
- ids.Add(id);
- }
- }
- }
- }
- ArxImports.acedSSFree(ref sset);
- return ids.ToArray();
- }
- }
Обсуждение: http://adn-cis.org/forum/index.php?topic=584
Опубликовано 03.03.2014