Не выделяются, несколько линий в подряд.
Всем добрый вечер. Такая проблема. Не могу выделить несколько линий в подряд в AutoCad 2010. То есть выделяю первую линию, выделяю потом вторую и выделение первой слетает. Где можно настроить данную штуковину, простите за нубизм Спасибо.
Просмотров: 16814
LISP, C# (ACAD 200[9,12,13,14])
Регистрация: 25.08.2003
С.-Петербург
Сообщений: 39,810
__________________
Моя библиотека lisp-функций
—
Обращение ко мне — на «ты».
Все, что сказано — личное мнение.
Кулик Алексей aka kpblc |
Посмотреть профиль |
Посетить домашнюю страницу Кулик Алексей aka kpblc |
Найти ещё сообщения от Кулик Алексей aka kpblc |
Выделение, выбор объектов в Автокад с помощью диалогового окна «Быстрый выбор»
Мы рассмотрели выделение (выбор) объектов в Автокад обычными способами, а также специальными. В этой статье мы рассмотрим выбор (выделение) объектов в Автокад по их свойствам, а также их исключение из текущего набора объектов по их свойствам при помощи диалогового окна «Быстрый выбор» (фильтрация текущего набора объектов в Автокад по их свойствам). К свойствам объектов можно отнести цвет, тип, вес линий, масштаб линий, печатаемые и непечатаемые объект и т.д. Свойствам объектов посвящен целый раздел видео курса Автокад.
Прежде чем приступить к изучению диалогового окна в Автокад «Быстрый выбор», вспомним, что такое новый набор объектов в Автокад, текущий набор и системная переменная «Pickadd»
Новый, текущий наборы объектов в Автокаде
Выделим объект «отрезок» щелчком мыши. Только что мы создали новый набор объектов в Автокад, состоящий из одного отрезка. Так как на чертеже Автокад мы уже имеем выделенный объект «Отрезок», то из состояния нового набора объектов он переходит в состояние текущего набора объектов Автокад. Каждое последующие выделение объектов в Автокад будет создавать новый набор объектов, если, конечно, они не будут включаться в текущий набор объектов. Например, выберем в Автокад следующий объект (отрезок) щелчком мыши по нему. Новый выбранный объект (отрезок) в Автокад не создал новый набор объектов, а добавился к текущему набору объектов (первому отрезку). За включение новых выбранных объектов в Автокад в текущий набор отвечает системная переменная «Pickadd» — курсы дизайн интерьера..
Системная переменная Pickadd в Автокад или выделение нескольких объектов в Автокад поочередно
То есть будут ли выделяться несколько объектов в Автокад поочередно, а другими словами добавляться последующие выбранные объекты к текущему набору выбранных объектов в Автокад — это зависит от системной переменной «Pickadd».
По умолчанию системная переменная «Pickadd» в Автокад имеет значение 1. Всего значений этой системной переменной — три (0, 1, 2).
Значение системной переменной в Автока «Pickadd» = 0.
Отключает системную переменную Pickadd. Последние выбранные индивидуально или с помощью рамки объекты становятся новым набором. Ранее выбранные объекты исключаются из выбранного набора.
Значение системной переменной Pickadd = 1 в Автокад (значение по умолчанию).
Включает системную переменную Pickadd. Последние выбранные индивидуально или с помощью рамки объекты добавляются в текущий набор.
Значение системной переменной Pickadd = 2 в Автокад.
Включает системную переменную Pickadd. Последние выбранные индивидуально или с помощью рамки объекты добавляются в текущий набор. Если используется команда «Выбрать, сохраняет выбранные объекты после завершения команды.
Снимем выделение в Автокад всех объектов (2 отрезков) нажатием на клавишу «Esc».
Системная переменная Выборстиль в Автокаде (способы изменения значений системной переменной Pickadd)
Существует несколько способов изменения значений системной переменной «Pickadd» в Автокад. Самый надежный способ — это прописать наименование системной переменной в командной строке «Выборстиль».
Системная переменная «Выборстиль» в Автокад позволяет вызвать системную переменную «Pickadd» и изменить ее значения (это название самой системной переменной Pickadd в русском Автокад).
Пропишем наименование системной переменной «Выборстиль» в командной строке и нажмем «Enter».
В командной строке появится запрос на ввод нового значения системной переменной:
Новое значение Pickadd :
В треугольных скобках стоит значение переменной «Pickadd» в Автокад по умолчанию равное 1. Введем новое значение системной переменной в Автокад равное 0 и нажмем «Enter». Теперь она имеет значение 0.
Выделим объект (отрезок) в Автокад щелчком мыши по нему. Затем выберем следующий объект в Автокад, пусть будет тоже отрезок. Второй отрезок создал новый набор выделенных объектов в Автокад, а не добавился к текущему набору объектов, т.е. к первому отрезку. С первого отрезка снялось выделение.
В Автокад с системной переменной «Pickadd» мы сталкивались, когда изучали окно Свойства, поэтому изменить ее значения (0, 1) можно в этом окне. Откроем окно-панель «Свойства». Щелкните правой кнопкой мыши на свободном месте графической зоны чертежа Автокад, и в контекстном меню выберите строку «Свойства» (выделение со второго отрезка не должно быть снято).
В верхнем левом углу окна «Свойства» в Автокад прописано название выбранного объекта (Отрезок), а правее находится кнопка системной переменной Pickadd в Автокад.
Переключение системной переменной Автокад «Pickadd» со значения 0 на 1, а также обратно в окне Свойства, производится щелчком мыши.
Значение системной переменной Pickadd = 0 в Автокад — нижний квадрат в кнопке не закрашен, в правом углу стрелка, указывающая на верхний закрашенный квадрат. Обозначение стрелки гласит, что каждый последующий выбор объектов будет создавать новый набор объектов.
Значение системной переменной Pickadd = 1 в Автокад — оба квадрата в кнопке закрашены, в правом углу знак «плюс», обозначающий, что каждый последующий выбор объектов в Автокад будет включаться к текущему набору объектов.
Присвоение нового значения 2 системной переменной «Pickadd» возможно только при помощи системной переменной «Выборстиль» в Автокад.
Команда Выбрать в Автокад
Значение 2 для системной переменной Pickadd необходимо только для работы команды Выбрать. Так как выбор объектов в Автокад по умолчанию осуществим обычным щелчком прицела-курсора, то многие пользователи системы не знают, что существует специальная команда Выбрать в Автокад для выбора объектов.
Вызовем системную переменную Выборстиль в Автокад и изменим значение системной переменной Pickadd на 2.
Вызовем команду Выбрать в Автокад, прописав наименование команды в командной строке и нажав Enter. Появился отмечающий указатель в Автокад. С его помощью выберем несколько объектов секущей рамкой и нажмем Enter. Таким способом мы выделили несколько объектов в Автокад.
Данный способ выделения объектов в Автокад работает только в значении 2 системной переменной Pickadd.
Диалоговое окно Быстрый выбор в Автокаде, команда БВыбор
Вызвать диалоговое окно «Быстрый выбор» в Автокад можно несколькими способами:
- Прописать наименование команды в командной строке «БВыбор» в Автокад.
- Щелкнуть правой кнопкой мыши в свободном месте графической зоны чертежа и в контекстном меню выбрать строку в Автокад «Быстрый выбор. «
- Открыть окно Свойства и в верхнем правом углу щелкнуть по кнопке «Быстрый выбор» в Автокад.
Рассмотрим диалоговое окно Быстрый выбор в Автокад.
1) Раскрывающийся список «Применить» окна «Быстрый выбор» содержит два параметра:
- Ко всему чертежу.
- К текущему набору объектов.
Ко всему чертежу — быстрый выбор объектов в Автокад по их свойствам и значениям во всем чертеже.
К текущему набору объектов — работа с объектами (их фильтрация) в текущем наборе объектов.
2) Справа от раскрывающегося списка «Применить» находится кнопка Автокад «Выбрать объекты» (не активна при установке флажка «Добавить в текущий набор»). Кнопка в Автокад «Выбрать объекты» позволяет временно закрыть диалоговое окно «Быстрый выбор» для выбора в текущем чертеже Автокад одного или группы (нескольких) объектов, к которым требуется применить критерии фильтрации. Таким образом, вы можете заранее не выделять объекты в Автокад для дальнейшей их фильтрации через диалоговое окно «Быстрый выбор».
3) Раскрывающийся список «Тип объектов» окна «Быстрый выбор» в Автокад показывает все типы объектов, присутствующие на чертеже или в текущем наборе объектов. С помощью данного списка можно выбирать из чертежа или исключать из текущего набора объекты по их типу. Для 3D тел, поверхностей в Автокад 3D нет разграничений по их типу: 3D тело выдавливания, 3D тело сдвига и т.д. В раскрывающемся списке можно выбрать только один тип объекта в Автокад или сразу все — параметр «Несколько».
4) Поле «Свойства» диалогового окна «Быстрый выбор» позволяет производить выбор объектов в Автокад или исключать (фильтровать) объекты в текущем наборе объектов по их свойствам. Набор свойств в данном поле зависит от выбранного типа (ов) объектов в поле «Тип объектов».
5) Раскрывающийся список «Оператор окна «Быстрый выбор» в Автокад придает значение выбранному ранее свойству, кроме оператора «Выбрать все». Доступны следующие операторы: «= Равно, <> Не равно, > Больше, < Меньше, Выбрать все". В Автокад при выборе оператора "Выбрать все" диалогового окна "Быстрый выбор" исключается фильтрация по свойствам объекта.
6) Раскрывающийся список «Значение» позволяет задать значение выбранного свойства.
7) Поле «Отобранные объекты» в Автокад окна «Быстрый выбор» содержит два значения переключателя:
- Включить в новый набор.
- Исключить из нового набора.
В значении «Включить в новый набор» в Автокад позволяет включить все объекты на текущем чертеже Автокад, удовлетворяющие заданным условиям, в новый набор объектов.
В Автокад в значении «Исключить из нового набора» позволяет исключить все объекты на текущем чертеже, удовлетворяющие заданным условиям, из нового набора (новый набор будет состоять из всех объектов текущего чертежа, кроме исключенных при помощи «Быстрого выбора» в Автокад).
8) Флажок «Добавить в текущий набор» объектов в Автокад диалогового окна «Быстрый выбор» позволяет добавить объекты, удовлетворяющие заданным условиям, к текущему набору объектов.
Практические работы с диалоговым окном Быстрый выбор в Автокад
Как выделить весь чертеж (все объекты чертежа) в Автокад с помощью команды БВыбор
- Снимем все выделения объектов в Автокад нажатием на клавишу «Esc» или щелчком правой кнопкой мыши на свободном месте графической зоны чертежа, выберите из контекстного меню строку «Отменить выбор» в Автокад.
- Вызываем диалоговое окно «Быстрый выбор» в Автокад любым из перечисленных способов.
- Применить — Ко всему чертежу.
- Тип объектов — Несколько.
- Оператор — Выбрать все.
- Отобранные объекты — Включить в новый набор Автокад.
- Нажимаем кнопку ОК.
не удаляется объект в максе
У кого-нибудь было такое, что импортируемый объект невозможно удалить? У меня уже невпервый раз, спасалась тем, что переносила всю сцену. Может есть способ получше?
На сайте c 28.12.2007
Сообщений: 1541
Москва
нет не было такого никогда.
Возможно он у вас заморожен
Возможно он у вас референсный
Возможно он у вас Shape, а в фильтре выделения стоит Geometry (или еще чтонить по аналогии)
Возможно он у вас попал в замороженый слой.
Пока все, что вспомнил.
На сайте c 16.09.2009
Сообщений: 448
у меня было с xref объектами такое. оно?
а если простой объект, его в списке видно? cntrl+a>del решает проблему?
На сайте c 17.09.2012
Сообщений: 124
Ялта
МюФ
Что значит xref объекты?
У меня объекты выделяются, но не удаляются.
На сайте c 15.01.2009
Сообщений: 144
оттуда
Главное меню — References — там xref scene ищешь, тебе список открывается и должен быть в списке твой объект. Выделяешь, удаляешь. пробуй
На сайте c 17.11.2012
Сообщений: 27
МюФ
У меня объекты выделяются, но не удаляются.
Фильтр находится в панели инструментов с левой стороны выкидной список.Но если объекты у вас выделяются-значит версия с фильтром отпадает.Фильтр фильтрует-что можно выделять, а что нельзя.
На сайте c 08.01.2008
Сообщений: 75
а версия макса какая?
На сайте c 11.12.2011
Сообщений: 782
Первый раз такую проблему вижу! Но думаю «Бяка» прав! Либо это всё что он перечислил либо глюк! А по поводу мкс рефа могу сразу сказать что нет т.к. его сначала нужно импортировать как икс реф а потом уже думать что и как! А так как человек им не пользовался и не знает сто это такое то и врятли он его несколько раз как икс реф кинул
На сайте c 15.02.2009
Сообщений: 257
Первый раз такую проблему вижу! Но думаю «Бяка» прав! Либо это всё что он перечислил либо глюк! А по поводу мкс рефа могу сразу сказать что нет т.к. его сначала нужно импортировать как икс реф а потом уже думать что и как! А так как человек им не пользовался и не знает сто это такое то и врятли он его несколько раз как икс реф кинул
Если вставлять файлы в макс перетаскиванием из папки в окно макса, то появляется выбор из трех вариантов, открыть, мержнуть, и хрефнуть. Возможно автор именно так и делал и впоследствии промахнулся нажав на хреф =)
На сайте c 11.12.2011
Сообщений: 782
Первый раз такую проблему вижу! Но думаю «Бяка» прав! Либо это всё что он перечислил либо глюк! А по поводу мкс рефа могу сразу сказать что нет т.к. его сначала нужно импортировать как икс реф а потом уже думать что и как! А так как человек им не пользовался и не знает сто это такое то и врятли он его несколько раз как икс реф кинул
Если вставлять файлы в макс перетаскиванием из папки в окно макса, то появляется выбор из трех вариантов, открыть, мержнуть, и хрефнуть. Возможно автор именно так и делал и впоследствии промахнулся нажав на хреф =)
Это да. Но незнаю как можно промахнуться несколько раз подряд делая мердж
Согласен 1.2 раза. Но не думаю что автор так промахивался.
Создание плагинов для AutoCAD с помощью .NET API (часть 6 – поиск и изменение объектов на чертеже)
Это шестая часть цикла про разработку плагинов для AutoCAD. В ней поговорим про поиск объектов на чертеже, а также про их изменение.
public static string disclaimer = "Автор не является профессиональным разработчиком и не обладает глубокими знаниями AutoCAD. Этот пост – просто небольшой рассказ о создании плагина.";
Введение
В первой части статьи мы рассмотрим поиск объектов на чертеже, во второй — коротко поговорим про их изменение. Однако прежде чем начать рассмотрение этих вопросов, давайте подготовим каркас тестового примера, который мы будем использовать на протяжении всей статьи.
- на нулевой слой: три линии, полилинию, окружность, текст;
- на слой «layer-1»: две линии, окружность, текст, вхождение блока «block-1»;
- на слой «layer-2»: линию, дугу, окружность, вхождение блока «block-1», вхождение блока «block-2».
Выбор не слишком стандартных цветов обусловлен тем, что подготавливать примеры кода к статьям — тоска смертная, и хочется хоть как-то этот процесс оживить.
Раскрасив первые линии, я вдруг вспомнил про Радужный флаг и подумал даже, не вставить ли его в статью…
Но во-вторых, я не так уж сильно поддерживаю это движение, а во-первых, флаг — это сильно сложнее трех линеек. Ну его нафиг.
Код, реализующий этот план действий, приведен далее. Все выполняемые операции: создание слоев и определений блока, вставка текста, графических примитивов и вхождений блока — мы уже рассматривали в прошлых статьях цикла. Единственное, что еще не рассматривалось, — это задание слоя и цвета для объекта чертежа, но оно занимает буквально одну строку кода и, я надеюсь, сложностей не вызовет.
Итак, создаем проект, выполняем первоначальную настройку (указываем версию .NET, отключаем CopyLocal ) и подключаем уже привычные библиотеки AcMgd и AcDbMgd. Далее помещаем туда функции, создающие объекты для нашего примера.
using System; using System.Collections.Generic; using Autodesk.AutoCAD.Runtime; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.EditorInput; using acad = Autodesk.AutoCAD.ApplicationServices.Application; namespace HabrPlug_SearchAndRescue < public class ClassMyAutoCADDLL_SearchAndRescue < public class Commands : IExtensionApplication < // используемые цвета Autodesk.AutoCAD.Colors.Color color_Pink = Autodesk.AutoCAD.Colors.Color.FromRgb(255, 128, 255); Autodesk.AutoCAD.Colors.Color color_Blue = Autodesk.AutoCAD.Colors.Color.FromRgb(0, 200, 255); Autodesk.AutoCAD.Colors.Color color_LightGreen = Autodesk.AutoCAD.Colors.Color.FromRgb(128, 255, 64); // ID слоев "layer-1" и "layer-2" ObjectId layer_1; ObjectId layer_2; // создаем слои public void createLayers() < // получаем текущий документ и его БД Document acDoc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument; Database acCurDb = acDoc.Database; // блокируем документ using (DocumentLock docloc = acDoc.LockDocument()) < // начинаем транзакцию using (Transaction tr = acCurDb.TransactionManager.StartTransaction()) < // открываем таблицу слоев документа LayerTable acLyrTbl = tr.GetObject(acCurDb.LayerTableId, OpenMode.ForWrite) as LayerTable; // создаем новый слой и задаем ему имя LayerTableRecord acLyrTblRec_1 = new LayerTableRecord(); acLyrTblRec_1.Name = "layer-1"; // заносим созданный слой в таблицу слоев, сохраняем ID созданной записи слоя layer_1 = acLyrTbl.Add(acLyrTblRec_1); // добавляем созданный слой в документ tr.AddNewlyCreatedDBObject(acLyrTblRec_1, true); // создаем новый слой и задаем ему имя LayerTableRecord acLyrTblRec_2 = new LayerTableRecord(); acLyrTblRec_2.Name = "layer-2"; // заносим созданный слой в таблицу слоев, сохраняем ID созданной записи слоя layer_2 = acLyrTbl.Add(acLyrTblRec_2); // добавляем созданный слой в документ tr.AddNewlyCreatedDBObject(acLyrTblRec_2, true); // фиксируем транзакцию tr.Commit(); >> > // создаем определение блока "block-1" public void createBlock_1() < // получаем ссылки на документ и его БД Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; // имя создаваемого блока const string blockName = "block-1"; // начинаем транзакцию Transaction tr = db.TransactionManager.StartTransaction(); using (tr) < // открываем таблицу блоков на запись BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForWrite); // проверяем, нет ли в таблице блока с таким именем; если есть - заканчиваем выполнение команды if (bt.Has(blockName)) < return; >// создаем новое определение блока, задаем ему имя BlockTableRecord btr = new BlockTableRecord(); btr.Name = blockName; // добавляем созданное определение блока в таблицу блоков и в транзакцию bt.Add(btr); tr.AddNewlyCreatedDBObject(btr, true); // добавляем к блоку элементы // создаем окружность Circle acCircle = new Circle(); // устанавливаем параметры созданного объекта acCircle.SetDatabaseDefaults(); acCircle.Center = Point3d.Origin; acCircle.Radius = 25; // добавляем созданный объект в определение блока и в транзакцию btr.AppendEntity(acCircle); tr.AddNewlyCreatedDBObject(acCircle, true); // создаем линию Line acLine = new Line(new Point3d(18, 18, 0), new Point3d(35, 35, 0)); // устанавливаем параметры созданного объекта равными параметрам по умолчанию acLine.SetDatabaseDefaults(); // добавляем созданный объект в определение блока и в транзакцию btr.AppendEntity(acLine); tr.AddNewlyCreatedDBObject(acLine, true); // создаем полилинию Polyline acPolyline = new Polyline(); // устанавливаем параметры созданного объекта равными параметрам по умолчанию acPolyline.SetDatabaseDefaults(); // добавляем к полилинии вершины acPolyline.AddVertexAt(0, new Point2d(20, 35), 0, 0, 0); acPolyline.AddVertexAt(1, new Point2d(35, 35), 0, 0, 0); acPolyline.AddVertexAt(2, new Point2d(35, 20), 0, 0, 0); // добавляем созданный объект в определение блока и в транзакцию btr.AppendEntity(acPolyline); tr.AddNewlyCreatedDBObject(acPolyline, true); // фиксируем транзакцию tr.Commit(); > > // создаем определение блока "block-2" public void createBlock_2() < // получаем ссылки на документ и его БД Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; // имя создаваемого блока const string blockName = "block-2"; // начинаем транзакцию Transaction tr = db.TransactionManager.StartTransaction(); using (tr) < // открываем таблицу блоков на запись BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForWrite); // проверяем, нет ли в таблице блока с таким именем; если есть - заканчиваем выполнение команды if (bt.Has(blockName)) < return; >// создаем новое определение блока, задаем ему имя BlockTableRecord btr = new BlockTableRecord(); btr.Name = blockName; // добавляем созданное определение блока в таблицу блоков и в транзакцию bt.Add(btr); tr.AddNewlyCreatedDBObject(btr, true); // добавляем к блоку элементы // создаем окружность Circle acCircle = new Circle(); // устанавливаем параметры созданного объекта acCircle.SetDatabaseDefaults(); acCircle.Center = Point3d.Origin; acCircle.Radius = 25; // добавляем созданный объект определение блока и в транзакцию btr.AppendEntity(acCircle); tr.AddNewlyCreatedDBObject(acCircle, true); // создаем первую линию Line acLine_1 = new Line(new Point3d(0, -25, 0), new Point3d(0, -50, 0)); // устанавливаем параметры созданного объекта равными параметрам по умолчанию acLine_1.SetDatabaseDefaults(); // добавляем созданный объект в определение блока и в транзакцию btr.AppendEntity(acLine_1); tr.AddNewlyCreatedDBObject(acLine_1, true); // создаем вторую линию Line acLine_2 = new Line(new Point3d(-7, -39, 0), new Point3d(7, -39, 0)); // устанавливаем параметры созданного объекта равными параметрам по умолчанию acLine_2.SetDatabaseDefaults(); // добавляем созданный объект в определение блока и в транзакцию btr.AppendEntity(acLine_2); tr.AddNewlyCreatedDBObject(acLine_2, true); // фиксируем транзакцию tr.Commit(); > > // создаем объекты на нулевом слое public void layer_0_createObjects() < // получаем текущий документ и его БД Document doc = acad.DocumentManager.MdiActiveDocument; Database db = doc.Database; // начинаем транзакцию using (Transaction tr = db.TransactionManager.StartTransaction()) < // открываем таблицу блоков документа BlockTable acBlkTbl; acBlkTbl = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; // открываем пространство модели (Model Space) - оно является одной из записей в таблице блоков документа BlockTableRecord ms = tr.GetObject(acBlkTbl[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord; // добавляем розовую линию Line acLine_1 = new Line(new Point3d(225, 225, 0), new Point3d(225, 175, 0)); // устанавливаем параметры созданного объекта равными параметрам по умолчанию acLine_1.SetDatabaseDefaults(); // устанавливаем для объекта нужный слой и цвет acLine_1.Layer = "0"; acLine_1.Color = color_Pink; // добавляем созданный объект в пространство модели и в транзакцию ms.AppendEntity(acLine_1); tr.AddNewlyCreatedDBObject(acLine_1, true); // добавляем голубую линию Line acLine_2 = new Line(new Point3d(250, 225, 0), new Point3d(250, 175, 0)); // устанавливаем параметры созданного объекта равными параметрам по умолчанию acLine_2.SetDatabaseDefaults(); // устанавливаем для объекта нужный слой и цвет acLine_2.Layer = "0"; acLine_2.Color = color_Blue; // добавляем созданный объект в пространство модели и в транзакцию ms.AppendEntity(acLine_2); tr.AddNewlyCreatedDBObject(acLine_2, true); // добавляем салатовую линию Line acLine_3 = new Line(new Point3d(275, 225, 0), new Point3d(275, 175, 0)); // устанавливаем параметры созданного объекта равными параметрам по умолчанию acLine_3.SetDatabaseDefaults(); // устанавливаем для объекта нужный слой и цвет acLine_3.Layer = "0"; acLine_3.Color = color_LightGreen; // добавляем созданный объект в пространство модели и в транзакцию ms.AppendEntity(acLine_3); tr.AddNewlyCreatedDBObject(acLine_3, true); // добавляем розовую полилинию Polyline acPolyline = new Polyline(); // устанавливаем параметры созданного объекта равными параметрам по умолчанию acPolyline.SetDatabaseDefaults(); // добавляем к полилинии вершины acPolyline.AddVertexAt(0, new Point2d(300, 225), 0, 0, 0); acPolyline.AddVertexAt(1, new Point2d(325, 175), 0, 0, 0); acPolyline.AddVertexAt(2, new Point2d(350, 225), 0, 0, 0); // устанавливаем для объекта нужный слой и цвет acPolyline.Layer = "0"; acPolyline.Color = color_Pink; // добавляем созданный объект в пространство модели и в транзакцию ms.AppendEntity(acPolyline); tr.AddNewlyCreatedDBObject(acPolyline, true); // добавляем голубую окружность Circle acCircle = new Circle(); // устанавливаем параметры созданного объекта acCircle.SetDatabaseDefaults(); acCircle.Center = new Point3d(400, 200, 0); acCircle.Radius = 25; // устанавливаем для объекта нужный слой и цвет acCircle.Layer = "0"; acCircle.Color = color_Blue; // добавляем созданный объект в пространство модели и в транзакцию ms.AppendEntity(acCircle); tr.AddNewlyCreatedDBObject(acCircle, true); // добавляем салатовый текст DBText text = new DBText(); text.Position = new Point3d(450, 175, 0); text.Height = 50; text.TextString = "HABR!"; // устанавливаем для объекта нужный слой и цвет text.Layer = "0"; text.Color = color_LightGreen; // добавляем созданный объект в пространство модели и в транзакцию ms.AppendEntity(text); tr.AddNewlyCreatedDBObject(text, true); // фиксируем изменения tr.Commit(); >> // создаем объекты на слое "layer-1" public void layer_1_createObjects() < // получаем текущий документ и его БД Document doc = acad.DocumentManager.MdiActiveDocument; Database db = doc.Database; // начинаем транзакцию using (Transaction tr = db.TransactionManager.StartTransaction()) < // запоминаем текущий слой и временно меняем его на нужный нам // (это позволит не задавать слой отдельно для каждого создаваемого объекта) ObjectId currentLayer = db.Clayer; db.Clayer = layer_1; // открываем таблицу блоков документа BlockTable acBlkTbl; acBlkTbl = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; // открываем пространство модели (Model Space) - оно является одной из записей в таблице блоков документа BlockTableRecord ms = tr.GetObject(acBlkTbl[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord; // добавляем розовую линию Line acLine_1 = new Line(new Point3d(225, 25, 0), new Point3d(225, -25, 0)); // устанавливаем параметры созданного объекта acLine_1.SetDatabaseDefaults(); acLine_1.Color = color_Pink; // добавляем созданный объект в пространство модели и в транзакцию ms.AppendEntity(acLine_1); tr.AddNewlyCreatedDBObject(acLine_1, true); // добавляем голубую линию Line acLine_2 = new Line(new Point3d(250, 25, 0), new Point3d(250, -25, 0)); // устанавливаем параметры созданного объекта acLine_2.SetDatabaseDefaults(); acLine_2.Color = color_Blue; // добавляем созданный объект в пространство модели и в транзакцию ms.AppendEntity(acLine_2); tr.AddNewlyCreatedDBObject(acLine_2, true); // добавляем салатовую окружность Circle acCircle = new Circle(); // устанавливаем параметры созданного объекта acCircle.SetDatabaseDefaults(); acCircle.Center = new Point3d(300, 0, 0); acCircle.Radius = 25; acCircle.Color = color_LightGreen; // добавляем созданный объект в пространство модели и в транзакцию ms.AppendEntity(acCircle); tr.AddNewlyCreatedDBObject(acCircle, true); // добавляем розовый текст DBText text = new DBText(); // устанавливаем параметры созданного объекта text.Position = new Point3d(350, -25, 0); text.Height = 50; text.TextString = "HABR!"; text.Color = color_Pink; // добавляем созданный объект в пространство модели и в транзакцию ms.AppendEntity(text); tr.AddNewlyCreatedDBObject(text, true); // добавляем вхождение блока "block-1" // открываем таблицу блоков для чтения BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead); // получаем ObjectID блока ObjectId btrId = bt["block-1"]; // создаем новое вхождение блока, используя полученный ID определения блока BlockReference br = new BlockReference(new Point3d(600, 0, 0), btrId); // добавляем вхождение блока на пространство модели и в транзакцию ms.AppendEntity(br); tr.AddNewlyCreatedDBObject(br, true); // возвращаем обратно старый текущий слой db.Clayer = currentLayer; // фиксируем изменения tr.Commit(); >> // создаем объекты на слое "layer-2" public void layer_2_createObjects() < // получаем текущий документ и его БД Document doc = acad.DocumentManager.MdiActiveDocument; Database db = doc.Database; // начинаем транзакцию using (Transaction tr = db.TransactionManager.StartTransaction()) < // запоминаем текущий слой и временно меняем его на нужный нам // (это позволит не задавать слой отдельно для каждого создаваемого объекта) ObjectId currentLayer = db.Clayer; db.Clayer = layer_2; // открываем таблицу блоков документа BlockTable acBlkTbl; acBlkTbl = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; // открываем пространство модели (Model Space) - оно является одной из записей в таблице блоков документа BlockTableRecord ms = tr.GetObject(acBlkTbl[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord; // добавляем розовую линию Line acLine_1 = new Line(new Point3d(225, -175, 0), new Point3d(225, -225, 0)); // устанавливаем параметры созданного объекта acLine_1.SetDatabaseDefaults(); acLine_1.Color = color_Pink; // добавляем созданный объект в пространство модели и в транзакцию ms.AppendEntity(acLine_1); tr.AddNewlyCreatedDBObject(acLine_1, true); // добавляем голубую дугу Arc acArc = new Arc(new Point3d(250, -200, 0), 25, -45 / 180.0 * Math.PI, 45 / 180.0 * Math.PI); // устанавливаем параметры созданного объекта acArc.SetDatabaseDefaults(); acArc.Color = color_Blue; // добавляем созданный объект в пространство модели и в транзакцию ms.AppendEntity(acArc); tr.AddNewlyCreatedDBObject(acArc, true); // добавляем салатовую окружность Circle acCircle = new Circle(); // устанавливаем параметры созданного объекта acCircle.SetDatabaseDefaults(); acCircle.Center = new Point3d(325, -200, 0); acCircle.Radius = 25; acCircle.Color = color_LightGreen; // добавляем созданный объект в пространство модели и в транзакцию ms.AppendEntity(acCircle); tr.AddNewlyCreatedDBObject(acCircle, true); // добавляем вхождение блока "block-1" // открываем таблицу блоков для чтения BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead); // получаем ObjectID блока ObjectId btrId = bt["block-1"]; // создаем новое вхождение блока, используя полученный ID определения блока BlockReference br = new BlockReference(new Point3d(400, -200, 0), btrId); // добавляем вхождение блока на пространство модели и в транзакцию ms.AppendEntity(br); tr.AddNewlyCreatedDBObject(br, true); // добавляем вхождение блока "block-2" // получаем ObjectID блока btrId = bt["block-2"]; // создаем новое вхождение блока, используя полученный ID определения блока br = new BlockReference(new Point3d(475, -200, 0), btrId); // добавляем вхождение блока на пространство модели и в транзакцию ms.AppendEntity(br); tr.AddNewlyCreatedDBObject(br, true); // возвращаем обратно старый текущий слой db.Clayer = currentLayer; // фиксируем изменения tr.Commit(); >> // создаем объекты при загрузке плагина public void Initialize() < createLayers(); createBlock_1(); createBlock_2(); layer_0_createObjects(); layer_1_createObjects(); layer_2_createObjects(); >// функция Terminate() необходима, чтобы реализовать интерфейс IExtensionApplication public void Terminate() < >> > >
Результат:
Код несложный и принципиальных вопросов вызывать не должен. Давайте быстро пробежимся по некоторым нюансам.
Момент первый: выполнение операций внутри метода Initialize()
Для начала заметим, что создание всех необходимых объектов происходят прямо во время загрузки плагина. Поскольку создаваемые объекты нужны нам для работы примеров и необходимы для работы любой из команд плагина, их вроде бы можно создавать в рамках метода Initialize() .
Однако насколько это правильно и разумно — вопрос очень спорный. Во-первых, создавая что-то автоматически, мы тем самым лишаем пользователя выбора и ставим его перед веселым фактом: привет, чувак, у тебя на чертеже теперь десять новых слоев и сто новых объектов!
Весело будет, но не слишком. Особенно пользователю.
Вторая неприятность состоит в том, что пользователь может случайно или намеренно удалить с чертежа некоторые из созданных объектов. И в этом случае он никак не сможет заново создать эти объекты; для этого ему придется закрыть и заново запустить AutoCAD.
- Никак не помешают пользователю и не создадут излишнюю нагрузку на чертеж.
- Никогда не потребуют повторного вызова (либо же у пользователя должен быть способ этот вызов сделать — вспомните пример из абзаца выше про случайное удаление объектов чертежа).
Сейчас с содроганием зашел в репозиторий с кодом реального проекта, зажмурился, открыл глаза…
Уф, все в порядке. Изложенные выше правила почти соблюдены.
…
Как там говорится-то? «Кто не может работать — учит»?
Момент второй: задание слоя объектов двумя способами
При заполнении объектами нулевого слоя (функция layer_0_createObjects() ) принадлежность к этому слою явно указывается для каждого создаваемого объекта. Пример:
acLine_1.Layer = "0";
Это понятно и несложно, однако при работе с большим количеством объектов можно запросто забыть задать слой, что нехорошо.
Пример из жизни: автор статьи добросовестно скопипастил в функцию layer_1_createObjects() код для вставки на чертеж линии из функции layer_0_createObjects() , забыв поменять слой на новый. Поиски ошибки и ее исправление заняли какое-то время. Осознав, что так придется делать со всеми примитивами, автор выбесился и решил все сделать другим способом.
Так родилась эта часть статьи.)
Альтернативный способ, уменьшающий вероятность подобной ошибки, был кратко упомянут в одной из предыдущих статей, посвященной слоям. Это использование свойства Clayer .
Суть способа состоит в том, что если при создании объекта мы вообще не укажем ему слой, то он будет присвоен автоматически на основе значения свойства Clayer базы данных текущего документа. Просмотреть это значение можно так:
Document doc = acad.DocumentManager.MdiActiveDocument; Database db = doc.Database; ObjectId currentLayer = db.Clayer;
Присваивается значение аналогично:
Document doc = acad.DocumentManager.MdiActiveDocument; Database db = doc.Database; db.Clayer = layer_1; // layer_1 - ObjectID нужного слоя
А вот так можно получить ObjectID слоя, зная его имя:
Document acDoc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument; Database acCurDb = acDoc.Database; using (DocumentLock docloc = acDoc.LockDocument()) < using (Transaction tr = acCurDb.TransactionManager.StartTransaction()) < LayerTable acLyrTbl = tr.GetObject(acCurDb.LayerTableId, OpenMode.ForWrite) as LayerTable; ObjectId layer_objID = acLyrTbl["someLayer"] >>
В нашем примере, чтобы не обращаться каждый раз к таблице слоев, я создал две глобальные переменные — layer_1 и layer_2 , в которые сохраняю ObjectID слоев «layer-1» и «layer-2» при их создании. А позже, в процедурах layer_1_createObjects() и layer_2_createObjects() , я использую следующую конструкцию:
// . ObjectId currentLayer = db.Clayer; db.Clayer = layer_1; // . // ДОБАВЛЯЕМ НОВЫЕ ОБЪЕКТЫ // . db.Clayer = currentLayer; // .
Зачем нужно сохранять текущее значение Clayer и восстанавливать его в конце функции?
Ну, одна из причин — чтобы пользователь после работы нашей функции смог продолжить добавлять объекты на тот же слой, что и до работы нашей процедуры.
Вторая причина — чтобы самому не путаться и не выяснять потом, почему это в половине запусков объекты добавляются не туда.
Если подключить немого воображения, то, наверное, можно придумать и еще причины. Но зачем?
Момент третий: техника безопасности
Источник: «Ривелти групп»
Хотелось бы еще раз сделать акцент на том, что этот цикл статей рассказывает об основах создания плагинов. Для использования в реальном проекте приведенные примеры должны быть доработаны — в частности, необходимо позаботиться о том, что все используемые объекты реально существуют.
В нашем примере такие проверки не проводятся. Посмотрите: я помещаю графические объекты на созданные ранее слои, помещаю на чертеж вхождения созданных при загрузке блоков — но нигде не проверяю, что эти слои и определения блоков существуют. В реальных проектах пренебрегать такими проверками настоятельно не рекомендуется. Особенно это касается случаев, когда между получением ссылки на объект и операцией над объектом могут произойти какие-нибудь действия пользователя.
В разделе, посвященном редактированию объектов чертежа, я опишу еще несколько ошибок, которые также необходимо отслеживать.
Теперь, когда мы обсудили особенности примера, можно наконец-то перейти к поиску объектов на чертеже. Мы разберем два разных подхода: просмотр объектов чертежа через обращение к объекту ModelSpace и получение идентификаторов объектов чертежа с помощью метода Editor.SelectAll() .
Можно искать объекты и другими способами. Но тут я уже ничем не помогу, ибо лично с ними не сталкивался.
Ближе к концу этой статьи будет ма-аленький бонусный нанораздел, где можно будет оценить масштабы извращений блестящих технических решений, которые приходится использовать людям программистам для оптимизации работы с большим количеством объектов.
1 Поиск объектов чертежа с помощью обращения к объекту ModelSpace
1.1 Итерация по всем объектам чертежа
Для начала давайте посмотрим, как можно перебрать в цикле все объекты, имеющиеся на чертеже.
Принцип простой: открываем пространство модели ( ModelSpace ) и получаем ссылки на все объекты внутри него. Затем приводим эти объекты к типу Entity и обрабатываем нужные нам свойства.
Пример почти полностью заимствован отсюда (англ.).
[CommandMethod("Habr_IterateThroughAllObjects_1")] public void iterateThroughAllObjects() < // получаем текущую БД Database db = HostApplicationServices.WorkingDatabase; // начинаем транзакцию using (Transaction tr = db.TransactionManager.StartTransaction()) < // получаем ссылку на пространство модели (ModelSpace) BlockTableRecord ms = (BlockTableRecord)tr.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForRead); // "пробегаем" по всем объектам в пространстве модели foreach (ObjectId id in ms) < // приводим каждый из них к типу Entity Entity entity = (Entity)tr.GetObject(id, OpenMode.ForRead); // выводим в консоль слой (entity.Layer), тип (entity.GetType().ToString()) и цвет (entity.Color) каждого объекта acad.DocumentManager.MdiActiveDocument.Editor.WriteMessage(string.Format("\nLayer:; Type:; Color: ,,\n", entity.Layer, entity.GetType().ToString(), entity.Color.Red.ToString(), entity.Color.Green.ToString(), entity.Color.Blue.ToString())); > tr.Commit(); > >
Результат:
Результат, как можно видеть, полностью совпадает с ожиданиями: у нас 16 объектов, цвета которых чередуются.
Из небольших новшеств: в этом примере мы использовали незнакомое нам свойство WorkingDatabase класса HostApplicationServices . Оно позволяет получить БД документа, который активен (имеет фокус ввода) в настоящий момент. Еще одно новшество — метод GetBlockModelSpaceId() класса SymbolUtilityServices , который позволяет быстро получить ObjectId пространства модели ( ModelSpace ).
Вместо новых классов мы, безусловно, могли бы использовать уже знакомые:
// команда для итерации по объектам (подход 1) [CommandMethod("Habr_IterateThroughAllObjects_1")] public void iterateThroughAllObjects() < // получаем текущий документ и его БД Document doc = acad.DocumentManager.MdiActiveDocument; Database db = doc.Database; // начинаем транзакцию using (Transaction tr = db.TransactionManager.StartTransaction()) < // получаем ссылку на пространство модели (ModelSpace) // открываем таблицу блоков документа BlockTable acBlkTbl = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; // открываем пространство модели (Model Space) - оно является одной из записей в таблице блоков документа BlockTableRecord ms = tr.GetObject(acBlkTbl[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord; // "пробегаем" по всем объектам в пространстве модели foreach (ObjectId id in ms) < // приводим каждый из них к типу Entity Entity entity = (Entity)tr.GetObject(id, OpenMode.ForRead); // выводим в консоль слой (entity.Layer), тип (entity.GetType().ToString()) и цвет (entity.Color) каждого объекта acad.DocumentManager.MdiActiveDocument.Editor.WriteMessage(string.Format("\nLayer:; Type:; Color: ,,\n", entity.Layer, entity.GetType().ToString(), entity.Color.Red.ToString(), entity.Color.Green.ToString(), entity.Color.Blue.ToString())); > tr.Commit(); > >
Никакой разницы.
С краткой хвалебной одой классу SymbolUtilityServices можно ознакомиться здесь (англ.).
Получив ссылку на пространство модели, мы просто пробегаемся по всем его объектам, приводим их к типу Entity (чтобы можно было просматривать их свойства) и выводим в консоль данные о слое, типе и цвете очередного объекта.
1.2 Поиск объектов заданного типа
Давайте попробуем выделить все окружности. Сделаем это на базе предыдущего примера.
Действовать будем так: просмотрим по порядку все объекты чертежа, определим тип каждого объекта; если объект окажется окружностью — выведем информацию о нем в консоль. В прошлой статье нам уже встречалась ссылка (англ.), где описаны пять способов узнать тип объекта. Как всегда, не будем отступать от традиций и используем один из самых простых способов:
if (entity.GetType() == typeof(Circle)) // если условие верно - значит, это окружность
[CommandMethod("Habr_FindCircles_1")] public void findCircles_1() < // получаем текущую БД Database db = HostApplicationServices.WorkingDatabase; // начинаем транзакцию using (Transaction tr = db.TransactionManager.StartTransaction()) < // получаем ссылку на пространство модели (ModelSpace) BlockTableRecord ms = (BlockTableRecord)tr.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForRead); // "пробегаем" по всем объектам в пространстве модели foreach (ObjectId id in ms) < // приводим каждый из них к типу Entity Entity entity = (Entity)tr.GetObject(id, OpenMode.ForRead); // если это окружность - выводим в консоль слой, тип и цвет каждого объекта if (entity.GetType() == typeof(Circle)) < acad.DocumentManager.MdiActiveDocument.Editor.WriteMessage(string.Format("\nLayer:; Type:; Color: ,,\n", entity.Layer, entity.GetType().ToString(), entity.Color.Red.ToString(), entity.Color.Green.ToString(), entity.Color.Blue.ToString())); > > tr.Commit(); > >
Результат:
Аналогично можно попробовать другие типы. Например, найти все линии:
if (entity.GetType() == typeof(Line))
Найти все аннотации (текстовые элементы):
if (entity.GetType() == typeof(DBText))
Все вхождения блоков:
if (entity.GetType() == typeof(BlockReference))
А если вдруг подзабудете, как именно называется тип, соответствующий объекту — можно просто добавить такой объект на чертеж вручную и запустить пример итерации по объектам — он выведет название типа в консоль AutoCAD.
1.3 Поиск объектов с заданными свойствами
Попробуем вместо типа поискать какое-нибудь свойство — например, цвет. Найдем все салатовые объекты.
Это очень просто: строку
if (entity.GetType() == typeof(Circle))
if (entity.Color == Autodesk.AutoCAD.Colors.Color.FromRgb(128, 255, 64))
и смотрим, что получается.)
// команда для поиска салатовых объектов (подход 1) [CommandMethod("Habr_FindLightGreenObjects_1")] public void findLightGreenObjects_1() < // получаем текущую БД Database db = HostApplicationServices.WorkingDatabase; // начинаем транзакцию using (Transaction tr = db.TransactionManager.StartTransaction()) < // получаем ссылку на пространство модели (ModelSpace) BlockTableRecord ms = (BlockTableRecord)tr.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForRead); // "пробегаем" по всем объектам в пространстве модели foreach (ObjectId id in ms) < // приводим каждый из них к типу Entity Entity entity = (Entity)tr.GetObject(id, OpenMode.ForRead); // если цвет объекта - салатовый, то выводим в консоль слой, тип и цвет каждого объекта if (entity.Color == Autodesk.AutoCAD.Colors.Color.FromRgb(128, 255, 64)) < acad.DocumentManager.MdiActiveDocument.Editor.WriteMessage(string.Format("\nLayer:; Type:; Color: ,,\n", entity.Layer, entity.GetType().ToString(), entity.Color.Red.ToString(), entity.Color.Green.ToString(), entity.Color.Blue.ToString())); > > tr.Commit(); > >
Результат:
Аналогично можно осуществлять поиск и по другим атрибутам — например, по слою:
if (entity.Layer == "0")
Сам я глубоко в свойства объектов не залезал. Советую при работе в Visual Studio просто набрать » Entity test; test. » — после этого IntelliSense высветит все доступные программисту свойства и методы. Можно пробежаться по этому списку и ознакомиться со свойствами и методами класса Entity:
Класс Entity задает свойства, характерные для любого объекта чертежа: слой, цвет и т. п. Если требуется осуществить поиск по свойствам, специфичным для какого-то конкретного класса объектов, то необходимо вначале найти все объекты такого типа, затем выполнить приведение к этому типу и просмотреть значение нужного свойства.
Для примера давайте найдем все вхождения блока «block-1». Класс Entity не содержит сведений об имени определения блока; зато эта информация есть в свойстве Name класса BlockReference .
[CommandMethod("Habr_FindBlocks_1")] public void findBlocks_1() < // получаем текущую БД Database db = HostApplicationServices.WorkingDatabase; // начинаем транзакцию using (Transaction tr = db.TransactionManager.StartTransaction()) < // получаем ссылку на пространство модели (ModelSpace) BlockTableRecord ms = (BlockTableRecord)tr.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForRead); // "пробегаем" по всем объектам в пространстве модели foreach (ObjectId id in ms) < // приводим каждый из них к типу Entity Entity entity = (Entity)tr.GetObject(id, OpenMode.ForRead); // проверяем, является ли объект вхождением блока if (entity.GetType() == typeof(BlockReference)) < // если является - приводим его к типу BlockReference BlockReference br = (BlockReference)entity; // если имя соответствующего определения блока - "block-1", то выводим в консоль слой, тип и цвет каждого объекта if (br.Name == "block-1") < acad.DocumentManager.MdiActiveDocument.Editor.WriteMessage(string.Format("\nLayer:; Type:; Color: ,,\n", entity.Layer, entity.GetType().ToString(), entity.Color.Red.ToString(), entity.Color.Green.ToString(), entity.Color.Blue.ToString())); > > > tr.Commit(); > >
Мы найдем два вхождения блока «block-1».
2 Поиск объектов чертежа с помощью метода Editor.SelectAll()
2.1 Итерация по всем объектам чертежа
Немного истории: упоминание этого метода есть в одной из статей Kean Walmsley (англ.). Кроме того, описание есть на AutoCAD DevBlog (англ.) и форуме Сообщества программистов Autodesk в СНГ (rus).
Если в предыдущем варианте мы просматривали каждый объект в отдельности, то здесь концепция меняется: мы сразу получаем массив идентификаторов всех интересующих нас объектов, при необходимости отфильтровывая нужные.
Начнем, как обычно, с примера итерации по всем объектам.
// команда для итерации по объектам (подход 2) [CommandMethod("Habr_IterateThroughAllObjects_2")] public void iterateThroughAllObjects_2() < // получаем БД и Editor текущего документа Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; // пытаемся получить ссылки на все объекты // ВНИМАНИЕ! Нужно проверить работоспособность метода с замороженными и заблокированными слоями! PromptSelectionResult selRes = ed.SelectAll(); // если произошла ошибка - сообщаем о ней if (selRes.Status != PromptStatus.OK) < ed.WriteMessage("\nError!\n"); return; >// получаем массив ID объектов ObjectId[] ids = selRes.Value.GetObjectIds(); // начинаем транзакцию using (Transaction tr = db.TransactionManager.StartTransaction()) < // "пробегаем" по всем полученным объектам foreach (ObjectId id in ids) < // приводим каждый из них к типу Entity Entity entity = (Entity)tr.GetObject(id, OpenMode.ForRead); // выводим в консоль слой (entity.Layer), тип (entity.GetType().ToString()) и цвет (entity.Color) каждого объекта acad.DocumentManager.MdiActiveDocument.Editor.WriteMessage(string.Format("\nLayer:; Type:; Color: ,,\n", entity.Layer, entity.GetType().ToString(), entity.Color.Red.ToString(), entity.Color.Green.ToString(), entity.Color.Blue.ToString())); > tr.Commit(); > >
Результат:
Мы вызываем метод Editor.SelectAll() . Поскольку никаких фильтров не задано, нам должны вернуться идентификаторы ( ObjectID ) всех объектов на чертеже. Они записываются в переменную типа PromptSelectionResult . На всякий случай нужно убедиться, что метод отработал корректно — для этого мы проверяем статус результата ( PromptSelectionResult.Status ). Если что-то не в порядке, значение этого свойства будет отлично от PromptStatus.OK — в этом случае мы завершаем выполнение функции.
Если же метод Editor.SelectAll() отработал корректно, мы получаем идентификаторы всех объектов, возвращенных этим методом. Для этого мы используем метод PromptSelectionResult.Value.GetObjectIds() . После этого мы просто обрабатываем все объекты в цикле — точь-в-точь как в первом разделе, когда мы обращались к ModelSpace .
Важный момент! Согласно своему описанию (англ.), метод Editor.SelectAll() должен возвращать только объекты на слоях, которые НЕ являются заблокированными (locked) или замороженными (frozen). Однако в этом случае документация, похоже, привирает: Editor.SelectAll() всегда возвращает все объекты на чертеже, вне зависимости от состояний слоев, на которых они находятся. Подробнее почитать про эту забавность можно в блоге Kean Walmsley (англ.), в AutoCAD Devblog (англ.), на форумах сообщества Autodesk (англ.).
В общем, мои выводы: у меня в AutoCAD 2010 этот подход работает. Возможно, он заработает в более новых версиях .NET API. Но что это — ошибка в документации или баг в API, который однажды, быть может, пофиксят, — я сказать не могу.
Краткий итог: аккуратнее с этим.
Другой важный момент. Для использования метода Editor.SelectAll() , очевидно, необходимо иметь объект класса Editor . При работе с документом, который непосредственно открыт в AutoCAD, проблем не возникнет; но использовать данный метод для обработки БД сторонних документов (не открытых в настоящий момент в AutoCAD) не получится (англ.).
2.2 Использование фильтров
Давайте снова попробуем выделить все окружности. Разумеется, можно сделать все аналогично предыдущему разделу: получить идентификаторы всех объектов и смотреть тип каждого объекта — это сработает. Однако при использовании метода Editor.SelectAll() есть возможность действовать по-другому.
Принцип такой: вначале мы задаем фильтр для объектов с помощью класса SelectionFilter , а затем применяем этот фильтр в методе Editor.SelectAll() . В результате у нас остаются только удовлетворяющие условию фильтра объекты.
[CommandMethod("Habr_FindCircles_2")] public void findCircles_2() < // получаем БД и Editor текущего документа Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; // создаем переменную, в которой будут содержаться данные для фильтра TypedValue[] filterlist = new TypedValue[1]; // первый аргумент (0) указывает, что мы задаем тип объекта // второй аргумент ("CIRCLE") - собственно тип filterlist[0] = new TypedValue(0, "CIRCLE"); // создаем фильтр SelectionFilter filter = new SelectionFilter(filterlist); // пытаемся получить ссылки на объекты с учетом фильтра // ВНИМАНИЕ! Нужно проверить работоспособность метода с замороженными и заблокированными слоями! PromptSelectionResult selRes = ed.SelectAll(filter); // если произошла ошибка - сообщаем о ней if (selRes.Status != PromptStatus.OK) < ed.WriteMessage("\nError!\n"); return; >// получаем массив ID объектов ObjectId[] ids = selRes.Value.GetObjectIds(); // начинаем транзакцию using (Transaction tr = db.TransactionManager.StartTransaction()) < // "пробегаем" по всем полученным объектам foreach (ObjectId id in ids) < // приводим каждый из них к типу Entity Entity entity = (Entity)tr.GetObject(id, OpenMode.ForRead); // выводим в консоль слой, тип и цвет каждого объекта acad.DocumentManager.MdiActiveDocument.Editor.WriteMessage(string.Format("\nLayer:; Type:; Color: ,,\n", entity.Layer, entity.GetType().ToString(), entity.Color.Red.ToString(), entity.Color.Green.ToString(), entity.Color.Blue.ToString())); > tr.Commit(); > >
Результат:
Работает. Теперь давайте разберемся, как.)
Итак, метод Editor.SelectAll() может принимать на вход объект типа SelectionFilter , который и задает фильтр. Этот фильтр инициализируется с помощью массива объектов типа TypedValue . Конструктор TypedValue принимает на вход два параметра:
public TypedValue(int typeCode, object value);
Эти значения связаны с форматом DXF (rus). Вот что говорит по этому поводу документация (англ.):
The DXF format is a tagged data representation of all the information contained in an AutoCAD drawing file of a specific version. Tagged data means that each data element in the file is preceded by an integer number that is called a group code. A group code’s value indicates what type of data element follows. It also indicates the meaning of a data element for a given object (or record) type. Virtually all user-specified information in a drawing file can be represented in DXF format. The DXF format is essentially the same when used with applications (AutoLISP and ARX). However, there are a few minor differences for some data groups.
Гугл-транслейт:
Формат DXF позволяет представить всю информацию в чертеже AutoCAD определенной версии в виде тегированных данных. Под тегированными данными понимается то, что каждый элемент данных содержит в начале целое число, которое называется групповым кодом. Значение группового кода позволяет узнать, данные какого типа содержатся в этом элементе. Также оно отражает смысл элемента данных для соответствующего типа объекта (или записи). Практически вся добавленная пользователем на чертеж информация может быть представлена в формате DXF. В целом формат DXF один и тот же и для AutoLISP, и для ARX, однако для некоторых групп данных возможны небольшие различия.
В рассмотренном примере мы указали в качестве typeCode значение 0. Откуда оно взялось и что означает — можно посмотреть в списке (англ.)
На всякий случай я продублирую список здесь:
-5: APP: persistent reactor chain
-4: APP: conditional operator (used only with ssget)
-3: APP: extended data (XDATA) sentinel (fixed)
-2: APP: entity name reference (fixed)
-1: APP: entity name. This changes each time a drawing is opened. It is never saved. (fixed)
0: Text string indicating the entity type (fixed)
1: Primary text value for an entity
2: Name (attribute tag, block name, and so on)
3-4: Other textual or name values
5: Entity handle. Text string of up to 16: hexadecimal digits (fixed)
6: Linetype name (fixed)
7: Text style name (fixed)
8: Layer name (fixed)
9: DXF: variable name identifier (used only in HEADER section of the DXF file).
10: Primary point. This is the start point of a line or text entity, center of a circle, and so on.
DXF: X value of the primary point (followed by Y and Z value codes 20: and 30)
APP: 3D point (list of three reals)
11-18: Other points.
DXF: X value of other points (followed by Y value codes 21-28: and Z value codes 31-38)
APP: 3D point (list of three reals)
20, 30: DXF: Y and Z values of the primary point
21-28, 31-37: DXF: Y and Z values of other points
38: DXF: entity’s elevation if nonzero.
39: Entity’s thickness if nonzero (fixed)
40-48: Floating-point values (text height, scale factors, and so on)
48: Linetype scale. Floating-point scalar value. Default value is defined for all entity types.
49: Repeated floating-point value. Multiple 49: groups may appear in one entity for variable-length tables (such as the dash lengths in the LTYPE table). A 7x group always appears before the first 49 group to specify the table length.
50-58: Angles (output in degrees to DXF files and radians through AutoLISP and ARX applications).
60: Entity visibility. Integer value. Absence or 0: indicates visibility; 1 indicates invisibility.
62: Color number (fixed)
66: «Entities follow» flag (fixed)
67: Space—that is, model or paper space (fixed)
68: APP: identifies whether viewport is on but fully off screen; is not active or is off.
69: APP: viewport identification number.
70-78: Integer values, such as repeat counts, flag bits, or modes
90-99: 32-bit integer values
100: Subclass data marker (with derived class name as a string). Required for all objects and entity classes that are derived from another concrete class to segregate data defined by different classes in the inheritance chain for the same object.
This is in addition to the requirement for DXF names for each distinct concrete class derived from ARX (see «Subclass Markers»).
102: Control string, followed by «» or «>». Similar to the xdata 1002: group code, except that when the string begins with «» as a group terminator. As noted before, AutoCAD does not interpret these strings except during drawing audit operations; they are for application use.
105: DIMVAR symbol table entry object handle
210: Extrusion direction (fixed).
DXF: X value of extrusion direction
APP: 3D extrusion direction vector
220, 230: DXF: Y and Z values of the extrusion direction
280-289: 8-bit integer values
300-309: Arbitrary text strings
310-319: Arbitrary binary chunks with same representation and limits as 1004: group codes: hexadecimal strings of up to 254 characters represent data chunks of up to 127 bytes.
320-329: Arbitrary object handles. Handle values that are taken «as is.» They are not translated during INSERT and XREF operations.
330-339: Soft-pointer handle. Arbitrary soft pointers to other objects within same DXF file or drawing. Translated during INSERT and XREF operations.
340-349: Hard-pointer handle. Arbitrary hard pointers to other objects within same DXF file or drawing. Translated during INSERT and XREF operations.
350-359: Soft-owner handle. Arbitrary soft ownership links to other objects within same DXF file or drawing. Translated during INSERT and XREF operations.
360-369: Hard-owner handle. Arbitrary hard ownership links to other objects within same DXF file or drawing. Translated during INSERT and XREF operations.
999: DXF: The 999: group code indicates that the line following it is a comment string. DXFOUT does not include such groups in a DXF output file, but DXFIN honors them and ignores the comments. You can use the 999 group to include comments in a DXF file that you’ve edited.
1000: ASCII string (up to 255: bytes long) in extended data.
1001: Registered application name (ASCII string up to 31: bytes long) for extended data.
1002: Extended data control string («»).
1003: Extended data layer name.
1004: Chunk of bytes (up to 127: bytes long) in extended data.
1005: Entity handle in extended data. Text string of up to 16: hexadecimal digits
1010: A point in extended data
DXF: X value (followed by 1020: and 1030 groups)
APP: 3D point
1020, 1030: DXF: Y and Z values of a point
1011: A 3D world space position in extended data
DXF: X value (followed by 1021: and 1031 groups)
APP: 3D point
1021, 1031: DXF: Y and Z values of a World space position
1012: A 3D world space displacement in extended data
DXF: X value (followed by 1022: and 1032 groups)
APP: 3D vector
1022, 1032: DXF: Y and Z values of a World space displacement
1013: A 3D world space direction in extended data.
DXF: X value (followed by 1022: and 1032 groups)
APP: 3D vector
1023, 1033: DXF: Y and Z values of a World space direction
1040: Extended data floating-point value.
1041: Extended data distance value.
1042: Extended data scale factor.
1070: Extended data 16-bit signed integer.
1071: Extended data 32-bit signed long.
Пятой шестой строкой в списке идет эта:
0: Text string indicating the entity type (fixed)
Таким образом, коду «0» соответствует тип объекта.
Вместо зубодробительных числовых констант можно использовать любезно предоставленное разработчиками API перечисление Autodesk.AutoCAD.DatabaseServices.DxfCode :
[Wrapper("AcDb::DxfCode")] public enum DxfCode
Например, в рассмотренном примере мы могли бы вместо
filterlist[0] = new TypedValue(0, "CIRCLE");
filterlist[0] = new TypedValue((int)DxfCode.Start, "CIRCLE");
И это сработает!
Вот только не надо спрашивать меня, почему это значению 0 соответствует элемент перечисления с названием «Start». Понятия не имею.
В общем, можете забить весь свой код непонятными числовыми константами (что, конечно, хреново). Или можете забить его соответствующими элементами перечисления Autodesk.AutoCAD.DatabaseServices.DxfCode с не менее непонятными, взятыми с потолка названиями (что, разумеется, хреново). Можете также создать свое собственное перечисление с необходимыми константами и использовать его (что, сами понимаете, хреново). Добро пожаловать в увлекательный мир программирования! Налево пойдешь — коня потеряешь, и так далее.
Поскольку в любом случае у вас получится нечитаемое спагетти — можете выбрать любой способ или даже использовать все три. А если сойти с ума через полгода не входит в ваши планы — пишите комментарии, это поможет продержаться чуть дольше.
Быстро-быстро пробежимся по простым возможностям фильтров.
Разумеется, искать мы можем не только окружности. Найдем все линии:
filterlist[0] = new TypedValue((int)DxfCode.Start, "LINE");
Найдем все вхождения блоков:
filterlist[0] = new TypedValue((int)DxfCode.Start, "INSERT");
Список некоторых имен классов объектов можно найти тут (англ.).
Вот он, на всякий случай:
3DFACE
3DSOLID
ACAD_PROXY_ENTITY
ARC
ARCALIGNEDTEXT
ATTDEF
ATTRIB
BODY
CIRCLE
DIMENSION
ELLIPSE
HATCH
IMAGE
INSERT
LEADER
LINE
LWPOLYLINE
MLINE
MTEXT
OLEFRAME
OLE2FRAME
POINT
POLYLINE
RAY
REGION
RTEXT
SEQEND
SHAPE
SOLID
SPLINE
TEXT
TOLERANCE
TRACE
VERTEX
VIEWPORT
WIPEOUT
XLINE
Именно отсюда было взято значение «INSERT» .
Найдем все объекты на слое «layer-1»:
filterlist[0] = new TypedValue((int)DxfCode.LayerName, "layer-1");
В одном объекте TypedValue можно перечислить несколько имен через запятую. В этом случае условия будут объединены операцией «ИЛИ» («OR»).
Давайте найдем все объекты, которые являются линиями ИЛИ окружностями:
filterlist[0] = new TypedValue((int)DxfCode.Start, "LINE,CIRCLE");
Найдем все объекты, которые находятся на слое «layer-1» ИЛИ «layer-2»:
filterlist[0] = new TypedValue((int)DxfCode.LayerName, "layer-1,layer-2");
ВАЖНО: после запятой между объединяемыми параметрами НЕ ДОЛЖНО быть пробела!
Наконец, можно добавить несколько объектов TypedValue к массиву условий. В этом случае условия будут объединены операцией «И» («AND»).
Найдем все объекты, которые являются линиями И находятся на нулевом слое:
TypedValue[] filterlist = new TypedValue[2]; filterlist[0] = new TypedValue((int)DxfCode.Start, "CIRCLE"); filterlist[1] = new TypedValue((int)DxfCode.LayerName, "0");
Найдем все вхождения блока «block-1»:
TypedValue[] filterlist = new TypedValue[2]; filterlist[0] = new TypedValue((int)DxfCode.Start, "INSERT"); filterlist[1] = new TypedValue((int)DxfCode.BlockName, "block-1");
Найдем все объекты, которые являются линиями И находятся на нулевом слое ИЛИ слое «layer-1»:
TypedValue[] filterlist = new TypedValue[2]; filterlist[0] = new TypedValue((int)DxfCode.Start, "LINE"); filterlist[1] = new TypedValue((int)DxfCode.LayerName, "0,layer-1");
Ну вот, вроде и не больно… Было. Пока что.
2.3 Фильтры посложнее
Давайте взглянем на украденный творчески переработанный (я написал перевел комментарии) пример из блога Kean Walmsley.
Пусть нам надо найти все линии на слое «layer-1» и все круги на слое «layer-2». Очевидно, мы не сможем этого сделать, просто добавив несколько объектов TypedValue : максимум, чего можно добиться этим способом, — это найти все линии и все круги на обоих слоях сразу.
Итак, нам нужно реализовать выбор по такому условию:
((СЛОЙ == «layer-1») И (ТИП == «Линия»)) ИЛИ ((СЛОЙ == «layer-2») И (ТИП == «Окружность»))
В синтаксисе AutoCAD наше условие можно записать так:
-
-
- Layer == «layer-1»
- Entity type == «LINE»
- Layer == «layer-2»
- Entity type == «CIRCLE»
TypedValue[] filterlist = new TypedValue[10]; filterlist[0] = new TypedValue((int)DxfCode.Operator, "
"); filterlist[5] = new TypedValue((int)DxfCode.Operator, " "); filterlist[9] = new TypedValue((int)DxfCode.Operator, "OR>"); После применения фильтра на базе этих условий мы увидим следующий вывод в консоли AutoCAD:
Layer:layer-2; Type:Autodesk.AutoCAD.DatabaseServices.Circle; Color: 128,255,64
Layer:layer-1; Type:Autodesk.AutoCAD.DatabaseServices.Line; Color: 0,200,255
Layer:layer-1; Type:Autodesk.AutoCAD.DatabaseServices.Line; Color: 255,128,255Принцип простой: осознаем, какое условие нам нужно, разбиваем его на элементарные части, а затем объединяем эти части с помощью операторов OR, AND, NOT, XOR…
Серьезно? XOR? Сколько лет, сколько зим… Кажется, последний раз я тебя видел на лабе по информатике.
Как жизнь, родимый? Где-то ты сейчас? Кто все эти люди, которые тебя используют?Почитать подробнее про сложные условия можно здесь (англ.).
Все, хватит уже о фильтрах. Сколько можно…
3 Альтернативные способы поиска объектов на чертеже
Помимо Editor.SelectAll() , есть ряд других способов поиска (точнее, выделения) объектов на чертеже. Вот ссылки: английская документация, русский перевод.
Как можно увидеть, существуют еще десять более узконаправленных вариантов, чем Editor.SelectAll() . Зато мы рассмотрели самый мощный из них.
Оффтоп: обещанные кровавые подробности (можно пропустить)
Я некоторое время думал, стоит ли вообще включать это в статью. В итоге, решил, что пусть будет — вот только зачем?
В общем, если у вас есть чертеж с огромным количеством объектов, и нужно все их обработать, и выхода нет, и скоро рассвет — то вот вам ссылочка (англ.), где Андрей Бушман hwd, Александр Ривилис и зарубежные эксперты развлекаются, оптимизируя эту задачку.
В этом примере мы получаем доступ не к видимому чертежу ( ModelSpace ), а ко всей БД документа целиком. Таким образом, мы увидим не только изображенные на чертеже объекты, но и слои, определения блоков и т.п.
// команда для итерации по объектам (подход 3) // Осторожно! Используется черная магия! [CommandMethod("Habr_IterateThroughAllObjects_3")] public void iterateThroughAllObjects_3() < Database db = HostApplicationServices.WorkingDatabase; Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; long amount = 0; Dictionaryd = new Dictionary(); ObjectId for (long i = db.BlockTableId.Handle.Value; i < db.Handseed.Value; i++) < Handle h = new Handle(i); if (db.TryGetObjectId(h, out id) && !id.IsNull && id.IsValid && !id.IsErased) < string t = id.ObjectClass.DxfName; amount++; if(d.ContainsKey(t)) d[t]++; else d.Add(t, 1); >> foreach(KeyValuePair kvp in d) ed.WriteMessage("\n: ", kvp.Key, kvp.Value); ed.WriteMessage("\nTotal objects in drawing\n", amount); >
Результат (вывод в консоль):
Command: Habr_IterateThroughAllObjects_3
TABLE: 11
DICTIONARY: 16
ACDBDICTIONARYWDFLT: 1
ACDBPLACEHOLDER: 1
LAYER: 3
STYLE: 2
APPID: 7
LTYPE: 3
MLINESTYLE: 1
BLOCK_RECORD: 5
BLOCK: 5
ENDBLK: 5
LAYOUT: 3
DIMSTYLE: 3
DICTIONARYVAR: 7
TABLESTYLE: 1
VPORT: 1
MATERIAL: 3
VISUALSTYLE: 19
SCALE: 17
MLEADERSTYLE: 2
XRECORD: 4
FONT_TABLE_RECORD: 2
CIRCLE: 5
LINE: 9
LWPOLYLINE: 2
TEXT: 2
INSERT: 3
ARC: 1
Total 144 objects in drawingТут применено какое-то особо сильное колдунство с прямым доступом к объектам БД по их хендлам. Я вначале попытался было вкурить код, но вовремя вспомнил, что уже давно завязал, и делать это совершенно необязательно. Если вы — программист, то welcome! Что же касается меня… Пожалуй, разбор этого элементарного фрагмента кода оставим читателям в качестве несложного домашнего задания.
UPD.: В комментариях Андрей Бушман привел ссылку на наиболее рациональный способ итерации по объектам.
4 Модификация объектов
Мне кажется, что модификация объектов происходит в разы проще поиска. Вот основной принцип: получив ObjectID , мы открываем сам объект, приводим его к нужному типу и вносим необходимые изменения в свойства. Ну или удаляем объект с чертежа, используя соответствующий метод.
4.1 Удаление объекта с чертежа
- открыть объект на запись;
- вызвать метод Erase();
- зафиксировать транзакцию.
Для примера давайте удалим с чертежа все окружности.
[CommandMethod("Habr_EraseCircles_2")] public void eraseCircles_2() < // получаем БД и Editor текущего документа Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; // создаем переменную, в которой будут содержаться данные для фильтра TypedValue[] filterlist = new TypedValue[1]; // первый аргумент (0) указывает, что мы задаем тип объекта // второй аргумент ("CIRCLE") - собственно тип filterlist[0] = new TypedValue(0, "CIRCLE"); // создаем фильтр SelectionFilter filter = new SelectionFilter(filterlist); // пытаемся получить ссылки на объекты с учетом фильтра // ВНИМАНИЕ! Нужно проверить работоспособность метода с замороженными и заблокированными слоями! PromptSelectionResult selRes = ed.SelectAll(filter); // если произошла ошибка - сообщаем о ней if (selRes.Status != PromptStatus.OK) < ed.WriteMessage("\nError!\n"); return; >// получаем массив ID объектов ObjectId[] ids = selRes.Value.GetObjectIds(); // начинаем транзакцию using (Transaction tr = db.TransactionManager.StartTransaction()) < // "пробегаем" по всем полученным объектам foreach (ObjectId id in ids) < // приводим каждый из них к типу Entity Entity entity = (Entity)tr.GetObject(id, OpenMode.ForRead); // открываем приговоренный объект на запись entity.UpgradeOpen(); // удаляем объект entity.Erase(); >tr.Commit(); > >
Результат:
Конечно, в данном случае можно было и не использовать метод UpgradeOpen() , а сразу открывать объект на запись и удалять:
Entity entity = (Entity)tr.GetObject(id, OpenMode.ForWrite); entity.Erase();
Важно! Если объект находится на заблокированном слое — доступ к нему на запись получить не удастся, и мы сможем полюбоваться на такое сообщение:
Поэтому еще раз напомню о необходимости как минимум упаковывать все требующие доступа на запись операции в конструкцию try. catch с последующим перехватыванием исключений. То есть что-то вроде такого:
Entity entity = (Entity)tr.GetObject(id, OpenMode.ForRead); try < entity.UpgradeOpen(); entity.Erase(); >catch ()
В этом случае мы вместо системного исключения увидим простое сообщение в консоли AutoCAD.
Разумеется, использование try. catch в таком виде — это тот еще быдлокод. Ведь мы же знаем, что в случае заблокированного слоя нас ждет ошибка eOnLockedLayer , так что можем перехватывать не все ошибки подряд, а только Autodesk.AutoCAD.Runtime.Exception с соответствующим кодом ошибки ( ErrorStatus ) — как-то так:
catch (Autodesk.AutoCAD.Runtime.Exception ex) < if (ex.ErrorStatus == ErrorStatus.OnLockedLayer) < . >>
Тут подумайте сами — либо решите, что «яжпрограммист», и обрабатывайте наиболее вероятные ошибки по отдельности, либо решите, что путешествия в дебри кода — это не для вас, и глубокая фильтрация ошибок НЕ_НУЖНА. По-хорошему, такое решение должно приниматься с учетом поставленной задачи, оценки рисков и т. д.
Но — подчеркну еще раз — подобную ситуацию нужно иметь в виду в любом случае.
UPD.: Андрей Бушман подсказал, что метод GetObject , вообще говоря, имеет следующую сигнатуру:
GetObject(ObjectId id, OpenMode mode, Boolean openErased, Boolean forceOpenOnLockedLayer)
Последний параметр позволяет в том числе работать с заблокированными слоями. Для примера, вот такой код у меня корректно отработал даже с заблокированными слоем:
Entity entity = (Entity)tr.GetObject(id, OpenMode.ForWrite, false, true); entity.Erase();
После завершения транзакции состояние слоя, на котором расположен объект, не изменяется.
В общем, похоже, что вместо конструкции try. catch можно использовать параметр forceOpenOnLockedLayer . В этом случае мы отказываемся от метода UpgradeOpen() и сразу открываем объект методом GetObject() на запись ( OpenMode.ForWrite ) с установленным флагом forceOpenOnLockedLayer .
4.2 Изменение типовых свойств объектов
Под «типовыми свойствами» я имею в виду те свойства, которые присущи всем объектам на чертеже AutoCAD. Это, например, слой и цвет.
- открываем объект на запись;
- модифицируем нужное свойство;
- фиксируем транзакцию.
[CommandMethod("Habr_RepaintOrange_2")] public void repaintOrange_2() < // получаем БД и Editor текущего документа Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; // пытаемся получить ссылки на все объекты // ВНИМАНИЕ! Нужно проверить работоспособность метода с замороженными и заблокированными слоями! PromptSelectionResult selRes = ed.SelectAll(); // если произошла ошибка - сообщаем о ней if (selRes.Status != PromptStatus.OK) < ed.WriteMessage("\nError!\n"); return; >// получаем массив ID объектов ObjectId[] ids = selRes.Value.GetObjectIds(); // начинаем транзакцию using (Transaction tr = db.TransactionManager.StartTransaction()) < // "пробегаем" по всем полученным объектам foreach (ObjectId id in ids) < // приводим каждый из них к типу Entity Entity entity = (Entity)tr.GetObject(id, OpenMode.ForRead); // открываем объект на запись entity.UpgradeOpen(); // изменяем цвет на оранжевый entity.Color = Autodesk.AutoCAD.Colors.Color.FromRgb(255, 128, 0); >tr.Commit(); > >
Результат:
Цвет всех объектов изменен на оранжевый. Однако объекты внутри вхождений блоков цвет не поменяли и остались черными — несмотря на то, что для всех вхождений блоков был успешно задан оранжевый цвет:
Доказательство — значение поля Color:
Нужно иметь эту особенность в виду.
Рассмотренный способ можно использовать для модификации всех изменяемых свойств, которые доступны в классе Entity . Но как и в предыдущем случае, вы должны быть на 146% уверены в том, что плагину удастся получить доступ к объекту на запись. Если степень уверенности меньше — используйте флаг forceOpenOnLockedLayer или
средства индивидуальной защитыконструкцию try. catch и перехватывайте возможные исключения.4.3 Изменение специфичных свойств объектов
Допустим, мы хотим поменять радиус окружности. Используя класс Entity , мы не сможем это сделать, поскольку у него нет свойства, отвечающего за радиус. В данном случае нужно приводить объект к типу Circle :
[CommandMethod("Habr_ModifyCircles_2")] public void modifyCircles_2() < // получаем БД и Editor текущего документа Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; // создаем переменную, в которой будут содержаться данные для фильтра TypedValue[] filterlist = new TypedValue[1]; // первый аргумент (0) указывает, что мы задаем тип объекта // второй аргумент ("CIRCLE") - собственно тип filterlist[0] = new TypedValue(0, "CIRCLE"); // создаем фильтр SelectionFilter filter = new SelectionFilter(filterlist); // пытаемся получить ссылки на объекты с учетом фильтра // ВНИМАНИЕ! Нужно проверить работоспособность метода с замороженными и заблокированными слоями! PromptSelectionResult selRes = ed.SelectAll(filter); // если произошла ошибка - сообщаем о ней if (selRes.Status != PromptStatus.OK) < ed.WriteMessage("\nError!\n"); return; >// получаем массив ID объектов ObjectId[] ids = selRes.Value.GetObjectIds(); // начинаем транзакцию using (Transaction tr = db.TransactionManager.StartTransaction()) < // "пробегаем" по всем полученным объектам foreach (ObjectId id in ids) < // приводим каждый из них к типу Circle Circle cir = (Circle)tr.GetObject(id, OpenMode.ForRead); // открываем объект на запись cir.UpgradeOpen(); // увеличиваем радиус cir.Radius = cir.Radius * 2; >tr.Commit(); > >
Результат:
Разумеется, необходимо убедиться, что мы имеем дело именно с окружностями. Попытка привести к типу Circle , например, вхождение блока добром не кончится:
Используйте конструкцию try. catch ! Или по крайней мере обеспечивайте фильтрацию, как в нашем примере, либо же проверяйте тип непосредственно перед приведением:
Entity ent = (Entity)tr.GetObject(id, OpenMode.ForRead); if (ent.GetType() == typeof(Circle))
Подчеркну, что ни фильтр, ни проверка не обезопасят от ситуации с заблокированным слоем и ошибкой eOnLockedLayer . Для борьбы с ней используйте либо флаг forceOpenOnLockedLayer , либо конструкцию try. catch , либо фильтры / проверки, которые отсекут заблокированные объекты.
4.4 Перемещение простых объектов
Если положение объекта на чертеже задается одной точкой (как, например, у окружности или блока), то для перемещения этой фигуры достаточно изменить соответствующие свойство. Для примера давайте переместим все вхождения блоков в начало координат:
[CommandMethod("Habr_MoveBlocks_2")] public void moveBlocks_2() < // получаем БД и Editor текущего документа Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; // создаем переменную, в которой будут содержаться данные для фильтра TypedValue[] filterlist = new TypedValue[1]; // первый аргумент (0) указывает, что мы задаем тип объекта // второй аргумент ("CIRCLE") - собственно тип filterlist[0] = new TypedValue(0, "INSERT"); // создаем фильтр SelectionFilter filter = new SelectionFilter(filterlist); // пытаемся получить ссылки на объекты с учетом фильтра // ВНИМАНИЕ! Нужно проверить работоспособность метода с замороженными и заблокированными слоями! PromptSelectionResult selRes = ed.SelectAll(filter); // если произошла ошибка - сообщаем о ней if (selRes.Status != PromptStatus.OK) < ed.WriteMessage("\nError!\n"); return; >// получаем массив ID объектов ObjectId[] ids = selRes.Value.GetObjectIds(); // начинаем транзакцию using (Transaction tr = db.TransactionManager.StartTransaction()) < // "пробегаем" по всем полученным объектам foreach (ObjectId id in ids) < // приводим каждый из них к типу BlockReference BlockReference br = (BlockReference)tr.GetObject(id, OpenMode.ForRead); // открываем объект на запись br.UpgradeOpen(); // перемещаем вхождение блока br.Position = Point3d.Origin; >tr.Commit(); > >
Результат:
Аналогом свойства Position для окружности является свойство Center .
4.5 Сложные операции над объектами
AutoCAD .NET API позволяет осуществлять масштабирование и поворот объектов. Наверное, даже можно как-то модифицировать вершины многоугольников и ломаных. К сожалению, я с такими вещами не работал и ничего по этому поводу сказать не могу.
Начать поиск информации, если уж придется, можно с руководства по .NET API (ссылка (англ.), зеркало (англ.)). Ну и форумы AutoCAD в помощь. Можно также задать вопрос на форумах Сообщества программистов Autodesk в СНГ.
Приложение
На всякий случай — вот финальная версия кода, содержащая основные из рассмотренных примеров
using System; using System.Collections.Generic; using Autodesk.AutoCAD.Runtime; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.EditorInput; using acad = Autodesk.AutoCAD.ApplicationServices.Application; namespace HabrPlug_SearchAndRescue < public class ClassMyAutoCADDLL_SearchAndRescue < public class Commands : IExtensionApplication < // используемые цвета Autodesk.AutoCAD.Colors.Color color_Pink = Autodesk.AutoCAD.Colors.Color.FromRgb(255, 128, 255); Autodesk.AutoCAD.Colors.Color color_Blue = Autodesk.AutoCAD.Colors.Color.FromRgb(0, 200, 255); Autodesk.AutoCAD.Colors.Color color_LightGreen = Autodesk.AutoCAD.Colors.Color.FromRgb(128, 255, 64); // ID слоев "layer-1" и "layer-2" ObjectId layer_1; ObjectId layer_2; // создаем слои public void createLayers() < // получаем текущий документ и его БД Document acDoc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument; Database acCurDb = acDoc.Database; // блокируем документ using (DocumentLock docloc = acDoc.LockDocument()) < // начинаем транзакцию using (Transaction tr = acCurDb.TransactionManager.StartTransaction()) < // открываем таблицу слоев документа LayerTable acLyrTbl = tr.GetObject(acCurDb.LayerTableId, OpenMode.ForWrite) as LayerTable; // создаем новый слой и задаем ему имя LayerTableRecord acLyrTblRec_1 = new LayerTableRecord(); acLyrTblRec_1.Name = "layer-1"; // заносим созданный слой в таблицу слоев, сохраняем ID созданной записи слоя layer_1 = acLyrTbl.Add(acLyrTblRec_1); // добавляем созданный слой в документ tr.AddNewlyCreatedDBObject(acLyrTblRec_1, true); // создаем новый слой и задаем ему имя LayerTableRecord acLyrTblRec_2 = new LayerTableRecord(); acLyrTblRec_2.Name = "layer-2"; // заносим созданный слой в таблицу слоев, сохраняем ID созданной записи слоя layer_2 = acLyrTbl.Add(acLyrTblRec_2); // добавляем созданный слой в документ tr.AddNewlyCreatedDBObject(acLyrTblRec_2, true); // фиксируем транзакцию tr.Commit(); >> > // создаем определение блока "block-1" public void createBlock_1() < // получаем ссылки на документ и его БД Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; // имя создаваемого блока const string blockName = "block-1"; // начинаем транзакцию Transaction tr = db.TransactionManager.StartTransaction(); using (tr) < // открываем таблицу блоков на запись BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForWrite); // проверяем, нет ли в таблице блока с таким именем; если есть - заканчиваем выполнение команды if (bt.Has(blockName)) < return; >// создаем новое определение блока, задаем ему имя BlockTableRecord btr = new BlockTableRecord(); btr.Name = blockName; // добавляем созданное определение блока в таблицу блоков и в транзакцию bt.Add(btr); tr.AddNewlyCreatedDBObject(btr, true); // добавляем к блоку элементы // создаем окружность Circle acCircle = new Circle(); // устанавливаем параметры созданного объекта acCircle.SetDatabaseDefaults(); acCircle.Center = Point3d.Origin; acCircle.Radius = 25; // добавляем созданный объект в определение блока и в транзакцию btr.AppendEntity(acCircle); tr.AddNewlyCreatedDBObject(acCircle, true); // создаем линию Line acLine = new Line(new Point3d(18, 18, 0), new Point3d(35, 35, 0)); // устанавливаем параметры созданного объекта равными параметрам по умолчанию acLine.SetDatabaseDefaults(); // добавляем созданный объект в определение блока и в транзакцию btr.AppendEntity(acLine); tr.AddNewlyCreatedDBObject(acLine, true); // создаем полилинию Polyline acPolyline = new Polyline(); // устанавливаем параметры созданного объекта равными параметрам по умолчанию acPolyline.SetDatabaseDefaults(); // добавляем к полилинии вершины acPolyline.AddVertexAt(0, new Point2d(20, 35), 0, 0, 0); acPolyline.AddVertexAt(1, new Point2d(35, 35), 0, 0, 0); acPolyline.AddVertexAt(2, new Point2d(35, 20), 0, 0, 0); // добавляем созданный объект в определение блока и в транзакцию btr.AppendEntity(acPolyline); tr.AddNewlyCreatedDBObject(acPolyline, true); // фиксируем транзакцию tr.Commit(); > > // создаем определение блока "block-2" public void createBlock_2() < // получаем ссылки на документ и его БД Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; // имя создаваемого блока const string blockName = "block-2"; // начинаем транзакцию Transaction tr = db.TransactionManager.StartTransaction(); using (tr) < // открываем таблицу блоков на запись BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForWrite); // проверяем, нет ли в таблице блока с таким именем; если есть - заканчиваем выполнение команды if (bt.Has(blockName)) < return; >// создаем новое определение блока, задаем ему имя BlockTableRecord btr = new BlockTableRecord(); btr.Name = blockName; // добавляем созданное определение блока в таблицу блоков и в транзакцию bt.Add(btr); tr.AddNewlyCreatedDBObject(btr, true); // добавляем к блоку элементы // создаем окружность Circle acCircle = new Circle(); // устанавливаем параметры созданного объекта acCircle.SetDatabaseDefaults(); acCircle.Center = Point3d.Origin; acCircle.Radius = 25; // добавляем созданный объект определение блока и в транзакцию btr.AppendEntity(acCircle); tr.AddNewlyCreatedDBObject(acCircle, true); // создаем первую линию Line acLine_1 = new Line(new Point3d(0, -25, 0), new Point3d(0, -50, 0)); // устанавливаем параметры созданного объекта равными параметрам по умолчанию acLine_1.SetDatabaseDefaults(); // добавляем созданный объект в определение блока и в транзакцию btr.AppendEntity(acLine_1); tr.AddNewlyCreatedDBObject(acLine_1, true); // создаем вторую линию Line acLine_2 = new Line(new Point3d(-7, -39, 0), new Point3d(7, -39, 0)); // устанавливаем параметры созданного объекта равными параметрам по умолчанию acLine_2.SetDatabaseDefaults(); // добавляем созданный объект в определение блока и в транзакцию btr.AppendEntity(acLine_2); tr.AddNewlyCreatedDBObject(acLine_2, true); // фиксируем транзакцию tr.Commit(); > > // создаем объекты на нулевом слое public void layer_0_createObjects() < // получаем текущий документ и его БД Document doc = acad.DocumentManager.MdiActiveDocument; Database db = doc.Database; // начинаем транзакцию using (Transaction tr = db.TransactionManager.StartTransaction()) < // открываем таблицу блоков документа BlockTable acBlkTbl; acBlkTbl = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; // открываем пространство модели (Model Space) - оно является одной из записей в таблице блоков документа BlockTableRecord ms = tr.GetObject(acBlkTbl[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord; // добавляем розовую линию Line acLine_1 = new Line(new Point3d(225, 225, 0), new Point3d(225, 175, 0)); // устанавливаем параметры созданного объекта равными параметрам по умолчанию acLine_1.SetDatabaseDefaults(); // устанавливаем для объекта нужный слой и цвет acLine_1.Layer = "0"; acLine_1.Color = color_Pink; // добавляем созданный объект в пространство модели и в транзакцию ms.AppendEntity(acLine_1); tr.AddNewlyCreatedDBObject(acLine_1, true); // добавляем голубую линию Line acLine_2 = new Line(new Point3d(250, 225, 0), new Point3d(250, 175, 0)); // устанавливаем параметры созданного объекта равными параметрам по умолчанию acLine_2.SetDatabaseDefaults(); // устанавливаем для объекта нужный слой и цвет acLine_2.Layer = "0"; acLine_2.Color = color_Blue; // добавляем созданный объект в пространство модели и в транзакцию ms.AppendEntity(acLine_2); tr.AddNewlyCreatedDBObject(acLine_2, true); // добавляем салатовую линию Line acLine_3 = new Line(new Point3d(275, 225, 0), new Point3d(275, 175, 0)); // устанавливаем параметры созданного объекта равными параметрам по умолчанию acLine_3.SetDatabaseDefaults(); // устанавливаем для объекта нужный слой и цвет acLine_3.Layer = "0"; acLine_3.Color = color_LightGreen; // добавляем созданный объект в пространство модели и в транзакцию ms.AppendEntity(acLine_3); tr.AddNewlyCreatedDBObject(acLine_3, true); // добавляем розовую полилинию Polyline acPolyline = new Polyline(); // устанавливаем параметры созданного объекта равными параметрам по умолчанию acPolyline.SetDatabaseDefaults(); // добавляем к полилинии вершины acPolyline.AddVertexAt(0, new Point2d(300, 225), 0, 0, 0); acPolyline.AddVertexAt(1, new Point2d(325, 175), 0, 0, 0); acPolyline.AddVertexAt(2, new Point2d(350, 225), 0, 0, 0); // устанавливаем для объекта нужный слой и цвет acPolyline.Layer = "0"; acPolyline.Color = color_Pink; // добавляем созданный объект в пространство модели и в транзакцию ms.AppendEntity(acPolyline); tr.AddNewlyCreatedDBObject(acPolyline, true); // добавляем голубую окружность Circle acCircle = new Circle(); // устанавливаем параметры созданного объекта acCircle.SetDatabaseDefaults(); acCircle.Center = new Point3d(400, 200, 0); acCircle.Radius = 25; // устанавливаем для объекта нужный слой и цвет acCircle.Layer = "0"; acCircle.Color = color_Blue; // добавляем созданный объект в пространство модели и в транзакцию ms.AppendEntity(acCircle); tr.AddNewlyCreatedDBObject(acCircle, true); // добавляем салатовый текст DBText text = new DBText(); text.Position = new Point3d(450, 175, 0); text.Height = 50; text.TextString = "HABR!"; // устанавливаем для объекта нужный слой и цвет text.Layer = "0"; text.Color = color_LightGreen; // добавляем созданный объект в пространство модели и в транзакцию ms.AppendEntity(text); tr.AddNewlyCreatedDBObject(text, true); // фиксируем изменения tr.Commit(); >> // создаем объекты на слое "layer-1" public void layer_1_createObjects() < // получаем текущий документ и его БД Document doc = acad.DocumentManager.MdiActiveDocument; Database db = doc.Database; // начинаем транзакцию using (Transaction tr = db.TransactionManager.StartTransaction()) < // запоминаем текущий слой и временно меняем его на нужный нам // (это позволит не задавать слой отдельно для каждого создаваемого объекта) ObjectId currentLayer = db.Clayer; db.Clayer = layer_1; // открываем таблицу блоков документа BlockTable acBlkTbl; acBlkTbl = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; // открываем пространство модели (Model Space) - оно является одной из записей в таблице блоков документа BlockTableRecord ms = tr.GetObject(acBlkTbl[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord; // добавляем розовую линию Line acLine_1 = new Line(new Point3d(225, 25, 0), new Point3d(225, -25, 0)); // устанавливаем параметры созданного объекта acLine_1.SetDatabaseDefaults(); acLine_1.Color = color_Pink; // добавляем созданный объект в пространство модели и в транзакцию ms.AppendEntity(acLine_1); tr.AddNewlyCreatedDBObject(acLine_1, true); // добавляем голубую линию Line acLine_2 = new Line(new Point3d(250, 25, 0), new Point3d(250, -25, 0)); // устанавливаем параметры созданного объекта acLine_2.SetDatabaseDefaults(); acLine_2.Color = color_Blue; // добавляем созданный объект в пространство модели и в транзакцию ms.AppendEntity(acLine_2); tr.AddNewlyCreatedDBObject(acLine_2, true); // добавляем салатовую окружность Circle acCircle = new Circle(); // устанавливаем параметры созданного объекта acCircle.SetDatabaseDefaults(); acCircle.Center = new Point3d(300, 0, 0); acCircle.Radius = 25; acCircle.Color = color_LightGreen; // добавляем созданный объект в пространство модели и в транзакцию ms.AppendEntity(acCircle); tr.AddNewlyCreatedDBObject(acCircle, true); // добавляем розовый текст DBText text = new DBText(); // устанавливаем параметры созданного объекта text.Position = new Point3d(350, -25, 0); text.Height = 50; text.TextString = "HABR!"; text.Color = color_Pink; // добавляем созданный объект в пространство модели и в транзакцию ms.AppendEntity(text); tr.AddNewlyCreatedDBObject(text, true); // добавляем вхождение блока "block-1" // открываем таблицу блоков для чтения BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead); // получаем ObjectID блока ObjectId btrId = bt["block-1"]; // создаем новое вхождение блока, используя полученный ID определения блока BlockReference br = new BlockReference(new Point3d(600, 0, 0), btrId); // добавляем вхождение блока на пространство модели и в транзакцию ms.AppendEntity(br); tr.AddNewlyCreatedDBObject(br, true); // возвращаем обратно старый текущий слой db.Clayer = currentLayer; // фиксируем изменения tr.Commit(); >> // создаем объекты на слое "layer-2" public void layer_2_createObjects() < // получаем текущий документ и его БД Document doc = acad.DocumentManager.MdiActiveDocument; Database db = doc.Database; // начинаем транзакцию using (Transaction tr = db.TransactionManager.StartTransaction()) < // запоминаем текущий слой и временно меняем его на нужный нам // (это позволит не задавать слой отдельно для каждого создаваемого объекта) ObjectId currentLayer = db.Clayer; db.Clayer = layer_2; // открываем таблицу блоков документа BlockTable acBlkTbl; acBlkTbl = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; // открываем пространство модели (Model Space) - оно является одной из записей в таблице блоков документа BlockTableRecord ms = tr.GetObject(acBlkTbl[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord; // добавляем розовую линию Line acLine_1 = new Line(new Point3d(225, -175, 0), new Point3d(225, -225, 0)); // устанавливаем параметры созданного объекта acLine_1.SetDatabaseDefaults(); acLine_1.Color = color_Pink; // добавляем созданный объект в пространство модели и в транзакцию ms.AppendEntity(acLine_1); tr.AddNewlyCreatedDBObject(acLine_1, true); // добавляем голубую дугу Arc acArc = new Arc(new Point3d(250, -200, 0), 25, -45 / 180.0 * Math.PI, 45 / 180.0 * Math.PI); // устанавливаем параметры созданного объекта acArc.SetDatabaseDefaults(); acArc.Color = color_Blue; // добавляем созданный объект в пространство модели и в транзакцию ms.AppendEntity(acArc); tr.AddNewlyCreatedDBObject(acArc, true); // добавляем салатовую окружность Circle acCircle = new Circle(); // устанавливаем параметры созданного объекта acCircle.SetDatabaseDefaults(); acCircle.Center = new Point3d(325, -200, 0); acCircle.Radius = 25; acCircle.Color = color_LightGreen; // добавляем созданный объект в пространство модели и в транзакцию ms.AppendEntity(acCircle); tr.AddNewlyCreatedDBObject(acCircle, true); // добавляем вхождение блока "block-1" // открываем таблицу блоков для чтения BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead); // получаем ObjectID блока ObjectId btrId = bt["block-1"]; // создаем новое вхождение блока, используя полученный ID определения блока BlockReference br = new BlockReference(new Point3d(400, -200, 0), btrId); // добавляем вхождение блока на пространство модели и в транзакцию ms.AppendEntity(br); tr.AddNewlyCreatedDBObject(br, true); // добавляем вхождение блока "block-2" // получаем ObjectID блока btrId = bt["block-2"]; // создаем новое вхождение блока, используя полученный ID определения блока br = new BlockReference(new Point3d(475, -200, 0), btrId); // добавляем вхождение блока на пространство модели и в транзакцию ms.AppendEntity(br); tr.AddNewlyCreatedDBObject(br, true); // возвращаем обратно старый текущий слой db.Clayer = currentLayer; // фиксируем изменения tr.Commit(); >> // создаем объекты при загрузке плагина public void Initialize() < createLayers(); createBlock_1(); createBlock_2(); layer_0_createObjects(); layer_1_createObjects(); layer_2_createObjects(); >// функция Terminate() необходима, чтобы реализовать интерфейс IExtensionApplication public void Terminate() < >////////////////////////////////////////////////////////////// // ПЕРВЫЙ ВАРИАНТ РЕАЛИЗАЦИИ ПОИСКА (>) ////////////////////////////////////////////////////////////// // команда для итерации по объектам (подход 1) [CommandMethod("Habr_IterateThroughAllObjects_1")] public void iterateThroughAllObjects_1() < // получаем текущую БД Database db = HostApplicationServices.WorkingDatabase; // начинаем транзакцию using (Transaction tr = db.TransactionManager.StartTransaction()) < // получаем ссылку на пространство модели (ModelSpace) BlockTableRecord ms = (BlockTableRecord)tr.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForRead); // "пробегаем" по всем объектам в пространстве модели foreach (ObjectId id in ms) < // приводим каждый из них к типу Entity Entity entity = (Entity)tr.GetObject(id, OpenMode.ForRead); // выводим в консоль слой (entity.Layer), тип (entity.GetType().ToString()) и цвет (entity.Color) каждого объекта acad.DocumentManager.MdiActiveDocument.Editor.WriteMessage(string.Format("\nLayer:; Type:; Color: ,,\n", entity.Layer, entity.GetType().ToString(), entity.Color.Red.ToString(), entity.Color.Green.ToString(), entity.Color.Blue.ToString())); > tr.Commit(); > > // команда для поиска окружностей (подход 1) [CommandMethod("Habr_FindCircles_1")] public void findCircles_1() < // получаем текущую БД Database db = HostApplicationServices.WorkingDatabase; // начинаем транзакцию using (Transaction tr = db.TransactionManager.StartTransaction()) < // получаем ссылку на пространство модели (ModelSpace) BlockTableRecord ms = (BlockTableRecord)tr.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForRead); // "пробегаем" по всем объектам в пространстве модели foreach (ObjectId id in ms) < // приводим каждый из них к типу Entity Entity entity = (Entity)tr.GetObject(id, OpenMode.ForRead); // если это окружность - выводим в консоль слой, тип и цвет каждого объекта if (entity.GetType() == typeof(Circle)) < acad.DocumentManager.MdiActiveDocument.Editor.WriteMessage(string.Format("\nLayer:; Type:; Color: ,,\n", entity.Layer, entity.GetType().ToString(), entity.Color.Red.ToString(), entity.Color.Green.ToString(), entity.Color.Blue.ToString())); > > tr.Commit(); > > // команда для поиска салатовых объектов (подход 1) [CommandMethod("Habr_FindLightGreenObjects_1")] public void findLightGreenObjects_1() < // получаем текущую БД Database db = HostApplicationServices.WorkingDatabase; // начинаем транзакцию using (Transaction tr = db.TransactionManager.StartTransaction()) < // получаем ссылку на пространство модели (ModelSpace) BlockTableRecord ms = (BlockTableRecord)tr.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForRead); // "пробегаем" по всем объектам в пространстве модели foreach (ObjectId id in ms) < // приводим каждый из них к типу Entity Entity entity = (Entity)tr.GetObject(id, OpenMode.ForRead); // если цвет объекта - салатовый, то выводим в консоль слой, тип и цвет каждого объекта if (entity.Color == Autodesk.AutoCAD.Colors.Color.FromRgb(128, 255, 64)) < acad.DocumentManager.MdiActiveDocument.Editor.WriteMessage(string.Format("\nLayer:; Type:; Color: ,,\n", entity.Layer, entity.GetType().ToString(), entity.Color.Red.ToString(), entity.Color.Green.ToString(), entity.Color.Blue.ToString())); > > tr.Commit(); > > // команда для поиска всех вхождений блока "block-1" (подход 1) [CommandMethod("Habr_FindBlocks_1")] public void findBlocks_1() < // получаем текущую БД Database db = HostApplicationServices.WorkingDatabase; // начинаем транзакцию using (Transaction tr = db.TransactionManager.StartTransaction()) < // получаем ссылку на пространство модели (ModelSpace) BlockTableRecord ms = (BlockTableRecord)tr.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForRead); // "пробегаем" по всем объектам в пространстве модели foreach (ObjectId id in ms) < // приводим каждый из них к типу Entity Entity entity = (Entity)tr.GetObject(id, OpenMode.ForRead); // проверяем, является ли объект вхождением блока if (entity.GetType() == typeof(BlockReference)) < // если является - приводим его к типу BlockReference BlockReference br = (BlockReference)entity; // если имя соответствующего определения блока - "block-1", то выводим в консоль слой, тип и цвет каждого объекта if (br.Name == "block-1") < acad.DocumentManager.MdiActiveDocument.Editor.WriteMessage(string.Format("\nLayer:; Type:; Color: ,,\n", entity.Layer, entity.GetType().ToString(), entity.Color.Red.ToString(), entity.Color.Green.ToString(), entity.Color.Blue.ToString())); > > > tr.Commit(); > > ////////////////////////////////////////////////////////////// // ВТОРОЙ ВАРИАНТ РЕАЛИЗАЦИИ ПОИСКА (>) ////////////////////////////////////////////////////////////// // команда для итерации по объектам (подход 2) [CommandMethod("Habr_IterateThroughAllObjects_2")] public void iterateThroughAllObjects_2() < // получаем БД и Editor текущего документа Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; // пытаемся получить ссылки на все объекты // ВНИМАНИЕ! Нужно проверить работоспособность метода с замороженными и заблокированными слоями! PromptSelectionResult selRes = ed.SelectAll(); // если произошла ошибка - сообщаем о ней if (selRes.Status != PromptStatus.OK) < ed.WriteMessage("\nError!\n"); return; >// получаем массив ID объектов ObjectId[] ids = selRes.Value.GetObjectIds(); // начинаем транзакцию using (Transaction tr = db.TransactionManager.StartTransaction()) < // "пробегаем" по всем полученным объектам foreach (ObjectId id in ids) < // приводим каждый из них к типу Entity Entity entity = (Entity)tr.GetObject(id, OpenMode.ForRead); // выводим в консоль слой (entity.Layer), тип (entity.GetType().ToString()) и цвет (entity.Color) каждого объекта acad.DocumentManager.MdiActiveDocument.Editor.WriteMessage(string.Format("\nLayer:; Type:; Color: ,,\n", entity.Layer, entity.GetType().ToString(), entity.Color.Red.ToString(), entity.Color.Green.ToString(), entity.Color.Blue.ToString())); > tr.Commit(); > > // команда для поиска окружностей (подход 2) [CommandMethod("Habr_FindCircles_2")] public void findCircles_2() < // получаем БД и Editor текущего документа Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; // создаем переменную, в которой будут содержаться данные для фильтра TypedValue[] filterlist = new TypedValue[1]; // первый аргумент (0) указывает, что мы задаем тип объекта // второй аргумент ("CIRCLE") - собственно тип filterlist[0] = new TypedValue(0, "CIRCLE"); // создаем фильтр SelectionFilter filter = new SelectionFilter(filterlist); // пытаемся получить ссылки на объекты с учетом фильтра // ВНИМАНИЕ! Нужно проверить работоспособность метода с замороженными и заблокированными слоями! PromptSelectionResult selRes = ed.SelectAll(filter); // если произошла ошибка - сообщаем о ней if (selRes.Status != PromptStatus.OK) < ed.WriteMessage("\nError!\n"); return; >// получаем массив ID объектов ObjectId[] ids = selRes.Value.GetObjectIds(); // начинаем транзакцию using (Transaction tr = db.TransactionManager.StartTransaction()) < // "пробегаем" по всем полученным объектам foreach (ObjectId id in ids) < // приводим каждый из них к типу Entity Entity entity = (Entity)tr.GetObject(id, OpenMode.ForRead); // выводим в консоль слой, тип и цвет каждого объекта acad.DocumentManager.MdiActiveDocument.Editor.WriteMessage(string.Format("\nLayer:; Type:; Color: ,,\n", entity.Layer, entity.GetType().ToString(), entity.Color.Red.ToString(), entity.Color.Green.ToString(), entity.Color.Blue.ToString())); > tr.Commit(); > > ////////////////////////////////////////////////////////////// // РЕДАКТИРОВАНИЕ ОБЪЕКТОВ ////////////////////////////////////////////////////////////// // команда для удаления окружностей [CommandMethod("Habr_EraseCircles_2")] public void eraseCircles_2() < // получаем БД и Editor текущего документа Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; // создаем переменную, в которой будут содержаться данные для фильтра TypedValue[] filterlist = new TypedValue[1]; // первый аргумент (0) указывает, что мы задаем тип объекта // второй аргумент ("CIRCLE") - собственно тип filterlist[0] = new TypedValue(0, "CIRCLE"); // создаем фильтр SelectionFilter filter = new SelectionFilter(filterlist); // пытаемся получить ссылки на объекты с учетом фильтра // ВНИМАНИЕ! Нужно проверить работоспособность метода с замороженными и заблокированными слоями! PromptSelectionResult selRes = ed.SelectAll(filter); // если произошла ошибка - сообщаем о ней if (selRes.Status != PromptStatus.OK) < ed.WriteMessage("\nError!\n"); return; >// получаем массив ID объектов ObjectId[] ids = selRes.Value.GetObjectIds(); // начинаем транзакцию using (Transaction tr = db.TransactionManager.StartTransaction()) < // "пробегаем" по всем полученным объектам foreach (ObjectId id in ids) < // приводим каждый из них к типу Entity Entity entity = (Entity)tr.GetObject(id, OpenMode.ForRead); // открываем приговоренный объект на запись entity.UpgradeOpen(); // удаляем объект entity.Erase(); >tr.Commit(); > > // команда для перекраски всех объектов в оранжевый цвет [CommandMethod("Habr_RepaintOrange_2")] public void repaintOrange_2() < // получаем БД и Editor текущего документа Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; // пытаемся получить ссылки на все объекты // ВНИМАНИЕ! Нужно проверить работоспособность метода с замороженными и заблокированными слоями! PromptSelectionResult selRes = ed.SelectAll(); // если произошла ошибка - сообщаем о ней if (selRes.Status != PromptStatus.OK) < ed.WriteMessage("\nError!\n"); return; >// получаем массив ID объектов ObjectId[] ids = selRes.Value.GetObjectIds(); // начинаем транзакцию using (Transaction tr = db.TransactionManager.StartTransaction()) < // "пробегаем" по всем полученным объектам foreach (ObjectId id in ids) < // приводим каждый из них к типу Entity Entity entity = (Entity)tr.GetObject(id, OpenMode.ForRead); // открываем объект на запись entity.UpgradeOpen(); // изменяем цвет на оранжевый entity.Color = Autodesk.AutoCAD.Colors.Color.FromRgb(255, 128, 0); >tr.Commit(); > > // команда для изменения радиуса окружностей [CommandMethod("Habr_ModifyCircles_2")] public void modifyCircles_2() < // получаем БД и Editor текущего документа Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; // создаем переменную, в которой будут содержаться данные для фильтра TypedValue[] filterlist = new TypedValue[1]; // первый аргумент (0) указывает, что мы задаем тип объекта // второй аргумент ("CIRCLE") - собственно тип filterlist[0] = new TypedValue(0, "CIRCLE"); // создаем фильтр SelectionFilter filter = new SelectionFilter(filterlist); // пытаемся получить ссылки на объекты с учетом фильтра // ВНИМАНИЕ! Нужно проверить работоспособность метода с замороженными и заблокированными слоями! PromptSelectionResult selRes = ed.SelectAll(filter); // если произошла ошибка - сообщаем о ней if (selRes.Status != PromptStatus.OK) < ed.WriteMessage("\nError!\n"); return; >// получаем массив ID объектов ObjectId[] ids = selRes.Value.GetObjectIds(); // начинаем транзакцию using (Transaction tr = db.TransactionManager.StartTransaction()) < // "пробегаем" по всем полученным объектам foreach (ObjectId id in ids) < // приводим каждый из них к типу Circle Circle cir = (Circle)tr.GetObject(id, OpenMode.ForRead); // открываем объект на запись cir.UpgradeOpen(); // увеличиваем радиус cir.Radius = cir.Radius * 2; >tr.Commit(); > > // команда для перемещения блоков [CommandMethod("Habr_MoveBlocks_2")] public void moveBlocks_2() < // получаем БД и Editor текущего документа Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; // создаем переменную, в которой будут содержаться данные для фильтра TypedValue[] filterlist = new TypedValue[1]; // первый аргумент (0) указывает, что мы задаем тип объекта // второй аргумент ("INSERT") - собственно тип filterlist[0] = new TypedValue(0, "INSERT"); // создаем фильтр SelectionFilter filter = new SelectionFilter(filterlist); // пытаемся получить ссылки на объекты с учетом фильтра // ВНИМАНИЕ! Нужно проверить работоспособность метода с замороженными и заблокированными слоями! PromptSelectionResult selRes = ed.SelectAll(filter); // если произошла ошибка - сообщаем о ней if (selRes.Status != PromptStatus.OK) < ed.WriteMessage("\nError!\n"); return; >// получаем массив ID объектов ObjectId[] ids = selRes.Value.GetObjectIds(); // начинаем транзакцию using (Transaction tr = db.TransactionManager.StartTransaction()) < // "пробегаем" по всем полученным объектам foreach (ObjectId id in ids) < // приводим каждый из них к типу BlockReference BlockReference br = (BlockReference)tr.GetObject(id, OpenMode.ForRead); // открываем объект на запись br.UpgradeOpen(); // перемещаем вхождение блока br.Position = Point3d.Origin; >tr.Commit(); > > ////////////////////////////////////////////////////////////// // БОНУСНЫЙ ВАРИАНТ РЕАЛИЗАЦИИ ПОИСКА (>) ////////////////////////////////////////////////////////////// // команда для итерации по объектам (подход 3) // Осторожно! Используется черная магия! [CommandMethod("Habr_IterateThroughAllObjects_3")] public void iterateThroughAllObjects_3() < Database db = HostApplicationServices.WorkingDatabase; Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; long amount = 0; Dictionaryd = new Dictionary(); ObjectId for (long i = db.BlockTableId.Handle.Value; i < db.Handseed.Value; i++) < Handle h = new Handle(i); if (db.TryGetObjectId(h, out id) && !id.IsNull && id.IsValid && !id.IsErased) < string t = id.ObjectClass.DxfName; amount++; if (d.ContainsKey(t)) d[t]++; else d.Add(t, 1); >> foreach (KeyValuePair kvp in d) ed.WriteMessage("\n: ", kvp.Key, kvp.Value); ed.WriteMessage("\nTotal objects in drawing\n", amount); > > > >
Как всегда, буду рад любым отзывам и комментариям.