Создание плагинов для autocad с помощью net api часть 4
Это шестая часть цикла про разработку плагинов для AutoCAD. В ней поговорим про поиск объектов на чертеже, а также про их изменение.
В первой части статьи мы рассмотрим поиск объектов на чертеже, во второй — коротко поговорим про их изменение. Однако прежде чем начать рассмотрение этих вопросов, давайте подготовим каркас тестового примера, который мы будем использовать на протяжении всей статьи.
Пример будет несложным, но довольно объемным. Мы создадим два слоя с именами «layer-1» и «layer-2» (итого вместе с нулевым слоем, который есть в каждом чертеже, получится три слоя). Также мы добавим два определения блока с именами «block-1» (он будет состоять из окружности, линии и полилинии) и «block-2» (он будет состоять из окружности и двух линий). После этого поместим на чертеж несколько графических объектов:
- на нулевой слой: три линии, полилинию, окружность, текст;
- на слой «layer-1»: две линии, окружность, текст, вхождение блока «block-1»;
- на слой «layer-2»: линию, дугу, окружность, вхождение блока «block-1», вхождение блока «block-2».
Ну и для полного счастья мы раскрасим все это дело в розовый, голубой и салатовый цвета.
Выбор не слишком стандартных цветов обусловлен тем, что подготавливать примеры кода к статьям — тоска смертная, и хочется хоть как-то этот процесс оживить.
Раскрасив первые линии, я вдруг вспомнил про Радужный флаг и подумал даже, не вставить ли его в статью…
Но во-вторых, я не так уж сильно поддерживаю это движение, а во-первых, флаг — это сильно сложнее трех линеек. Ну его нафиг.
Код, реализующий этот план действий, приведен далее. Все выполняемые операции: создание слоев и определений блока, вставка текста, графических примитивов и вхождений блока — мы уже рассматривали в прошлых статьях цикла. Единственное, что еще не рассматривалось, — это задание слоя и цвета для объекта чертежа, но оно занимает буквально одну строку кода и, я надеюсь, сложностей не вызовет.
Код несложный и принципиальных вопросов вызывать не должен. Давайте быстро пробежимся по некоторым нюансам.
Для начала заметим, что создание всех необходимых объектов происходят прямо во время загрузки плагина. Поскольку создаваемые объекты нужны нам для работы примеров и необходимы для работы любой из команд плагина, их вроде бы можно создавать в рамках метода Initialize() .
Однако насколько это правильно и разумно — вопрос очень спорный. Во-первых, создавая что-то автоматически, мы тем самым лишаем пользователя выбора и ставим его перед веселым фактом: привет, чувак, у тебя на чертеже теперь десять новых слоев и сто новых объектов!
Весело будет, но не слишком. Особенно пользователю.
Вторая неприятность состоит в том, что пользователь может случайно или намеренно удалить с чертежа некоторые из созданных объектов. И в этом случае он никак не сможет заново создать эти объекты; для этого ему придется закрыть и заново запустить AutoCAD.
В общем, с выполнением действий при загрузке плагина надо быть «нежнее, еще нежнее» ©. Шутки с этим вполне допустимы в учебном примере, однако на практике стоит трижды подумать перед использованием Initialize() . В идеале здесь должны находиться только бетон и металлоконструкции те участки кода, которые:
- Никак не помешают пользователю и не создадут излишнюю нагрузку на чертеж.
- Никогда не потребуют повторного вызова (либо же у пользователя должен быть способ этот вызов сделать — вспомните пример из абзаца выше про случайное удаление объектов чертежа).
Сейчас с содроганием зашел в репозиторий с кодом реального проекта, зажмурился, открыл глаза…
Уф, все в порядке. Изложенные выше правила почти соблюдены.
…
Как там говорится-то? «Кто не может работать — учит»?
При заполнении объектами нулевого слоя (функция layer_0_createObjects() ) принадлежность к этому слою явно указывается для каждого создаваемого объекта. Пример:
Это понятно и несложно, однако при работе с большим количеством объектов можно запросто забыть задать слой, что нехорошо.
Пример из жизни: автор статьи добросовестно скопипастил в функцию layer_1_createObjects() код для вставки на чертеж линии из функции layer_0_createObjects() , забыв поменять слой на новый. Поиски ошибки и ее исправление заняли какое-то время. Осознав, что так придется делать со всеми примитивами, автор выбесился и решил все сделать другим способом.
Так родилась эта часть статьи.)
Альтернативный способ, уменьшающий вероятность подобной ошибки, был кратко упомянут в одной из предыдущих статей, посвященной слоям. Это использование свойства Clayer .
Суть способа состоит в том, что если при создании объекта мы вообще не укажем ему слой, то он будет присвоен автоматически на основе значения свойства Clayer базы данных текущего документа. Просмотреть это значение можно так:
Присваивается значение аналогично:
А вот так можно получить ObjectID слоя, зная его имя:
В нашем примере, чтобы не обращаться каждый раз к таблице слоев, я создал две глобальные переменные — layer_1 и layer_2 , в которые сохраняю ObjectID слоев «layer-1» и «layer-2» при их создании. А позже, в процедурах layer_1_createObjects() и layer_2_createObjects() , я использую следующую конструкцию:
Зачем нужно сохранять текущее значение Clayer и восстанавливать его в конце функции?
Ну, одна из причин — чтобы пользователь после работы нашей функции смог продолжить добавлять объекты на тот же слой, что и до работы нашей процедуры.
Вторая причина — чтобы самому не путаться и не выяснять потом, почему это в половине запусков объекты добавляются не туда.
Если подключить немого воображения, то, наверное, можно придумать и еще причины. Но зачем?
Источник: «Ривелти групп»
Хотелось бы еще раз сделать акцент на том, что этот цикл статей рассказывает об основах создания плагинов. Для использования в реальном проекте приведенные примеры должны быть доработаны — в частности, необходимо позаботиться о том, что все используемые объекты реально существуют.
В нашем примере такие проверки не проводятся. Посмотрите: я помещаю графические объекты на созданные ранее слои, помещаю на чертеж вхождения созданных при загрузке блоков —, но нигде не проверяю, что эти слои и определения блоков существуют. В реальных проектах пренебрегать такими проверками настоятельно не рекомендуется. Особенно это касается случаев, когда между получением ссылки на объект и операцией над объектом могут произойти какие-нибудь действия пользователя.
В разделе, посвященном редактированию объектов чертежа, я опишу еще несколько ошибок, которые также необходимо отслеживать.
Теперь, когда мы обсудили особенности примера, можно наконец-то перейти к поиску объектов на чертеже. Мы разберем два разных подхода: просмотр объектов чертежа через обращение к объекту ModelSpace и получение идентификаторов объектов чертежа с помощью метода Editor.SelectAll() .
Можно искать объекты и другими способами. Но тут я уже ничем не помогу, ибо лично с ними не сталкивался.
Ближе к концу этой статьи будет ма-аленький бонусный нанораздел, где можно будет оценить масштабы извращений блестящих технических решений, которые приходится использовать людям программистам для оптимизации работы с большим количеством объектов.
2.1 Итерация по всем объектам чертежа
Немного истории: упоминание этого метода есть в одной из статей Kean Walmsley (англ.). Кроме того, описание есть на AutoCAD DevBlog (англ.) и форуме Сообщества программистов Autodesk в СНГ (rus).
Если в предыдущем варианте мы просматривали каждый объект в отдельности, то здесь концепция меняется: мы сразу получаем массив идентификаторов всех интересующих нас объектов, при необходимости отфильтровывая нужные.
Начнем, как обычно, с примера итерации по всем объектам.
Мы вызываем метод Editor.SelectAll() . Поскольку никаких фильтров не задано, нам должны вернуться идентификаторы ( ObjectID ) всех объектов на чертеже. Они записываются в переменную типа PromptSelectionResult . На всякий случай нужно убедиться, что метод отработал корректно — для этого мы проверяем статус результата ( PromptSelectionResult.Status ). Если что-то не в порядке, значение этого сво
Решил рассказать о своем опыте работы с AutoCAD. Может быть, кому-то это поможет – ну или хотя бы интересным покажется.
Предыстория
Началось все достаточно просто: в очередной раз почувствовав острую нехватку денег, я решил, что пора бы уже начать их где-нибудь разыскивать. И вот после пары недель поиска на «Фрилансим» обнаружилась вакансия разработчика для создания программы, взаимодействующей с AutoCAD.
Скажу сразу: до того дня общаться с AutoCAD мне не доводилось. Однако объявление содержало в себе фразу «Опыт работы не требуется», которая наполнила мою душу надеждой. Я связался с разместившим вакансию человеком и получил тестовое задание.
Для пробы предлагалось создать на чертеже пару объектов, а также вывести текст. Несколько дней я искал информацию об API и пытался подружиться с непривычной программой. В конце концов фигуры были нарисованы, текст выведен, а тестовое задание отправлено на проверку. И через несколько дней я неожиданно узнал, что принят! Чудеса, да и только.
1. Подготовка необходимых инструментов
1.1. Собственно AutoCAD
Тут все понятно. Качайте с официального сайта Autodesk, ставьте, 30 дней наслаждайтесь прекрасным инструментом. Потом узнайте цену покупки и повесьтесь. Для разработчиков действует специальная программа ADN , по которой можно получать девелоперские лицензии на продукты Autodesk. Стоимость базовой версии подписки, как указано на сайте, составляет от 700 долларов в год.
1.2. ObjectARX SDK – набор библиотек, необходимых для работы с AutoCAD
Последние три-четыре версии библиотек можно бесплатно скачать тут после регистрации. Более ранние придется поискать – скажем, тут. На всякий случай продублирую список прямо здесь – не такой уж он и длинный:
Версия SDK и ссылка для загрузки | Совместимость с версиями AutoCAD |
---|---|
2011 | 2011, 2012 |
2010 | 2010, 2011, 2012 |
2009 | 2009 |
2008 x86 | 2008, 2009 x86 |
2008 x64 | 2008, 2009 x64 |
2007 | 2007, 2008, 2009 x86 |
2006 | 2006 |
2005 | 2005, 2006 |
2004 | 2004, 2005, 2006 |
2002 | 2002 |
2000i | 2000i, 2002 |
2000 | 2000, 2000i, 2002 |
R14 | R14 |
Лично меня в свое время очень заинтересовал вопрос обратной совместимости ObjectARX. Как-то раз заказчик спросил: «А с какими версиями AutoCAD сможет работать программа?», и мне пришлось изрядно времени потратить на поиски ответа. В целом, ответ звучит так: «Autodesk поддерживает обратную совместимость в течение трех лет ». Какие версии совместимы между собой, можно посмотреть под спойлером выше.
Пока задачи перекомпилировать программу с другими библиотеками у меня не возникало. Думаю, что это хорошо: перспектива создавать отдельную версию продукта для других выпусков AutoCAD не радует совершенно.
1.3. MS Visual Studio 2013 Express
Великолепная IDE! Больше про нее и говорить-то нечего. Ссылок для скачивания масса – например, вот.
Можно, конечно, использовать и более ранние версии. Я начинал работу над проектом в MS Visual Studio 2010, но потом решил перейти на более современный выпуск.
1.4. Поисковик, усидчивость, здравый смысл
- нет привычных всплывающих подсказок о назначении класса, свойства или метода;
- нет подробной справки.
В конце этой статьи приведен список ресурсов, на которых можно позадавать вопросы и, если повезет, получить на них ответы.
2. Создание проекта библиотеки
Вкратце продублирую основные этапы:
2.1. Создать проект «Библиотека классов» («Class Library»)
Эти DLL-файлы находятся в папке с именем inc- . Так, в моем случае я добавляю ссылки на библиотеки из папки C:\ObjectARX 2010\inc-win32.
у меня дома установлена 32-разрядная ОС, у заказчика – 64-разрядная. Пока серьезных проблем с совместимостью не возникало. Но однажды я все же напоролся на то, что у меня функция возвращала Int32 , а у заказчика – Int64 . Линковщик ОЧЕНЬ расстраивался. Нужно иметь эту особенность в виду.
Первое знакомство с API у меня заключалось в лихорадочных попытках скомпилировать хоть какой-нибудь из примеров, щедро разбросанных по Сети. И что сλка характерно, компилироваться они упорно не хотели, ругаясь на неизвестные пространства имен и классы. В попытках собрать свой первый проект я с горя включил в него чуть ли не все DLL-файлы, которые шли с ObjectARX. Плохой способ – так делать не надо.
Ну, это вопрос не ко мне. Я только могу сказать, что в начале примеров обычно идет перечисление используемых пространств имен – скажем, так:
То, что начинается со слова «Autodesk», – это и есть те дроиды пространства имен, которые мы ищем. Теперь осталось разыскать контейнеры, которые их содержат. Исчерпывающего перечня сопоставлений я найти не смог, поэтому все проверялось методом научного тыка. Если есть более правильный способ, было бы интересно его узнать…
А пока – вот список DLL-файлов, которые я использую в проекте, и содержащихся в них пространств имен:
Теперь в любом месте этого файла можно вместо Autodesk.AutoCAD.ApplicationServices.Application писать acadApp .
Есть смысл провернуть такую операцию с наиболее часто употребляемыми классами. Непременно так делайте, код будет компактнее и понятнее. Цинизм данного совета заключается в том, что к тому моменту, когда вы наконец поймете, какие же классы являются у вас наиболее часто употребляемыми, что-то менять будет уже сильно лень.
2.3. Написать код плагина
Все очень просто. Вначале мы указываем необходимые пространства имен. Нам потребуются два.
Таким образом, всего необходимо добавить три ссылки:
UPD. 09.04.2018. Важное дополнение от lasteran: В новых версиях (предположительно начиная с AutoCAD 2013) класс CommandMethod содержится не в AcDBMgd.dll, а в AcCoreMgd.dll, так что придётся добавить ссылку ещё и на эту библиотеку. Учтите это, если работаете с новой версией!
Затем мы объявляем класс Commands . Именно он и будет «отправной точкой» плагина. Наш класс унаследован от интерфейса IExtensionApplication , поэтому в нем могут быть реализованы методы Initialize и Terminate . Первый из них автоматически выполняется при загрузке плагина, второй – при выгрузке.
AutoCAD не предоставляет разработчику возможность выгрузить плагин после того, как он будет загружен. Поэтому реально метод Terminate будет вызываться только в одном случае – при закрытии самого AutoCAD.
Почитать поподробнее про методы Initialize и Terminate можно тут (rus) и там (англ.).
Чтобы «превратить» созданный метод в команду AutoCAD, применяется атрибут CommandMethod . В скобках после него указывается имя создаваемой команды, которое можно будет использовать непосредственно в среде AutoCAD.
После сборки этого проекта у нас получится готовый к употреблению плагин.
2.4. Загрузить созданный плагин
Нужно запустить AutoCAD и выполнить команду «NETLOAD»:
Затем в открывшемся окне указать путь к файлу плагина:
Если при загрузке плагина произошла критическая ошибка, она будет выведена в консоль AutoCAD:
если плагин не смог загрузиться из-за ошибки, то перед тестированием очередного (исправленного) варианта нужно закрыть и заново запустить AutoCAD. В противном случае он может отказаться загружать плагин, даже если ошибок в коде уже не будет.
Теперь, когда плагин загружен, можно выполнить нашу тестовую команду:
… и увидеть результат:
Работает. Теперь можно закрывать AutoCAD.
2.5. Отладить плагин (при необходимости)
Процедура запуска плагина для отладки очень хорошо расписана в этом посте Tepliuk.
В этой статье будут разобраны самые простые операции, которые могут потребоваться разработчику: создание и размещение на чертеже графических примитивов (линия, ломаная, окружность, эллипс и круг).
Если уважаемый читатель захочет повторить изложенные примеры на своем компьютере, то для упрощения жизни стоит изучить автозагрузку плагинов. Например, можно почитать пост Namolem — там есть упоминание об этом.
Кратко изложу суть проблемы: поскольку после загрузки плагина «выгрузить» его невозможно, единственный способ запустить плагин, код которого был изменен, — это закрыть AutoCAD, затем вновь запустить его и повторно загрузить плагин. Эта пустяковая операция занимает от силы пару минут, однако уже после пятидесяти-ста повторений она начинает дико раздражать, я бы даже сказал — бесить.
Автозагрузка плагина позволяет исключить один из этапов этой процедуры, что поможет сберечь пару тысяч нервных клеток.
Добавление на чертеж линии не представляет никакого труда. Давайте это проделаем, использовав в качестве основы пример, изложенный в документации (перевод).
Во-вторых, нужно добавить ссылки на библиотеки AcDbMgd.dll и AcMgd.dll и в свойствах этих ссылок запретить копирование библиотек в целевую папку проекта (установить параметр CopyLocal в False ).
Сам код крайне прост, подробно расписывать здесь особо нечего.
Во-первых, мы начинаем транзакцию (что это и зачем нужно, рассматривалось в прошлой статье).
Во-вторых, мы открываем пространство модели ( Model Space ) — это, собственно, и есть наш чертеж. Более четкое определение Model Space можно найти тут и там. Поскольку мы будем добавлять на чертеж новый объект, нужно запросить доступ «Чтение и запись» ( OpenMode.ForWrite ).
В-третьих, мы создаем графический объект (в нашем случае — линию), указав в конструкторе необходимые параметры (в нашем случае — начальную и конечную точки).
В-четвертых, мы должны задать для объекта все свойства. В данном примере мы устанавливаем всем свойствам заданные по умолчанию значения, вызвав для этого функцию SetDatabaseDefaults() . Скупое мужское описание этой функции можно найти в ObjectARX Reference.
на практике для простых случаев вызовом этой функции можно пренебречь, однако есть мнение, что лучше так не делать.
В-пятых, необходимо добавить созданный объект в пространство модели (проще говоря — на чертеж). Это делается с помощью функции AppendEntity() .
В-шестых, необходимо добавить созданный объект к транзакции, в рамках которой мы работаем. Для этого используется функция AddNewlyCreatedDBObject() , которая уже разбиралась в предыдущей статье.
Наконец, после выполнения всех нужных нам операций мы должны зафиксировать транзакцию, вызвав метод Commit() .
После компиляции проекта запускаем AutoCAD, загружаем наш плагин и выполняем команду HabrCommand. Результат прост — но зато, надеюсь, понятен и предсказуем.
Если после запуска команды линия на экране так и не появилась — не спешите расстраиваться. Вместо этого убедитесь, что центр координат — точка (0;0) — находится в пределах видимой рабочей области. Кроме того, есть смысл поменять масштаб, покрутив колесом мыши.
Для начинающих пользователей AutoCAD может стать сюрпризом то, что масштабирование чертежа происходит в определенных пределах. Проще говоря, с ходу перейти от масштаба 1000% к масштабу 1% невозможно — после пары прокруток колеса мыши масштаб «упрется» в невидимый ограничитель. Чтобы продолжить масштабирование чертежа, необходимо выполнить команду REGEN в командной строке AutoCAD.
из личного опыта: если программист вконец упоролся, и ему уже непонятно, какие объекты создаются и создаются ли вообще, то можно открыть чистый чертеж, выполнить на нем интересующий кусок кода и выделить все объекты на чертеже, нажав комбинацию клавиш «Ctrl+A». После этого найти созданные объекты, как правило, не составляет никакого труда.
Давайте освежимся примером.
Ну и где здесь линия?)
Притворившуюся пылинкой на мониторе линию с головой выдают синий квадратик и появившееся окно свойств объекта.
Пояснять здесь особенно нечего. При желании с помощью добавления новых точек полилинию можно превратить в троллейбус довольно сложную геометрическую фигуру — например, звездочку…
Да, я тоже заметил, что у троллейбуса нет колес. Но окружности мы рисовать еще не умеем, а квадратные колеса… Нет, пусть уж лучше едет так.
Рассмотрим пример добавления на чертеж окружности, прилежно перепечатанный из документации (ссылки были выше).
Никаких сложностей с добавлением на чертеж окружности быть не должно: все, что нужно, — это установить центр и радиус.
При работе с непрямыми линиями стоит помнить, что в ряде случаев они могут не совсем корректно отображаться на экране.
В частности, если в этом примере выполнить команду HabrCommand при большом отдалении, а затем сильно увеличить масштаб (приблизить элементы чертежа), то будет наблюдаться эффект, представленный на картинке ниже. Те, кто часто работает с AutoCAD, наверняка к такому привыкли — а вот я, впервые увидев, во что превратилась моя окружность, очень долго искал ошибку в коде и убил несколько часов, пытаясь «повысить точность отрисовки».)
Для восстановления первозданной формы окружности можно выполнить в командной строке AutoCAD команду REGEN:
Еще раз подчеркну, что такой эффект возникает исключительно при отображении. Внутри документа окружность всегда остается окружностью, кем бы она ни казалась снаружи.
Создание эллипса несколько отличается от создания окружности: его параметры должны быть явно указаны в конструкторе (подробности приведены здесь).
Результат (синяя линия добавлена для пояснения):
- Центр (center) эллипса — с этим все понятно.
- Нормаль (normal) — вектор, перпендикулярный плоскости эллипса. Поскольку мы добавляем объект на плоскость XY, нормалью будет ось Z.
- Большая ось (majorAxis) — в понятиях AutoCAD это вектор с длиной, равной половине «ширины» эллипса (как синий отрезок на рисунке). В обычной математике это называется «большая полуось». В нашем примере мы задали вектор длиной 100, по направлению совпадающий с осью абсцисс.
- Радиус (radiusRatio) — определяет, насколько вытянут эллипс. В данном примере максимальная координата эллипса по оси Y составит 100 * 0.5 = 50.
- Начальный и конечный углы — определяют начальный и конечный углы отрисовки кривых. Принцип неплохо поясняет картинка ниже, которая взята отсюда.
Попробуем задать эллипсу новые параметры:
Получим следующее (линия проведена для удобства ориентирования):
Поскольку startAng больше нуля, верхняя часть эллипса начинается не от оси, а чуть дальше. Поскольку endAng сильно не дотягивает до 2pi, нижняя часть эллипса заканчивается значительно раньше положенного.
Наклон обеспечивается за счет задания вектора оси ( majorAxis = 100 * Vector3d.XAxis + 100 * Vector3d.YAxis ). Поскольку X-компонента этого вектора по модулю равна Y-компоненте, получаем наклон 45 градусов.
Длину полуоси можно посчитать по теореме Пифагора: это корень квадратный из суммы квадратов координат X и Y. Вычислив корень квадратный из 100 2 + 100 2 (итого 20000), получим около 141. Общая длина эллипса, соответственно, составит примерно 282.
Для создания круга можно использовать штриховку — Hatch (документация, перевод).
- Создаем границу штриховки. В качестве границы может выступать любая замкнутая кривая — в нашем случае это будет окружность.
- Задаем для границы штриховки необходимые свойства.
- Добавляем границу штриховки на чертеж.
- Создаем массив объектов, которые будут являться границами штриховки, и добавляем в него нашу границу.
- Создаем штриховку.
- Добавляем штриховку на чертеж.
- Задаем для штриховки необходимые свойства.
Часть, где мы создаем границу штриховки, проблем вызывать не должна — это просто окружность, которую мы создали и поместили на чертеж. Часть, где создается и добавляется на чертеж штриховка, тоже в пояснениях не нуждается. Осталась часть, где мы задаем параметры штриховки. Остановимся на ней поподробнее.
Вначале мы вызываем функцию setDatabaseDefaults() , которая устанавливает параметры штриховки равными параметрам по умолчанию. Назначение этой функции было рассмотрено в первом разделе.
Затем мы указываем тип штриховки с помощью функции SetHatchPattern() . В данном случае мы хотим нарисовать обычный круг, поэтому выбираем тип SOLID — «сплошная».
можно задать и другой тип штриховки, помимо сплошной. «Из коробки» AutoCAD предоставляет несколько десятков штриховок — чтобы их узнать, достаточно вызвать свойства объекта Hatch:
Вместо SOLID мы могли бы указать что-то другое. Например, указав HOUND , мы получили бы такой результат:
Помимо стандартных штриховок ( HatchPatternType.PreDefined ) можно использовать и сторонние, которые предварительно были подключены к AutoCAD. Вот ссылка на пример.
После задания типа штриховки я указал ее цвет (свойство Color ) с помощью функции FromRgb(byte ref, byte blue, byte green) .
Затем следует свойство Associative — оно определяет, будет ли штриховка менять размер при изменении размера ее границ.
Вот что произойдет с нашим примером ( Associative = true ) при изменении границ штриховки:
Новый радиус круга — 5, штриховка растянулась под новый радиус.
А вот пример с Associative = false :
Метод AppendLoop позволяет указать внешнюю границу штриховки и принимает на вход два параметра: тип границы и массив объектов-границ штриховки. В качестве первого параметра мы указали HatchLoopTypes.Outermost , тем самым обозначив, что хотим задать внешнюю границу штриховки. В качестве второго мы указали ранее созданный массив объектов-границ штриховки.
при необходимости можно задать несколько внутренних границ в дополнение к внешней и определить поведение штриховки при их пересечении. Подробности есть здесь; я же ограничусь простеньким примером такой фигуры.
Теперь все параметры созданной штриховки определены. Чтобы штриховка смогла отобразиться на экране, AutoCAD должен выполнить необходимые расчеты. Для выполнения этих расчетов необходимо вызвать функцию EvaluateHatch() .
Все необходимые для создания штриховки действия выполнены. Мы уже добавили ее на чертеж и в транзакцию на шаге 6. Теперь осталось зафиксировать транзакцию, чтобы AutoCAD сохранил внесенные нами изменения.
Иногда даже программисту приходится работать с чертежом прямо в AutoCAD (в одной из следующих статей я хотел рассказать о динамических блоках — там это бывает особенно актуально). В этом случае для создания круга проще всего воспользоваться командой DONUT в консоли AutoCAD. Ее синтаксис:
Вот две команды DONUT и результат их выполнения:
Кстати, какой круг больше — черный или белый?)
Вообще, я планировал здесь же рассказать про добавление текста и создание простых блоков, но что-то и так уже прилично набежало. Так что эти вопросы будут освещены в следующей статье.
Спасибо за внимание!
Любые отзывы, замечания и пожелания приветствуются в комментариях.)
Это очередная статья из цикла, посвященного разработке плагинов для AutoCAD. Речь в ней будет идти о базовых операциях со слоями в документе.
1. Общая информация
Слои могут сильно упростить жизнь не только инженеру, создающему чертеж, но и программисту. Например, все расположенные на одном слое элементы можно при необходимости очень быстро скрыть, а затем вновь отобразить на рабочей области.
В рамках этой статьи нам потребуются некоторые базовые сведения, которые в основном можно почерпнуть отсюда (зеркало)
1.1. Слой номер ноль
Любой документ AutoCAD всегда содержит в себе как минимум один слой — нулевой (с именем «0»), который невозможно удалить. Этот слой часто оказывается очень полезным — например, при удалении других слоев.
1.2. Текущий слой
Один из слоев документа обязательно должен являться текущим (current). На него помещаются все добавляемые на чертеж объекты (по крайней мере примитивные — с блоками дела обстоят несколько сложнее).
1.3. Свойства слоев
- включен ("on");
- заморожен ("freeze");
- заблокирован ("lock").
Флаг "включен" отвечает за видимость слоя. Если этот флаг сброшен, то все элементы, расположенные на слое, перестают отображаться на чертеже.
Флаг "заморожен" также отвечает за видимость слоя. По своему действию он аналогичен флагу «включен». Как указано в документации, это свойство позволяет повысить производительность на очень больших чертежах.
Флаг "заблокирован" отвечает за возможность редактирования слоя. Если этот флаг установлен, то все элементы, расположенные на слое, становятся недоступными для любых изменений (перемещение, удаление и т. п.). Кроме того, на заблокированный слой нельзя добавлять новые элементы.
Свойствами слоев можно управлять из AutoCAD. На расположенном ниже рисунке ядовито-зеленым выделена панель «Слои» (Layers), а фиолетовым выделен выпадающий список слоев, в левой части которого находятся переключатели свойств (лампочка, солнышко, открытый замок — «on», «freeze» и «lock» соответственно).
На эти переключатели действительно стоит обратить внимание: при написании плагина не всегда всё получается с первого раза, и порой приходится применять «ручное управление».
Некоторые сайты утверждают, что на этой картинке изображены не зеленый и фиолетовый цвета, а лайм и гелиотроп.
…
Наверное, я — собака.
1.4. Управление слоями
Самые простые действия со слоями были рассмотрены в предыдущем пункте. Для более сложных операций (например, создание или удаление слоя) нужно воспользоваться панелью «Свойства слоев» (Layer Properties), которая вызывается командой LAYER или щелчком по соответствующей иконке на панели «Слои» (обозначена оранжевым на рисунке в предыдущем подразделе). Вот как выглядит эта панель:
Особый интерес представляет выделенная рамкой секция кнопок. Первая кнопка в ней создает новый слой, третья — удаляет существующий, если это возможно.
- нулевой слой;
- текущий слой;
- слой, содержащий хотя бы один объект;
- слой, на который имеется ссылка в определении блока.
На самом деле список запрещенных к удалению слоев несколько длиннее:
К сожалению, автору данного поста не приходилось сталкиваться ни с Defpoints, ни с Xref-dependent layers, поэтому проработку этих элементарных вопросов оставим читателям в качестве несложного домашнего задания. ^__^
На этом с теорией все, можно приступать к практике.
2. Написание плагина
2.1. Создание нового проекта плагина
2.2. Добавление ссылок на необходимые библиотеки
2.3. Добавление нового слоя
Все присутствующие в документе слои хранятся в списке слоев. Для добавления на чертеж нового слоя достаточно добавить новый элемент в этот список.
Однако несмотря на простоту операции, кода получается довольно много.
После загрузки плагина и выполнения команды «TestCommand» на чертеже должен появиться новый слой:
В приведенном выше коде использовались две важные конструкции, которые часто встречаются при работе с объектами чертежа:
Документация Autocad говорит, что запросы на изменение объектов или на доступ к AutoCAD могут выполняться в любом контексте и от любого количества приложений. Чтобы предотвратить конфликты с другими запросами, программист должен сам обеспечить блокировку документа перед его изменением. Невыполнение этого требования в некоторых случаях приводит к возникновению ошибки при изменении базы данных документа.
После выполнения всех необходимых изменений в базе данных её следует разблокировать, используя метод Dispose() объекта DocumentLock . Также можно использовать блок using с объявлением в нём объекта DocumentLock — в этом случае при выходе из блока база данных разблокируется автоматически. (источник, зеркало)
По моему опыту, использование конструкции using гораздо удобнее, так как сводит к нулю вероятность забыть вызвать метод Dispose() .
В документации (зеркало) указано, что при работе с объектами, такими как отрезки, окружности и полилинии, или с таблицей идентификаторов и ее записями необходимо открывать объект для чтения или записи.
Попытки игнорировать это требование ни к чему хорошему не приведут — могут возникнуть весьма неприятные (в том числе с точки зрения отладки) ошибки.
- начало новой транзакции;
- открытие объекта на чтение или запись;
- если объект создается впервые (его еще нет в документе) — добавление его в базу данных документа;
- фиксация транзакции;
- завершение транзакции.
Как и в случае с DocumentLock , необходимо или вызывать метод Dispose() после окончания работы с транзакцией, или использовать конструкцию using .
Теперь, когда мы разобрались с блокировкой документа и транзакциями, можно перейти к разбору остальной части кода.
Во-первых, мы подключаем необходимые пространства имен:
Во-вторых, мы блокируем документ и начинаем транзакцию:
В-третьих, мы получаем ссылку на таблицу слоев документа:
Метод GetObject() принимает на вход два параметра: Id объекта, который необходимо открыть, и уровень доступа. Узнать Id таблицы слоев документа можно с помощью свойства LayerTableId базы данных этого документа. Поскольку нам необходимо изменить таблицу слоев (добавить в нее новый слой), мы используем уровень доступа «запись» ( OpenMode.ForWrite ). Наконец, метод GetObject() возвращает значение типа «объект» ( object ), которое необходимо явно привести к нужному нам типу ( LayerTable ).
В-четвертых, мы создаем новый слой:
Слой (а точнее, соответствующая ему запись в таблице слоев документа) имеет тип LayerTableRecord . Его конструктор не принимает никаких значений, поэтому всю инициализацию приходится проводить уже после создания.
В этом примере мы изменили только имя нового слоя, однако при необходимости можно задать и другие параметры, например видимость (свойство IsOff ) или доступность для редактирования (свойство IsLocked ).
В-пятых, мы заносим новый слой в таблицу слоев документа:
Таблица слоев — это класс, для которого реализован интерфейс IEnumerable . Для добавления нового элемента используется метод Add() .
В-шестых, мы добавляем новый слой в БД документа:
Поскольку до начала транзакции этого слоя не существовало, мы должны явно указать, что его необходимо добавить в БД документа. Если этого не сделать, запись добавлена не будет.
Важная деталь: функция AddNewlyCreatedDBObject должна вызываться не до, а после добавления слоя в таблицу слоев документа. Если попытаться поменять эти операции местами, то слой не будет добавлен. Стройного логического объяснения этому у меня нет; буду рад, если знающие люди поделятся разгадкой в комментариях.
В-седьмых, мы фиксируем транзакцию:
Если какие-либо элементы, открытые во время транзакции с помощью метода GetObject() , были изменены, то для сохранения этих изменений в БД документа необходимо зафиксировать транзакцию, вызвав метод Commit() . В противном случае никакие изменения сохранены не будут, и документ останется в состоянии, которое существовало на момент начала транзакции.
Наконец, мы заканчиваем транзакцию и снимаем блокировку документа:
Вот он, важный плюс использования конструкции using ! Без нее очень легко забыть вызвать метод Dispose() , тем более что сделать это нужно дважды — для фиксации транзакции и для разблокировки документа. А при использовании конструкции using метод Dispose() вызывается автоматически.
2.4. Установка текущего слоя
Получить текущий слой документа и установить новый можно с помощью свойства Clayer базы данных документа.
2.5. Изменение свойств и переименование слоя
Для изменения свойств слоя нужно открыть соответствующую запись в таблице слоев.
Перед операцией со слоем нелишним будет убедиться в его существовании с помощью метода Has() таблицы слоев документа.
Если мы загрузим плагин, выполним команду «TestCommand», а затем — команду «NewCommand», то увидим, что созданный нами слой стал скрытым, заблокированным и сменил имя. Теперь никто его не найдет.
2.6. Удаление слоя
Удаление слоя происходит во многом аналогично изменению. Для соответствующей записи в таблице слоев вызывается метод Erase() следующим образом:
- нулевой слой;
- текущий слой;
- слой, содержащий хотя бы один объект;
- слой, на который имеется ссылка в определении блока.
Если необходимо удалить слой, на котором присутствуют какие-либо объекты, то перед удалением слоя необходимо удалить все эти объекты или же перенести их на другой слой.
А в блоге Kean Walmsley есть шаблон для поиска всех размещенных на слое объектов. На базе этого примера можно написать функцию, удаляющую со слоя все объекты.
В любом случае, перед удалением слоя не лишним будет убедиться в корректности операции, а само удаление обрамить конструкцией try . catch .
В этом коде нам уже знакомо все, кроме метода Purge() . Принцип его действия описан в документации: он анализирует коллекцию объектов и на выходе оставляет в ней только те объекты, на которые никто не ссылается.
В рассмотренном примере метод Purge() вызывается для коллекции, в которой находится только один объект — удаляемый слой. Если на этот слой никто не ссылается, то после вызова метода Purge() он останется в коллекции, и тогда можно приступать к удалению. Если же вызов метода Purge() вернет пустую коллекцию, то на удаляемый слой имеются ссылки — вероятно, это размещенные на нем объекты.
Если мы загрузим плагин и выполним команду «TestCommand», а затем — команду «NewCommand», то увидим, что созданный нами слой удалится с чертежа.
2.7. Запрет затемнения заблокированных слоев
В завершение хотелось бы рассказать еще об одном параметре, связанном со слоями.
По умолчанию заблокированные слои в AutoCAD отображаются затемненными. Максимально возможное затемнение составляет 90% (как у прямоугольника на рисунке ниже).
Для изменения степени затемнения служит переменная LAYLOCKFADECTL. Ее значение устанавливается соответствующим ползунком на панели управления слоями (обведен на рисунке зеленым цветом).
Для изменения значения переменной LAYLOCKFADECTL внутри плагина можно воспользоваться функцией SetSystemVariable :
В результате выполнения этого кода затемнение заблокированных слоев будет отключено.
Перед изменением переменной LAYLOCKFADECTL стоит сохранить ее текущее значение, чтобы вернуть переменную в исходное состояние после того, как плагин закончит работу.
К сожалению, метод Terminate() не всегда отрабатывает корректно, и исходное значение может не восстановиться. Но мы хотя бы попытались.)
На этом пока все. В следующий раз напишу о создании простых объектов и блоков.
2014-09-05 в 6:31, admin , рубрики: autocad, CAD/CAM, плагин, метки: Autocad, c++, плагин
Решил рассказать о своем опыте работы с AutoCAD. Может быть, кому-то это поможет – ну или хотя бы интересным покажется.
Предыстория
Началось все достаточно просто: в очередной раз почувствовав острую нехватку денег, я решил, что пора бы уже начать их где-нибудь разыскивать. И вот после пары недель поиска на «Фрилансим» обнаружилась вакансия разработчика для создания программы, взаимодействующей с AutoCAD.
Скажу сразу: до того дня общаться с AutoCAD мне не доводилось. Однако объявление содержало в себе фразу «Опыт работы не требуется», которая наполнила мою душу надеждой. Я связался с разместившим вакансию человеком и получил тестовое задание.
Для пробы предлагалось создать на чертеже пару объектов, а также вывести текст. Несколько дней я искал информацию об API и пытался подружиться с непривычной программой. В конце концов фигуры были нарисованы, текст выведен, а тестовое задание отправлено на проверку. И через несколько дней я неожиданно узнал, что принят! Чудеса, да и только.
1. Подготовка необходимых инструментов
1.1. Собственно AutoCAD
Тут все понятно. Качайте с официального сайта Autodesk, ставьте, 30 дней наслаждайтесь прекрасным инструментом. Потом узнайте цену покупки и повесьтесь. Для разработчиков действует специальная программа ADN , по которой можно получать девелоперские лицензии на продукты Autodesk. Стоимость базовой версии подписки, как указано на сайте, составляет от 700 долларов в год.
1.2. ObjectARX SDK – набор библиотек, необходимых для работы с AutoCAD
Последние три-четыре версии библиотек можно бесплатно скачать тут после регистрации. Более ранние придется поискать – скажем, тут. На всякий случай продублирую список прямо здесь – не такой уж он и длинный:
Версия SDK и ссылка для загрузки | Совместимость с версиями AutoCAD |
---|---|
2011 | 2011, 2012 |
2010 | 2010, 2011, 2012 |
2009 | 2009 |
2008 x86 | 2008, 2009 x86 |
2008 x64 | 2008, 2009 x64 |
2007 | 2007, 2008, 2009 x86 |
2006 | 2006 |
2005 | 2005, 2006 |
2004 | 2004, 2005, 2006 |
2002 | 2002 |
2000i | 2000i, 2002 |
2000 | 2000, 2000i, 2002 |
R14 | R14 |
Лично меня в свое время очень заинтересовал вопрос обратной совместимости ObjectARX. Как-то раз заказчик спросил: «А с какими версиями AutoCAD сможет работать программа?», и мне пришлось изрядно времени потратить на поиски ответа. В целом, ответ звучит так: «Autodesk поддерживает обратную совместимость в течение трех лет ». Какие версии совместимы между собой, можно посмотреть под спойлером выше.
Пока задачи перекомпилировать программу с другими библиотеками у меня не возникало. Думаю, что это хорошо: перспектива создавать отдельную версию продукта для других выпусков AutoCAD не радует совершенно.
1.3. MS Visual Studio 2013 Express
Великолепная IDE! Больше про нее и говорить-то нечего. Ссылок для скачивания масса – например, вот.
Можно, конечно, использовать и более ранние версии. Я начинал работу над проектом в MS Visual Studio 2010, но потом решил перейти на более современный выпуск.
1.4. Поисковик, усидчивость, здравый смысл
- нет привычных всплывающих подсказок о назначении класса, свойства или метода;
- нет подробной справки.
В итоге информацию я черпал из файлов помощи Object ARX (у меня они установлены в папку с именем C:ObjectARX 2010docs), а также из многочисленных форумов, блогов и сообществ разработчиков AutoCAD. Признаться, больше помогало последнее, чем первое. :)
В конце этой статьи приведен список ресурсов, на которых можно позадавать вопросы и, если повезет, получить на них ответы.
2. Создание проекта библиотеки
Вкратце продублирую основные этапы:
2.1. Создать проект «Библиотека классов» («Class Library»)
Эти DLL-файлы находятся в папке с именем inc- . Так, в моем случае я добавляю ссылки на библиотеки из папки C:ObjectARX 2010inc-win32.
у меня дома установлена 32-разрядная ОС, у заказчика – 64-разрядная. Пока серьезных проблем с совместимостью не возникало. Но однажды я все же напоролся на то, что у меня функция возвращала Int32 , а у заказчика – Int64 . Линковщик ОЧЕНЬ расстраивался. Нужно иметь эту особенность в виду.
Первое знакомство с API у меня заключалось в лихорадочных попытках скомпилировать хоть какой-нибудь из примеров, щедро разбросанных по Сети. И что сλка характерно, компилироваться они упорно не хотели, ругаясь на неизвестные пространства имен и классы. В попытках собрать свой первый проект я с горя включил в него чуть ли не все DLL-файлы, которые шли с ObjectARX. Плохой способ – так делать не надо.
Ну, это вопрос не ко мне. Я только могу сказать, что в начале примеров обычно идет перечисление используемых пространств имен – скажем, так:
То, что начинается со слова «Autodesk», – это и есть те дроиды пространства имен, которые мы ищем. Теперь осталось разыскать контейнеры, которые их содержат. Исчерпывающего перечня сопоставлений я найти не смог, поэтому все проверялось методом научного тыка. Если есть более правильный способ, было бы интересно его узнать…
А пока – вот список DLL-файлов, которые я использую в проекте, и содержащихся в них пространств имен:
Контейнер «AcMgd» (файл «AcMgd.dll»):
Контейнер «AcDbMgd» (файл «AcDbMgd.dll»):
Контейнер «AdWindows» (файл «AdWindows.dll»):
Контейнер «AcCui» (файл AcCui.dll»):
Теперь в любом месте этого файла можно вместо Autodesk.AutoCAD.ApplicationServices.Application писать acadApp .
Есть смысл провернуть такую операцию с наиболее часто употребляемыми классами. Непременно так делайте, код будет компактнее и понятнее. Цинизм данного совета заключается в том, что к тому моменту, когда вы наконец поймете, какие же классы являются у вас наиболее часто употребляемыми, что-то менять будет уже сильно лень.
2.3. Написать код плагина
Все очень просто. Вначале мы указываем необходимые пространства имен. Нам потребуются два.
Таким образом, всего необходимо добавить три ссылки:
Затем мы объявляем класс Commands . Именно он и будет «отправной точкой» плагина. Наш класс унаследован от интерфейса IExtensionApplication , поэтому в нем могут быть реализованы методы Initialize и Terminate . Первый из них автоматически выполняется при загрузке плагина, второй – при выгрузке.
AutoCAD не предоставляет разработчику возможность выгрузить плагин после того, как он будет загружен. Поэтому реально метод Terminate будет вызываться только в одном случае – при закрытии самого AutoCAD.
Почитать поподробнее про методы Initialize и Terminate можно тут (rus) и там (англ.).
Чтобы «превратить» созданный метод в команду AutoCAD, применяется атрибут CommandMethod . В скобках после него указывается имя создаваемой команды, которое можно будет использовать непосредственно в среде AutoCAD.
После сборки этого проекта у нас получится готовый к употреблению плагин.
2.4. Загрузить созданный плагин
Нужно запустить AutoCAD и выполнить команду «NETLOAD»:
Затем в открывшемся окне указать путь к файлу плагина:
Если при загрузке плагина произошла критическая ошибка, она будет выведена в консоль AutoCAD:
если плагин не смог загрузиться из-за ошибки, то перед тестированием очередного (исправленного) варианта нужно закрыть и заново запустить AutoCAD. В противном случае он может отказаться загружать плагин, даже если ошибок в коде уже не будет.
Теперь, когда плагин загружен, можно выполнить нашу тестовую команду:
… и увидеть результат:
Работает. Теперь можно закрывать AutoCAD.
2.5. Отладить плагин (при необходимости)
Процедура запуска плагина для отладки очень хорошо расписана в этом посте Tepliuk.
Финал
Ну что же – для первого раза достаточно. Осталось привести обещанные ссылки. В посте Namolem и посте n00buK уже приведен большой объем источников; часть из них я продублирую здесь.
На этом моя статья закончена. Если ее признают годной – напишу еще несколько простых заметок о том, с чем сталкивался, как-то:
1.2 Поиск объектов заданного типа
Давайте попробуем выделить все окружности. Сделаем это на базе предыдущего примера.
Действовать будем так: просмотрим по порядку все объекты чертежа, определим тип каждого объекта; если объект окажется окружностью — выведем информацию о нем в консоль. В прошлой статье нам уже встречалась ссылка (англ.), где описаны пять способов узнать тип объекта. Как всегда, не будем отступать от традиций и используем один из самых простых способов:
Аналогично можно попробовать другие типы. Например, найти все линии:
Найти все аннотации (текстовые элементы):
Все вхождения блоков:
А если вдруг подзабудете, как именно называется тип, соответствующий объекту — можно просто добавить такой объект на чертеж вручную и запустить пример итерации по объектам — он выведет название типа в консоль AutoCAD.
1.1 Итерация по всем объектам чертежа
Для начала давайте посмотрим, как можно перебрать в цикле все объекты, имеющиеся на чертеже.
Принцип простой: открываем пространство модели ( ModelSpace ) и получаем ссылки на все объекты внутри него. Затем приводим эти объекты к типу Entity и обрабатываем нужные нам свойства.
Пример почти полностью заимствован отсюда (англ.).
Результат, как можно видеть, полностью совпадает с ожиданиями: у нас 16 объектов, цвета которых чередуются.
Из небольших новшеств: в этом примере мы использовали незнакомое нам свойство WorkingDatabase класса HostApplicationServices . Оно позволяет получить БД документа, который активен (имеет фокус ввода) в настоящий момент. Еще одно новшество — метод GetBlockModelSpaceId() класса SymbolUtilityServices , который позволяет быстро получить ObjectId пространства модели ( ModelSpace ).
Получив ссылку на пространство модели, мы просто пробегаемся по всем его объектам, приводим их к типу Entity (чтобы можно было просматривать их свойства) и выводим в консоль данные о слое, типе и цвете очередного объекта.
1.3 Поиск объектов с заданными свойствами
Попробуем вместо типа поискать какое-нибудь свойство — например, цвет. Найдем все салатовые объекты.
Это очень просто: строку
и смотрим, что получается.)
Аналогично можно осуществлять поиск и по другим атрибутам — например, по слою:
Сам я глубоко в свойства объектов не залезал. Советую при работе в Visual Studio просто набрать » Entity test; test. » — после этого IntelliSense высветит все доступные программисту свойства и методы. Можно пробежаться по этому списку и ознакомиться со свойствами и методами класса Entity:
Класс Entity задает свойства, характерные для любого объекта чертежа: слой, цвет и т. п. Если требуется осуществить поиск по свойствам, специфичным для какого-то конкретного класса объектов, то необходимо вначале найти все объекты такого типа, затем выполнить приведение к этому типу и просмотреть значение нужного свойства.
Для примера давайте найдем все вхождения блока «block-1». Класс Entity не содержит сведений об имени определения блока; зато эта информация есть в свойстве Name класса BlockReference .
Мы найдем два вхождения блока «block-1».
Читайте также: