Возможности SelectionFilter, предоставляемого AutoCAD .NET API достаточно ограничены. Далеко не любое условие выборки можно сформировать с его помощью. Например, недавно некоторым нашим сотрудникам потребовалась команда, которая бы в текущем пространстве (Model\Layout) выбирала любые полилинии, имеющие указанное в запросе количество вершин. Такую выборку с помощью фильтров AutoCAD сделать не удастся.
Дело в том, что в отличие от класса Poliline, имеющего свойство NumberOfVertices, классы Polyline2d и Polyline3d подобного свойства не имеют, а доступ к их вершинам осуществляется посредством интерфейса IEnumerable.
Т. о. при помощи SelectionFilter мы можем, под наше условие, создать фильтр для Poliline но, к сожалению, никак не для Polyline2d или Polyline3d.
Сам по себе код, выполняющий обозначенную выше задачу является небольшим:
1: // SelectionCommands.cs
2: // Выбор всех полилиний с указанным количеством вершин. © Andrey Bushman, 2013
3:
4: //Microsoft
5: using System;
6: using System.Collections.Generic;
7: using System.Linq;
8:
9: //Autodesk
10: using cad = Autodesk.AutoCAD.ApplicationServices.Application;
11: using App = Autodesk.AutoCAD.ApplicationServices;
12: using Db = Autodesk.AutoCAD.DatabaseServices;
13: using Ed = Autodesk.AutoCAD.EditorInput;
14: using Rtm = Autodesk.AutoCAD.Runtime;
15:
16: // Bushman
17: using Bushman.CAD.DatabaseServices;
18: using System.Collections;
19:
20: [assembly: Rtm.ExtensionApplication(typeof(Bushman.CAD.Commands.SelectionCommands))]
21: [assembly: Rtm.CommandClass(typeof(Bushman.CAD.Commands.SelectionCommands))]
22:
23: namespace Bushman.CAD.Commands {
24:
25: public sealed class SelectionCommands : Rtm.IExtensionApplication {
26:
27: Int32 numberOfVertices = 0;
28:
29: #region Commands
30:
31: /// <summary>
32: /// Команда для выбора всех полилиний (2D и 3D) с указанным количеством вершин
33: /// в текущем пространстве (Model\Layout).
34: /// </summary>
35: [Rtm.CommandMethod("PlineSel", Rtm.CommandFlags.Modal)]
36: public void PolylineSelectionViaVertexCount() {
37: App.Document doc = cad.DocumentManager.MdiActiveDocument;
38: Db.Database db = doc.Database;
39: Ed.Editor ed = doc.Editor;
40:
41: Ed.PromptIntegerOptions intOpt = new Ed.PromptIntegerOptions(
42: "\nКоличество вершин в полилиниях, подлежащих выборке");
43: intOpt.AllowNegative = false;
44: intOpt.AllowNone = false;
45: intOpt.AllowZero = false;
46:
47: Ed.PromptIntegerResult intRes = ed.GetInteger(intOpt);
48:
49: if (intRes.Status != Ed.PromptStatus.OK) {
50: ed.WriteMessage("\nНе было выбрано ни одного примитива.\n");
51: return;
52: }
53:
54: numberOfVertices = intRes.Value;
55:
56: // Получаем идентификаторы объектов, соответствующих фильтру PlinesWithSomeVertexFilter:
57: Db.ObjectId[] selectionIds = db.GetData<Db.ObjectId>(PlinesWithSomeVertexFilter, (t, x) => x);
58:
59: ed.WriteMessage("\nКоличество выбранных примитивов: {0}\n", selectionIds.Length);
60:
61: // Результаты выборки подсвечиваю с отображением "ручек":
62: ed.SetImpliedSelection(selectionIds);
63: }
64:
65: #endregion
66:
67: /// <summary>
68: /// Фильтр для выборки любых полилиний с количеством вершин, указанном в переменной
69: /// numberOfVertices. Выбираются только полилинии текущего пространства (Model\Layout).
70: /// </summary>
71: /// <param name="tr">Объект транзакции, с помощью которого можно получить объект
72: /// на основе идентификатора, переданного во втором параметре</param>
73: /// <param name="id">Идентификатор объекта, подлежащего проверке.</param>
74: /// <returns>true - объект соответствует критериям фильтрации, false - не соответствует.
75: /// </returns>
76: Boolean PlinesWithSomeVertexFilter(Db.Transaction tr, Db.ObjectId id) {
77:
78: // Если один из аргументов недопустимый - возвращаем false
79: if (tr == null || tr.IsDisposed || id == Db.ObjectId.Null || !id.IsValid || id.IsErased)
80: return false;
81:
82: Db.Database db = id.Database;
83: Db.DBObject item = tr.GetObject(id, Db.OpenMode.ForRead);
84:
85: // Проверяю, является ли объект одним из трёх видов полилиний
86: Boolean result = item is Db.Polyline || item is Db.Polyline2d || item is Db.Polyline3d;
87: if (!result) return false;
88:
89: // Проверяю, расположена ли полилиния в текущем пространстве (Model\Layout)
90: Db.ObjectId ownerId = db.CurrentSpaceId;
91: // По достижении этой строки кода, мы уже знаем, что имеем дело с полилинией.
92: // Соответственно можем спокойно преобразовывать её в Entity.
93: Db.Entity entity = item as Db.Entity;
94:
95: if (entity.BlockId != ownerId) return false;
96:
97: // Проверяю количество вершин
98: Int32 _numberOfVertices = 0;
99:
100: if (item is Db.Polyline) {
101: Db.Polyline pline = item as Db.Polyline;
102: _numberOfVertices = pline.NumberOfVertices;
103: }
104: else {
105: IEnumerable pline = item as IEnumerable;
106: foreach (var vertex in pline) {
107: ++_numberOfVertices;
108: }
109: }
110:
111: return numberOfVertices == _numberOfVertices;
112: }
113:
114: #region IExtensionApplication Members
115:
116: public void Initialize() {
117: Ed.Editor ed = cad.DocumentManager.MdiActiveDocument.Editor;
118: ed.WriteMessage("\nPlineSel. © Andrey Bushman, 2013\n");
119: }
120:
121: public void Terminate() {
122: // throw new NotImplementedException();
123: }
124:
125: #endregion
126: }
127: }
Если вы внимательно читали выше приведённый код, то наверняка обратили внимание на строки 57 и 90. Обозначенные в этих строках методы отсутствуют в AutoCAD .NET API и добавлены мною в виде методов расширения. В конце этой заметки я привожу исходный код классов и перечислений, задействованных мною в решении обозначенной в этой теме задачи. На самом деле упомянутые вспомогательные классы и перечисления не писались специально под эту задачу, но предоставляют общий полезный функционал, которым я нередко пользуюсь в различных проектах. Например, методы класса DatabaseExtensionMethods позволяют посредством LINQ достаточно просто и быстро получать или изменять информацию, хранящуюся в Database.
1: // DBObjectStatus.cs
2: // © Andrey Bushman, 2011
3: namespace Bushman.CAD.DatabaseServices {
4: /// <summary>
5: /// Перечисление, с помощью которого в методах IDBSearcher
6: /// можно указывать, идентификаторы (DBObjectId) каких объектов
7: /// (DBObject) следует выбирать из базы данных (Database)
8: /// </summary>
9: public enum DBObjectStatus {
10: /// <summary>
11: /// Выбирать только такие ObjectId, значения свойства
12: /// IsErased которых равно false
13: /// </summary>
14: NotErased,
15: /// <summary>
16: /// Выбирать только такие ObjectId, значения свойства
17: /// IsErased которых равно true
18: /// </summary>
19: Erased,
20: /// <summary>
21: /// Выбирать все ObjectId, не зависимо от значения
22: /// свойства IsErased
23: /// </summary>
24: Any
25: }
26: }
1: // SpaceEnum.cs
2: // © Andrey Bushman, 2011
3: // За основу взят код с сайта https://sites.google.com/site/bushmansnetlaboratory/sendbox/stati/database
4:
5: namespace Bushman.CAD.DatabaseServices {
6: /// <summary>
7: /// This enum indicates the current space in the current Database.
8: /// </summary>
9: public enum SpaceEnum {
10: /// <summary>
11: /// The Model space.
12: /// </summary>
13: Model,
14: /// <summary>
15: /// The Layout space.
16: /// </summary>
17: Layout,
18: /// <summary>
19: /// The Model space through the Layout's viewport.
20: /// </summary>
21: Viewport
22: }
23: }
1: // LayoutManagerExtensionMethods.cs
2: // © Andrey Bushman, 2011
3:
4: //Microsoft
5: using System;
6:
7: //Autodesk
8: using cad = Autodesk.AutoCAD.ApplicationServices.Application;
9: using App = Autodesk.AutoCAD.ApplicationServices;
10: using Db = Autodesk.AutoCAD.DatabaseServices;
11: using Ed = Autodesk.AutoCAD.EditorInput;
12:
13: namespace Bushman.CAD.DatabaseServices {
14:
15: /// <summary>
16: /// Методы расширения для класса Autodesk.AutoCAD.DatabaseServices.LayoutManager.
17: /// </summary>
18: public static class LayoutManagerExtensionMethods {
19: /// <summary>
20: /// This is Extension Method for the <c>Autodesk.AutoCAD.DatabaseServices.LayoutManager</c>
21: /// class. It gets the current space in the current Database.
22: /// </summary>
23: /// <param name="mng">Target <c>Autodesk.AutoCAD.DatabaseServices.LayoutManager</c>
24: /// instance.</param>
25: /// <returns>Returns the SpaceEnum value.</returns>
26: public static SpaceEnum GetCurrentSpaceEnum(this Db.LayoutManager mng) {
27: Db.Database db = cad.DocumentManager.MdiActiveDocument.Database;
28: Int16 tilemode = (Int16)cad.GetSystemVariable("TILEMODE");
29:
30: if (tilemode == 1)
31: return SpaceEnum.Model;
32:
33: Int16 cvport = (Int16)cad.GetSystemVariable("CVPORT");
34: if (cvport == 1)
35: return SpaceEnum.Layout;
36: else
37: return SpaceEnum.Viewport;
38: }
39:
40: /// <summary>
41: /// This is Extension Method for the <c>Autodesk.AutoCAD.DatabaseServices.LayoutManager</c>
42: /// class. It gets the name of the current space in the current Database.
43: /// </summary>
44: /// <param name="mng">Target <c>Autodesk.AutoCAD.DatabaseServices.LayoutManager</c>
45: /// instance.</param>
46: /// <returns>Returns the name of current space.</returns>
47: public static String GetCurrentSpaceName(this Db.LayoutManager mng) {
48: SpaceEnum space = GetCurrentSpaceEnum(mng);
49: Db.Database db = cad.DocumentManager.MdiActiveDocument.Database;
50: String modelSpaceLocalizedName = String.Empty;
51: using (Db.Transaction tr = db.TransactionManager.StartTransaction()) {
52: Db.BlockTable bt = tr.GetObject(db.BlockTableId, Db.OpenMode.ForRead)
53: as Db.BlockTable;
54: Db.BlockTableRecord btr = tr.GetObject(bt[Db.BlockTableRecord.ModelSpace],
55: Db.OpenMode.ForRead)
56: as Db.BlockTableRecord;
57: modelSpaceLocalizedName = (tr.GetObject(btr.LayoutId, Db.OpenMode.ForRead)
58: as Db.Layout).LayoutName;
59: }
60: String result = space == SpaceEnum.Viewport ?
61: "Model" as String : mng.CurrentLayout;
62: return result;
63: }
64:
65: /// <summary>
66: /// This is Extension Method for the <c>Autodesk.AutoCAD.DatabaseServices.LayoutManager</c>
67: /// class. It gets the localized name of the Model tab.
68: /// </summary>
69: /// <param name="mng">Target <c>Autodesk.AutoCAD.DatabaseServices.LayoutManager</c>
70: /// instance.</param>
71: /// <returns>Returns the name of current space.</returns>
72: public static String GetModelTabLocalizedName(this Db.LayoutManager mng) {
73: Db.Database db = cad.DocumentManager.MdiActiveDocument.Database;
74: String modelTabLocalizedName = String.Empty;
75: using (Db.Transaction tr = db.TransactionManager.StartTransaction()) {
76: Db.BlockTable bt = tr.GetObject(db.BlockTableId, Db.OpenMode.ForRead)
77: as Db.BlockTable;
78: Db.BlockTableRecord btr = tr.GetObject(bt[Db.BlockTableRecord.ModelSpace],
79: Db.OpenMode.ForRead)
80: as Db.BlockTableRecord;
81: modelTabLocalizedName = (tr.GetObject(btr.LayoutId, Db.OpenMode.ForRead)
82: as Db.Layout).LayoutName;
83: }
84: return modelTabLocalizedName;
85: }
86: }
87: }
1: // DatabaseExtensionMethods.cs
2: // © Andrey Bushman, 2011
3: // За основу взят код с сайта https://sites.google.com/site/bushmansnetlaboratory/sendbox/stati/database
4:
5: // Microsoft
6: using System;
7: using System.Collections.Generic;
8: using System.Collections;
9: using System.Linq;
10: using System.Text;
11: using System.ComponentModel;
12:
13: // Autodesk
14: using cad = Autodesk.AutoCAD.ApplicationServices.Application;
15: using App = Autodesk.AutoCAD.ApplicationServices;
16: using Db = Autodesk.AutoCAD.DatabaseServices;
17: using Ed = Autodesk.AutoCAD.EditorInput;
18: using Rtm = Autodesk.AutoCAD.Runtime;
19:
20: namespace Bushman.CAD.DatabaseServices {
21: /// <summary>
22: /// Методы расширения для класса Autodesk.AutoCAD.DatabaseServices.Database.
23: /// </summary>
24: public static class DatabaseExtensionMethods {
25:
26: /// <summary>
27: /// Проверка корректности хэндла, а так же полученного на его основе ObjectId.
28: /// </summary>
29: /// <param name="h">Проверяемый хэндл</param>
30: /// <param name="id">ссылка на ObjectId, в котором следует сохранить результат
31: /// в случае успешной проверки</param>
32: /// <returns>true - проверка прошла успешно, иначе - false</returns>
33: public static bool TryGetValidObjectId(this Db.Database database, Db.Handle h,
34: ref Db.ObjectId id) {
35: Boolean result = database.TryGetObjectId(h, out id);
36: if (result && id.IsValid)
37: return result;
38: else {
39: id = Db.ObjectId.Null;
40: return false;
41: }
42: }
43:
44: /// <summary>
45: /// Получить идентификаторы всех объектов, унаследованных от DBObject, имеющихся в базе
46: /// данных чертежа
47: /// </summary>
48: /// <param name="status">Какие именно объекты (удалённые/не удалённые/все) следует
49: /// выбирать</param>
50: /// <returns>Возвращается массив ObjectId[]</returns>
51: public static Db.ObjectId[] GetAll(this Db.Database database,
52: DBObjectStatus status) {
53: bool? a = null;
54: switch (status) {
55: case DBObjectStatus.Any:
56: a = null;
57: break;
58: case DBObjectStatus.Erased:
59: a = true;
60: break;
61: case DBObjectStatus.NotErased:
62: a = false;
63: break;
64: }
65: return GetData<Db.ObjectId>(database, (n, x) => !a.HasValue ?
66: true : x.IsErased == (bool)a,
67: (n, m) => m);
68: }
69:
70: /// <summary>
71: /// Выбрать данные из базы данных
72: /// </summary>
73: /// <typeparam name="R">Тип объектов, возвращаемых в массиве</typeparam>
74: /// <param name="requirement">Условие выборки объектов</param>
75: /// <param name="result">Правило построения результирующего объекта</param>
76: /// <returns>Возвращается массив объектов R</returns>
77: public static R[] GetData<R>(this Db.Database database, Func<Db.Transaction,
78: Db.ObjectId, Boolean> requirement, Func<Db.Transaction, Db.ObjectId, R> result) {
79: List<R> primitives = new List<R>();
80: using (Db.Transaction tr = database.TransactionManager.StartTransaction()) {
81: for (long i = database.BlockTableId.Handle.Value; i < database.Handseed.Value;
82: i++) {
83: Db.ObjectId id = Db.ObjectId.Null;
84: Db.Handle h = new Db.Handle(i);
85: if (TryGetValidObjectId(database, h, ref id) && requirement(tr, id))
86: primitives.Add(result(tr, id));
87: }
88: }
89: return primitives.ToArray();
90: }
91:
92: /// <summary>
93: /// Получить словарь идентификаторов объектов, соответствующих заданному условию.
94: /// Результирующая выборка будет представлена в виде словаря и сгруппирована в списки
95: /// по именам классов (берётся из ObjectId.ObjectClass.Name) которые, в свою очередь,
96: /// выступают в роли ключей. Т.о. можно быстро выбирать все объекты нужного типа,
97: /// соответствующие заданному условию для дальнейшей их обработки.
98: /// </summary>
99: /// <param name="requirement">Условие выборки объектов</param>
100: /// <returns>Возвращается словарь, у которого в качестве ключа используется имя класса
101: /// (берётся из ObjectId.ObjectClass.Name), а в качестве значения - список, содержащий
102: /// в себе идентификаторы объектов этого класса</returns>
103: public static Dictionary<String, List<Db.ObjectId>> GetByTypes(this Db.Database database,
104: Func<Db.Transaction, Db.ObjectId, Boolean> requirement) {
105: Dictionary<string, List<Db.ObjectId>> dict = new Dictionary<String, List<Db.ObjectId>>();
106: using (Db.Transaction t = database.TransactionManager.StartTransaction()) {
107: for (Int64 i = database.BlockTableId.Handle.Value; i < database.Handseed.Value;
108: i++) {
109: Db.ObjectId id = Db.ObjectId.Null;
110: Db.Handle h = new Db.Handle(i);
111: if (TryGetValidObjectId(database, h, ref id) && requirement(t, id)) {
112: string type = id.ObjectClass.Name;
113: if (!dict.Keys.Contains(type))
114: dict.Add(type, new List<Db.ObjectId>());
115: dict[type].Add(id);
116: }
117: }
118: }
119: return dict;
120: }
121:
122: /// <summary>
123: /// Сгруппировать идентификаторы (ObjectId) объектов по именам их классов (имена берутся из
124: /// ObjectId.ObjectClass.Name).
125: /// </summary>
126: /// <param name="ids">Исходный массив данных</param>
127: /// <returns>Возвращается словарь, у которого в качестве ключа используется имя класса
128: /// (берётся из ObjectId.ObjectClass.Name), а в качестве значения - список, содержащий
129: /// в себе идентификаторы объектов этого класса</returns>
130: public static Dictionary<String, List<Db.ObjectId>> GroupByTypes(this Db.Database database,
131: Db.ObjectId[] ids) {
132: Dictionary<String, List<Db.ObjectId>> dict = new Dictionary<String, List<Db.ObjectId>>();
133: foreach (Db.ObjectId id in ids) {
134: string type = id.ObjectClass.Name;
135: if (!dict.Keys.Contains(type))
136: dict.Add(type, new List<Db.ObjectId>());
137: dict[type].Add(id);
138: }
139: return dict;
140: }
141:
142: /// <summary>
143: /// Выполнить указанное действие над обозначенным набором примитивов.
144: /// </summary>
145: /// <param name="ids">Идентификаторы объектов, подлежащих модификации.</param>
146: /// <param name="action">Действие, которое необходимо выполнить над каждым из указанных
147: /// объектов.</param>
148: public static void Action(this Db.Database database, Db.ObjectId[] ids,
149: Action<Db.Transaction, Db.ObjectId> action) {
150: using (Db.Transaction tr = database.TransactionManager.StartTransaction()) {
151: foreach (Db.ObjectId item in ids)
152: action(tr, item);
153: tr.Commit();
154: }
155: }
156: }
157: }
Комментариев нет:
Отправить комментарий