Autocad net руководство по

Обзор руководства по созданию первого плагина для AutoCAD.
Являетесь ли вы продвинутым пользователем AutoCAD заинтересованным в повышении производительности? Хотели бы вы автоматизировать или расширить возможности AutoCAD, но являетесь новичком в программировании? Тогда это руководство для вас.
Данная инструкция предназначено для самостоятельного обучения и плавного входа в мир программирования. Это универсальный путь для пользователей, которые знают продукты Autodesk, но абсолютные новички в программировании. Вы будете работать с интерфейсом программирования AutoCAD.NET (API) и языком программирования Visual Basic.NET для создания плагина – модуля, загружаемого в AutoCAD для расширения его функциональности. По завершению этого руководства вы поймёте основы программирования .NET и способы их применения в AutoCAD.
Продукт: AutoCAD 2018*
Язык программирования: Visual Basic.NET
Программный интерфейс (API): AutoCAD.NET API
*Это руководство разработано для стандартного AutoCAD 2018. Однако все эти этапы применимы к любому вертикальному продукту, построенному на базе AutoCAD (AutoCAD Architecture, AutoCAD Civil 3D и т.д.). API AutoCAD.NET является общим для всех этих продуктов; каждая вертикаль добавляет собственный специализированный API поверх API AutoCAD.NET. Прилагаемый код будет работать и в AutoCAD 2013, 2014, 2015, 2016 и 2017.
Обзор
В интернете множество ресурсов, на которых можно узнать о API AutoCAD.NET. Однако эти ресурсы предназначены для людей знакомых с программированием. Это руководство отличается: оно не требует никаких дополнительных знаний о программировании и тем не менее помогает быстро создать свой первый плагин, не загружая вас деталями. У вас будет работающее приложение в течении часа с момента начала знакомства с этим материалом, независимо от вашего нынешнего уровня знаний о программировании.
Руководство начнётся с описания преимуществ кастомизации программного обеспечения Autodesk, прежде чем перейти к занятиям, посвящённым использованию API AutoCAD.NET. Занятия начнутся с создания рабочего плагина, затем будут даны подробные объяснения основополагающих принципов и будущего развития функциональности плагина.
Преимущества кастомизации AutoCAD
Время – деньги! Чем эффективнее вы выполняете рутинные операции, тем более конкурентоспособны на рынке. В этом кроется ключевое преимущество кастомизации: адаптация программного обеспечения с целью оптимизации или автоматизации рабочих процессов. Autodesk предоставляет мощные API и SDK (комплекты разработки программного обеспечения), которые позволяют вам получить большую отдачу от ваших инвестиций в программное обеспечение Autodesk, адаптировав его к потребностям вашего бизнеса – кастомизацией существующих функций или добавлением новых.
AutoCAD имеет четыре разных API — .NET, LISP, ActiveX и ObjectARX (С++). В этом руководстве используется .NET, так он предоставляет самую мощную и современную среду программирования – сочетание всеобъемлющий API AutoCAD .NET с самыми современными средствами программирования (Microsoft .NET Framework и Microsoft Visual Studio). .NET даёт вам доступ к любому из языков программирования совместимых с .NET (Visual Basic .NET, С#, F# и т.д.) для разработки плагинов. Хотя у каждого языка программирования есть свои сильные стороны, Visual Basic .NET является наилучшим выбором для новичков: он прост в изучении и использовании; он является наиболее удобочитаемым для человека; и (как и любой другой язык .NET), он даёт вам полный доступ к базе Microsoft .NET Framework. Как только вы освоите это руководство и наберётесь опыта работы с Visual Basic .NET, вы сможете перейти к решению более сложных задач, связанных с языком программирования.
Программирование может быть очень полезным. Надеемся, что вы тоже это почувствуете по завершению этих уроков. Наслаждайтесь!
Краткое описание плагина.
Данная инструкция описывает команду, которая позволяет вставлять в чертёж блок с атрибутами, которые всегда остаются параллельны оси Х независимости от угла поворота блока. То есть атрибуты блока всегда остаются горизонтальными в МСК (мировая система координат).
Горизонтальное положение текста для читателя является общим требованием, и этот простой плагин облегчает его выполнение. Готовый плагин можно легко расширить, чтобы охватить другие объекты аннотаций, такие как однострочный и многострочный текст; и отображать текст всегда горизонтально по отношению к видовому экрану, а не только к МСК.
Необходимое программное обеспечение.
1. AutoCAD.
Или любой другой продукт на базе AutoCAD. Данное руководство применимо ко всем продуктам на основе AutoCAD.
2. Microsoft® Visual Studio® Community.
В данном руководстве будет использоваться Visual Basic Express, но вы можете использовать любую профессиональную версию Visual Studio 2015. Некоторые элементы пользовательского интерфейса могут отличаться в зависимости от версии.
3. ObjectARX SDK.
ObjectARX SDK включает в себя документацию для AutoCAD .NET API и некоторые важные файлы, на которые будет ссылаться наш проект Visual Studio. Он также включает в себя ряд примеров проектов .NET (SDK содержит документацию и файлы для API ObjectARX (C++) b .NET API. Не стоит беспокоиться о всей документации для С++ — .NET намного проще).
4. Мастер-файлы AutoCAD.NET.
Мастер-файлы – это простые шаблоны, которые можно использовать для быстрого создания новых проектов AutoCAD .NET (Visual Basic или С#). Важно отметить что пользователи Visual Basic Express настраивают проект для запуска AutoCAD из отладчика – но это невозможно сделать из пользовательского интерфейса Visual Studio Express.

Оригинал статьи: http://usa.autodesk.com/adsk/servlet/index?siteID=123112&id=18162650

26/09/2014

Некоторое время назад я написал статью о том, как использовать метод Entity.Explode() чтобы получить те же результаты, что и в команде РАСЧЛЕНИ (_EXPLODE). В комментариях я получил замечание, что с этим лучше справляется метод BlockReference.ExplodeToOwnerSpace.

Мне захотелось использовать этот же метод для расчленения вложенных блоков, но так как BlockReference.ExplodeToOwnerSpace() не возвращает список созданных объектов, мне пришлось приспособить событие Database.ObjectAppended для этого и для рекурсивного вызова моей функции ExplodeBlock() со всеми вновь созданными блоками. Мы можем так же удалить оригинальный примитив (или примитивы если функция вызвана рекурсивно).

Опубликовано 26.09.2014
    Читать далее >>>

24/09/2014

Как средствами AutoCAD определить расположение точки относительно контура.Автор: Александр Ривилис

Вопрос:

Есть ли встроенные средства для определения как расположена точка относительно контура в AutoCAD .NET API?

Ответ:

Специальных средств для этого нет. Но есть по меньшей мере два способа, которыми можно воспользоваться для этого:

  1. С использованием BREP .NET API (Autodesk.AutoCAD.BoundaryRepresentation) – Если превратить контур в Region, то можно будет воспользоваться возможностью Brep для определения положения точки внутри Region. Для этого можно воспользоваться методом BrepEntity.GetPointContainment, который возвращает объект-перечисление PointContainment, которое может принимать одно из трёх значений: Inside (внутри), Outside (снаружи), OnBoundary (на самом контуре).
  2. C использованием класса примитива MPolygon. Обычно этот класс используется в Civil 3D, но так как он есть и в базовом AutoCAD, то мы можем им воспользоваться.

В данной статье мы рассмотрим второй способ. Будем считать, что в качестве контура у нас имеется замкнутая полилиния. В общем случае она может иметь и дуговые сегменты, но не должна быть самопересекающейся – иначе теряется смысл понятия «внутри контура». Ниже пример команды, которая просит пользователя указать точку и полилинию. Обратите внимание на следующие моменты:

Опубликовано 24.09.2014
    Читать далее >>>

23/09/2014

Использование потоков (Thread) для фоновой обработки

Вопрос:

Внутри моей команды я хочу запустить фоновую задачу для синхронизации с базой данных. Как только эта задача заканчивается я хотел бы при помощи AutoCAD .NET API сделать некоторые изменения в базе данных. Однако, когда я вызываю функции AutoCAD API из этой задачи, они не работают. Например, the MdiActiveDocument равен null.

Ответ:

AutoCAD .NET API не позволяют использовать мультизадачность. Необходимо вызывать функции API из главной задачи.

Если вы находитесь в другой задаче, вам необходимо настроить вызов из главной задачи. Самый простой способ достичь этого – это создать объект System.Windows.Forms.Control в главной задаче и использовать его метод Invoke() для запуска функции которая и выполнит окончательную обработку.

Опубликовано 23.09.2014
    Читать далее >>>

10/09/2014

Как удалить неиспользуемые и необработанные ссылки на данные (DataLink)?Автор: Александр Ривилис

Для необработанных ссылок на данные свойство IsValid всегда равно false. А вот с неиспользуемыми ссылками на данные несколько сложнее. Метод DataLink.GetTargets() возвращает коллекцию ObjectId объектов/примитивов, которые используют эту связь. Если таблица (Table) использовала связь, то её ObjectId содержится в коллекции DataLink.GetTargets(). Если эту таблицу стереть, то DataLink.GetTargets() не будет содержать ObjectId таблицы, а будет содержать ObjectId для объекта TableContent (это не примитив, т.е. не является наследником Entity). Таким образом, если DataLink.GetTargets() не содержит ObjectId ни одного примитива, то соответственно он не используется и его можно удалить. Следующий код это и показывает:

Опубликовано 10.09.2014
    Читать далее >>>

08/09/2014

Как получить неиспользуемые внешние ссылки, растры и подложки?Автор: Александр Ривилис

Вопрос: Мне нужно средствами AutoCAD .NET API получить неиспользуемые внешние ссылки (XREF), растры (IMAGE), pdf-подложки (PDFUNDERLAY) и dwf-подложки (DWFUNDERLAY). Как это можно сделать?

Ответ: Для внешних ссылок достаточно найти все записи таблицы блоков (BlockTableRecord), свойство XrefStatus которых отлично от NotAnXref и для них справедливо выражение GetBlockReferenceIds(false, true).Count == 0

Для растров и подложек процедура несколько иная. Необходимо найти соответствующую таблицу (ACAD_IMAGE_DICT – для IMAGE, ACAD_PDFDEFINITIONS – для PDFUNDERLAY, ACAD_DWFDEFINITIONS – для DWFUNDERLAY) и получить из неё все ObjectId элементов. После этого достаточно воспользоваться методом Database.Purge(), чтобы получить только те ObjectId элементов, которые не используются.

Опубликовано 08.09.2014
    Читать далее >>>

06/08/2014

Запись и чтение Lisp-переменных в .NET API

Мы знаем, что записывать и читать lisp-переменные можно при помощи P/Invoke функций acedGetSym/acedPutSym из ObjectARX. Однако начиная с AutoCAD 2013 мы можем воспользоваться методами SetLispSymbol и GetLispSymbol класса Document.

В следующем примере мы обратим внимание на запись нескольких фрагментов данных в lisp-переменную. Для этой цели используем тип TypedValue со значением перечисления LispDataType

Опубликовано 06.08.2014
    Читать далее >>>

26/06/2014

Установка набора предварительного выбора в .NET API

Вы можете воспользоваться методом Editor.SetImpliedSelection для установки набора предварительного выбора (выбора с ручками). Ниже код команды, которая запрашивает у пользователя выбрать примитивы и устанавливает набор предварительного выбора. Обратите внимание на флаги команды, которые являются обязательными для установки pickfirst :

Опубликовано 26.06.2014
    Читать далее >>>

24/06/2014

Вставка блока из другого DWG-файла с помощью .NET

С помощью метода WblockCloneObjects() можно скопировать блок из одного чертежа в другой. Код на C# показывает как можно использовать метод WblockCloneObjects чтобы скопировать определенный блок с именем «test» из чертежа, находящегося по пути «C:TEMPtest.dwg».

Опубликовано 24.06.2014
    Читать далее >>>

23/06/2014

Поиск удаленных примитивов

Чтобы получить запись таблицы блоков, в которой имеются удаленные примитивы, можно воспользоваться методом BlockTableRecord.IncludingErased. Например таким образом можно восстановить удаленные примитивы в блоке, как показано ниже.

Опубликовано 23.06.2014
    Читать далее >>>

18/06/2014

Изменение порядка отрисовки примитивов

Каждый блок в AutoCAD содержит информацию о порядке отрисовки примитивов в нём. Порядок отрисовки (DrawOrderTable) хранится в словаре расширения записи таблицы блоков. В .NET можно работать с этим словарем используя метод BlockTableRecord.DrawOrderTableId. Порядок отрисовки (DrawOrderTable), обеспечивает методы, такие как MoveToBottom, MoveToTop, MoveBelow, MoveAbove и так далее, которые позволяют изменить порядок отрисовки примитивов в блоке.

Опубликовано 18.06.2014
    Читать далее >>>

12/06/2014

Создание нового слоя и установка его текущим

Следующий код показывает процедуру создания нового слоя. Все слои хранятся в символьной таблице, называемой таблицей слоёв. Чтобы сделать любой слой текущим, нужно установить свойство Clayer объекта Database в идентификатор объекта слоя.

Опубликовано 12.06.2014
    Читать далее >>>

11/06/2014

Обновление выравнивания текста

В ряде случаев выравнивание текста примитива DBText не выполняется. Для того чтобы заставить выполнится выравнивание используется метод DBText.AdjustAlignment. Этот метод для работы использует рабочую базу данных. Таким образом особенно важно указать ему рабочую базу данных, когда DBText еще не добавлен в рабочую базу данных.

Опубликовано 11.06.2014
    Читать далее >>>

09/06/2014

Создание анонимного слоя

Чтобы создать анонимный слой необходимо вызвать метод AcDbLayerTableRecord::setIsHidden()как показано ниже в коде. Анонимные слои не видны в диспетчере слоёв и соответственно пользователь не может редактировать/удалить анонимный слой.

Опубликовано 09.06.2014
    Читать далее >>>

05/06/2014

Создание мультиполигона в AutoCAD средствами .NET API

Мультиполигон (MPolygon) – это пользовательский объект созданный для AutoCAD Map 3D. Но он доступен и в чистом AutoCAD и с ним можно работать средствами ObjectARX. Чтобы работать с ним в .NET необходимо добавить добавить ссылку на AcMPolygonMGD.dllи кроме того требуется, чтобы файл AcMPolygonObjNN.dbx (где NN = 19 для AutoCAD 2013 и 2014 и NN = 20 для AutoCAD 2015) загружен в AutoCAD до вызова любого из методов MPolygon. Оба этих файла содержатся в корневом каталоге AutoCAD.

Опубликовано 05.06.2014
    Читать далее >>>

CAD/CAM


Рекомендация: подборка платных и бесплатных курсов создания сайтов — https://katalog-kursov.ru/

В пятой части

цикла

, посвященного разработке плагинов под AutoCAD, я расскажу про создание простых блоков и размещение их на чертеже.

public static string disclaimer = "Автор не является профессиональным разработчиком и не обладает глубокими знаниями AutoCAD. Этот пост – просто небольшой рассказ о создании плагина."

Введение

Если при работе с чертежом возникает необходимость создания однотипных объектов, то лучше всего делать это с помощью механизма блоков — именованных групп объектов, которые ведут себя как единый объект. Общие сведения о блоках можно получить

здесь

.

Важно уметь четко разграничивать два понятия: определение блока и вхождение блока. Подробное описание различий приведено

здесь

(англ.) и

тут

(rus). Вкратце изложу суть: когда мы создаем новый блок, AutoCAD помещает его описание в специальную таблицу блоков. Это описание называется определением блока (block definition). Определение блока существует исключительно в таблице блоков и на чертеже не отображается. Непосредственно на поле чертежа AutoCAD помещает вхождение блока (block reference) — ссылку на определение блока. При изменении определения блока все вхождения блока повторяют эти изменения. Далее в статье я иногда буду опускать первое слово («определение» или «вхождение») и писать просто «блок».

NB:

Должен сказать, что лично мне перевод «экземпляр блока» нравится гораздо больше, чем «вхождение блока». Но раз уж так написано в документации…
Как говорится, почитай отца своего, почитай мать свою и почитай руководство по эксплуатации.

В процессе общения с Autocad я сталкивался с двумя видами блоков: обычными и динамическими. Главная особенность вторых — возможность задать их элементам некоторые «настраиваемые» параметры (например, длину линии в блоке или угол ее наклона). Главный минус динамических блоков в том, что их невозможно создать средствами .NET API.

Про динамические блоки я расскажу в другой раз. А пока посмотрим, как можно работать с обычными блоками.

Создание блока вручную средствами AutoCAD

Для начала давайте создадим блок непосредственно в AutoCAD. Программирования здесь не будет — но умение создать блок вручную полезно для того, чтобы быть в состоянии хотя бы приблизительно оценить результат работы плагина. Подробности процесса — под спойлером.

Если читатель уже знаком с редактированием блоков в AutoCAD, то из этого раздела ничего нового он для себя не почерпнет.

Итак, во-первых необходимо добавить на чертеж элементы, которые мы будем объединять в блок. Для начала добавим в блок простую окружность.

В командной строке AutoCAD выполним команду CIRCLE, затем укажем центр и радиус. На чертеже появится окружность:

Выделим окружность и выполним команду BLOCK. На экране появится меню создания блока. К аналогичному результату приведет нажатие кнопки «Create» на панели «Block»:

Зададим блоку имя, выставим единицы измерения «Unitless» и снимем флажок «Open in Block Editor», после чего нажмем «ОК»:

NB:

Выбор единиц измерения позволяет задать блоку размер, который будет автоматически пересчитываться в разных системах измерения (европейской, с метрами и миллиметрами — и американской, с футами и дюймами). Если выставить значение «Unitless», то блок будет использовать те единицы измерения, которые установлены в чертеже. Я всегда выставлял значение «Unitless»; хорошо это или плохо — не знаю.

После этого AutoCAD попросит указать на чертеже точку, которая будет являться для блока базовой. Обычно в качестве этой точки задают центр блока или его нижний левый угол. Укажем в качестве базовой точки центр окружности.

Мы завершили создание блока. Первое вхождение блока будет добавлено на чертеж автоматически и заменит собой элементы, из которых был создан этот блок. Чтобы добавить на чертеж несколько вхождений созданного блока, можно использовать команду INSERT или одноименную кнопку на панели «Block»:

После выполнения команды или нажатия на кнопку на экране появится окно вставки блока:

Выбираем в списке нужный блок, нажимаем «OK», указываем точку вставки — и блок появляется на чертеже. В том, что это именно блок, можно убедиться, выделив объект и посмотрев на окно свойств:

Для редактирования блоков используется редактор, который вызывается командой BEDIT или соответствующей кнопкой на панели «Block»:

После выполнения команды или нажатия на упомянутую кнопку на экране появится окно выбора определения блока:

В этом окне необходимо выбрать определение, которое мы будем редактировать, и нажать «OK». После этого AutoCAD откроет редактор блоков. Пробежимся по самым, на мой взгляд, важным кнопкам панели редактора:

  • Save Block — сохранить все сделанные изменения. После закрытия редактора блоков все вхождения блока, размещенные на чертеже, будут обновлены в соответствии с изменениями определения блока.
  • Authoring Palettes — показать или скрыть окно атрибутов. Это окно пригодится при работе с динамическими блоками.
  • Point — в этом выпадающем подменю находится пункт «Basepoint», с помощью которого можно задать блоку новую базовую точку.
  • Close Block Editor — закрыть редактор блоков. Все несохраненные изменения будут потеряны.

Внутри редактора можно добавлять к определению блока новые объекты и удалять ненужные. Редактирование блока происходит аналогично редактированию обычного чертежа.

На этом краткий экскурс в создание блоков в AutoCAD завершен. Можно переходить к коду. :-)

Создание блока с помощью AutoCAD .NET API

Пример создания простого блока

Этот раздел базируется на соответствующем

посте

в блоге Kean Walmsley (наш пример по сравнению с первоисточником будет заметно облегчен). Мы будем создавать простейший блок, состоящий из полилинии, окружности и блока текста.

немного о колбасных обрезках

Кстати, раз уж зашел разговор про колбасные обрезки…
За последние пару лет (боже, как время-то летит!) произошло много всего хорошего. В частности,

Самая Лучшая И Величайшая Компания В Мире

все-таки

выпустила

бесплатную профессиональную версию Visual Studio.
Да, Visual Studio Community Edition занимает больше места, чем Express, и интерфейс у нее чуть посложнее — но рано или поздно у разработчика обычно возникает необходимость работы с дополнениями IDE, а они в Express-версиях не поддерживаются. Такие дела.
Пример из жизни: автор этих строк весело писал плагин в VS Express, и ничто не предвещало беды… пока не встала задача добавить программу установки (msi-файл, который создается с помощью технологии Wix). В итоге весь код инсталлятора пришлось писать в Notepad++, ежеминутно сверяясь с документацией, и компилировать вручную.

А в Visual Studio Professional можно было бы просто подключить плагин для работы с Wix и делать все прямо в IDE — с подсказками, автодополнением кода и компиляцией прямо в проекте. Можно. Было бы…

В общем, если вы работаете в компании, у которой не больше 250 ПК и годовой доход менее $1 000 000, то очень рекомендую при наличии пары-тройки лишних, бессмысленных часов жизни потратить их на установку этого продукта и далее работать с ним.
Если же бессмысленных часов в вашей жизни нет, то осознайте, что все — тлен, жизнь — боль, а влачить свое существование без установленной Visual Studio Community Edition — значит лишь увеличивать энтропию этого мира.

Чтобы создать блок и поместить его вхождение на чертеж, необходимо сделать следующее:

  1. Создать в таблице блоков новую запись (определение блока).
  2. Добавить в определение блока необходимые геометрические объекты.
  3. Добавить на чертеж вхождение блока.

Итак, приступим.

Я уже не буду подробно останавливаться на таких мелких особенностях создания плагина, как подключение внешних ссылок, запрет CopyLocal, указание версии .NET и тому подобное — это неоднократно обговаривалось в прошлых постах цикла. В этом примере нам потребуются ссылки на библиотеки AcMgd и AcDBMgd. Кода будет немного; сразу приведу его весь, а потом разберем детали.

Код:

using System;
using System.IO;
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_SimpleBlock
{
    public class ClassMyAutoCADDLL_SimpleBlock
    {
        public class Commands : IExtensionApplication
        {
            // эта функция будет вызываться при выполнении в AutoCAD команды "HabrCommand"
            [CommandMethod("HabrCommand")]
            public void HabrCommand()
            {
                // получаем ссылки на документ и его БД
                Document doc = Application.DocumentManager.MdiActiveDocument;
                Database db = doc.Database;

                // поле документа "Editor" понадобится нам для вывода сообщений в окно консоли AutoCAD
                Editor ed = doc.Editor;

                // имя создаваемого блока
                const string blockName = "pvtBlock";

                // начинаем транзакцию
                Transaction tr = db.TransactionManager.StartTransaction();
                using (tr)
                {
                    //***
                    // ШАГ 1 - создаем новую запись в таблице блоков
                    //***

                    // открываем таблицу блоков на запись
                    BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForWrite);

                    // вначале проверяем, нет ли в таблице блока с таким именем
                    // если есть - выводим сообщение об ошибке и заканчиваем выполнение команды
                    if (bt.Has(blockName))
                    {
                        ed.WriteMessage("nA block with the name "" + blockName + "" already exists.");
                        return;
                    }

                    // создаем новое определение блока, задаем ему имя
                    BlockTableRecord btr = new BlockTableRecord();
                    btr.Name = blockName;

                    // добавляем созданное определение блока в таблицу блоков и в транзакцию,
                    // запоминаем ID созданного определения блока (оно пригодится чуть позже)
                    ObjectId btrId = bt.Add(btr);
                    tr.AddNewlyCreatedDBObject(btr, true);

                    //***
                    // ШАГ 2 - добавляем к созданной записи необходимые геометрические примитивы
                    //***

                    // создаем полилинию
                    Polyline poly = new Polyline();
                    poly.SetDatabaseDefaults();
                    poly.AddVertexAt(0, new Point2d(-50, -125), 0, 0, 0);
                    poly.AddVertexAt(1, new Point2d(-50, 105), 0, 0, 0);
                    poly.AddVertexAt(2, new Point2d(-20, 125), 0, 0, 0);
                    poly.AddVertexAt(3, new Point2d(20, 125), 0, 0, 0);
                    poly.AddVertexAt(4, new Point2d(50, 105), 0, 0, 0);
                    poly.AddVertexAt(5, new Point2d(50, -125), 0, 0, 0);
                    poly.AddVertexAt(6, new Point2d(-50, -125), 0, 0, 0);

                    // добавляем полилинию в определение блока и в транзакцию
                    btr.AppendEntity(poly);
                    tr.AddNewlyCreatedDBObject(poly, true);

                    // создаем окружность
                    Circle cir = new Circle();
                    cir.SetDatabaseDefaults();
                    cir.Center = new Point3d(0, 90, 0);
                    cir.Radius = 15;

                    // добавляем окружность в определение блока и в транзакцию
                    btr.AppendEntity(cir);
                    tr.AddNewlyCreatedDBObject(cir, true);

                    // создаем текст
                    DBText text = new DBText();
                    text.Position = new Point3d(-25, -95, 0);
                    text.Height = 35;
                    text.TextString = "BC";

                    // добавляем текст в определение блока и в транзакцию
                    btr.AppendEntity(text);
                    tr.AddNewlyCreatedDBObject(text, true);

                    //***
                    // ШАГ 3 - добавляем вхождение созданного блока на чертеж
                    //***

                    // открываем пространство модели на запись
                    BlockTableRecord ms = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);

                    // создаем новое вхождение блока, используя ранее сохраненный ID определения блока
                    BlockReference br = new BlockReference(Point3d.Origin, btrId);

                    // добавляем созданное вхождение блока на пространство модели и в транзакцию
                    ms.AppendEntity(br);
                    tr.AddNewlyCreatedDBObject(br, true);

                    // фиксируем транзакцию
                    tr.Commit();
                }
            }

            // функции Initialize() и Terminate() необходимы, чтобы реализовать интерфейс IExtensionApplication
            public void Initialize()
            {

            }

            public void Terminate()
            {

            }
        }
    }
}

Поcле загрузки плагина и выполнения команды HabrCommand получим такую картинку:

Теперь давайте разбираться, что происходит в коде.

Вначале мы проводим подготовительные операции: получаем ссылки на документ, его БД и свойство Editor, задаем имя нового блока и начинаем транзакцию. Ничего нового в этом нет, кроме разве что свойства Editor.

Разыскать подробную информацию по классу Editor я с ходу не смог. Буду рад, если кто-то из более знающих людей подскажет ссылку на вменяемое описание этого класса и его методов (или хотя бы где его найти в ObjectARX Reference). В этом примере класс Editor используется только для вывода сообщения о том, что блок с таким именем уже существует в БД. В AutoCAD это сообщение выглядит так:

Внутри транзакции мы действуем по алгоритму, изложенному в начале этого подраздела.

На первом шаге

мы создаем новую запись в таблице блоков, для чего:

  1. открываем таблицу блоков (запрашиваем доступ на запись, поскольку будем вносить изменения);
  2. проверяем, нет ли в таблице блока с таким именем (при попытке создать блок с именем, которое уже есть в таблице блоков, мы получим исключение eDuplicateRecordName, которое нам совершенно не нужно);
  3. создаем новое определение блока;
  4. задаем созданному определению блока имя;
  5. добавляем созданное определение блока в таблицу блоков и в транзакцию.

На втором шаге

мы добавляем к созданной записи необходимые геометрические примитивы. У нас их будет три: полилиния, окружность и текст.

Добавление на чертеж полилинии и окружности разбиралось в предыдущей статье; с текстом все происходит аналогично. Добавление к блоку графического объекта обычно происходит в три приема:

  1. создается необходимый объект;
  2. задаются свойства этого объекта;
  3. созданный объект добавляется в определение блока и в транзакцию.

Это применимо как к полилинии и окружности, так и к тексту. Какая часть кода за какой пункт отвечает — вполне очевидно. Почитать подробнее про добавление текста на чертеж можно

здесь

(англ.) и

тут

(rus).

На третьем шаге

мы добавляем вхождение созданного блока на чертеж. Порядок действий тут такой:

  1. открывается на запись пространство модели;
  2. создается новое вхождение блока;
  3. созданное вхождение блока добавляется на пространство модели и в транзакцию.

Остановимся чуть подробнее на конструкторе класса BlockReference, используемом при создании нового вхождения блока. Данный конструктор принимает два параметра: точку размещения блока (с этой точкой будет совмещена базовая точка вхождения блока) и ID определения блока. Иными словами, мы должны сообщить AutoCAD, какой блок мы хотим увидеть и где. В нашем примере в качестве точки размещения блока указано начало координат (Point3d.Origin), а ID определения блока мы сохранили еще на первом шаге, при создании этого определения.

Использование блоков, уже имеющихся в таблице блоков документа

Если в предыдущем примере выполнить команду HabrCommand, а затем удалить с чертежа появившийся блок и выполнить команду HabrCommand еще раз, то мы увидим сообщение «A block with the name „pvtBlock“ already exists», и на чертеже ничего не появится. Чтобы использовать ранее созданное определение блока, нужно узнать ObjectID этого определения. В нашем случае код можно переписать так:

Код команды:

[CommandMethod("HabrCommand")]
public void HabrCommand()
{
    // получаем ссылки на документ и его БД
    Document doc = Application.DocumentManager.MdiActiveDocument;
    Database db = doc.Database;

    // поле документа "Editor" понадобится нам для вывода сообщений в окно консоли AutoCAD
    Editor ed = doc.Editor;

    // имя создаваемого блока
    const string blockName = "pvtBlock";

    // начинаем транзакцию
    Transaction tr = db.TransactionManager.StartTransaction();
    using (tr)
    {
        //***
        // ШАГ 1 - создаем новую запись в таблице блоков (или получаем ID существующей)
        //***

        // открываем таблицу блоков на чтение
        BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);

        // проверяем, нет ли в таблице блока с таким именем
        // если есть - используем его ID
        ObjectId btrId;
        if (bt.Has(blockName))
        {
            btrId = bt[blockName];
        }
        else
        {
            // открываем таблицу блоков на запись
            bt.UpgradeOpen();

            // создаем новое определение блока, задаем ему имя
            BlockTableRecord btr = new BlockTableRecord();
            btr.Name = blockName;

            // добавляем созданное определение блока в таблицу блоков, сохраняем его ID
            btrId = bt.Add(btr);
            // добавляем созданное определение блока в транзакцию
            tr.AddNewlyCreatedDBObject(btr, true);

            //***
            // ШАГ 2 - добавляем к созданной записи необходимые геометрические примитивы
            //***

            // создаем полилинию
            Polyline poly = new Polyline();
            poly.SetDatabaseDefaults();
            poly.AddVertexAt(0, new Point2d(-50, -125), 0, 0, 0);
            poly.AddVertexAt(1, new Point2d(-50, 105), 0, 0, 0);
            poly.AddVertexAt(2, new Point2d(-20, 125), 0, 0, 0);
            poly.AddVertexAt(3, new Point2d(20, 125), 0, 0, 0);
            poly.AddVertexAt(4, new Point2d(50, 105), 0, 0, 0);
            poly.AddVertexAt(5, new Point2d(50, -125), 0, 0, 0);
            poly.AddVertexAt(6, new Point2d(-50, -125), 0, 0, 0);

            // добавляем полилинию в определение блока и в транзакцию
            btr.AppendEntity(poly);
            tr.AddNewlyCreatedDBObject(poly, true);

            // создаем окружность
            Circle cir = new Circle();
            cir.SetDatabaseDefaults();
            cir.Center = new Point3d(0, 90, 0);
            cir.Radius = 15;

            // добавляем окружность в определение блока и в транзакцию
            btr.AppendEntity(cir);
            tr.AddNewlyCreatedDBObject(cir, true);

            // создаем текст
            DBText text = new DBText();
            text.Position = new Point3d(-33, -95, 0);
            text.Height = 35;
            text.TextString = "BC";

            // добавляем текст в определение блока и в транзакцию
            btr.AppendEntity(text);
            tr.AddNewlyCreatedDBObject(text, true);
        }

        //***
        // ШАГ 3 - добавляем вхождение созданного блока на чертеж
        //***

        // открываем пространство модели на запись
        BlockTableRecord ms = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);

        // создаем новое вхождение блока, используя ранее сохраненный ID определения блока
        BlockReference br = new BlockReference(Point3d.Origin, btrId);

        // добавляем вхождение блока на пространство модели и в транзакцию
        ms.AppendEntity(br);
        tr.AddNewlyCreatedDBObject(br, true);

        // фиксируем транзакцию
        tr.Commit();
    }
}

Если теперь выполнить команду HabrCommand, затем удалить с чертежа появившийся блок и повторно выполнить команду HabrCommand, то на чертеже вновь появится вхождение нашего блока. При этом нового определения блока создано не будет — плагин возьмет запись, которая уже имеется в таблице блоков.

В рассмотренном примере я для разнообразия использовал метод UpgradeOpen(), который довольно часто фигурирует в примерах и документации. Давайте разберем, как он работает.

Если в первом примере мы сразу открывали таблицу блоков на запись (OpenMode.ForWrite), то теперь мы вначале открываем ее только на чтение (OpenMode.ForRead). Этого нам хватит, чтобы просмотреть таблицу и выяснить, содержится ли в ней блок с нашим именем. Если такой блок найдется, то мы можем прочитать его ObjectID, для чего нам опять-таки хватит доступа только на чтение. Если же блока с нашим именем в таблице нет, то нам нужно его добавить, и для этого уже потребуется доступ на запись. Чтобы получить такой доступ, используется метод UpgradeOpen() — после вызова этого метода мы можем работать с таблицей блоков, как будто открыли ее с уровнем доступа OpenMode.ForWrite.

NB:

Использование UpgradeOpen(), наверное, является хорошей практикой. По крайней мере, Kean Walmsley точно так делает. Но подробных данных о том, какие сокровища и жизненные блага достаются тем, кто использует UpgradeOpen(), у меня нет. Сам я в работе эту конструкцию не использовал и особых огорчений по этому поводу не испытывал. Если кто-то может представить свою позицию по использованию UpgradeOpen() — рад буду увидеть ее в комментариях или ЛС.

Изменение позиции вставки блока

В каком месте чертежа появится блок,

заранее предсказать невозможно

зависит от двух вещей:

  • от базовой точки, заданной в определении блока;
  • от точки вставки, заданной во вхождении блока.

В нашем примере базовая точка расположена в центре блока.

Картинка:

Заметим, что при создании определения блока мы нигде не задавали базовую точку в явном виде — в подобном случае AutoCAD считает, что базовой точкой является начало координат. В нашем примере «центр» фигуры был специально подобран так, чтобы он совпал с центром координат.

Изменить базовую точку определения блока можно вручную с помощью редактора блоков (как это сделать, говорилось в первом разделе поста) или средствами .NET API. В последнем случае для этой цели используется свойство Origin класса BlockTableRecord. В качестве примера давайте перенесем базовую точку определения блока в нижний левый угол. Для этого после указания имени блока зададим свойство Origin:

// задаем имя блока
btr.Name = blockName;

// задаем базовую точку
btr.Origin = new Point3d(-50, -125, 0);

Числа -50 и -125 — это минимальные координаты нашего блока по осям X и Y соответственно (такие координаты имеет нижняя левая вершина полилинии, образующей внешнюю границу нашего блока).

Результат:

Блок сдвинулся.

Теперь разберемся с точкой вставки блока. Она задается для каждого вхождения блока и показывает, где после вставки блока окажется его базовая точка. Это свойство задается в конструкторе класса BlockReference — например, так:

BlockReference br = new BlockReference(new Point3d(150, 150, 0), btrId);

После одновременного изменения базовой точки и точки вставки мы получим такую картину:

Блок сдвинулся опять.

Точку вставки блока можно также изменить с помощью свойства Position класса BlockReference:

BlockReference br = new BlockReference(Point3d.Origin, btrId);
br.Position = new Point3d(150, 150, 0);

Эффект будет таким же, как и при задании точки непосредственно в конструкторе класса BlockReference.

Использование блоков внутри блока

Мы тут все любим блоки, так что давайте-ка добавим пару блоков в наш блок, чтобы упростить чертеж, пока мы упрощаем чертеж.

Yo dawg!

Добавление блока в определение блока не представляет никакой сложности. Главная особенность заключается в том, что если уж мы добавляем в блок не геометрический примитив, а вхождение вспомогательного блока, то нам обязательно нужно позаботиться о том, чтобы определение этого вспомогательного блока имелось в таблице блоков документа.

Код команды:

[CommandMethod("HabrCommand")]
public void HabrCommand()
{
    // получаем ссылки на документ и его БД
    Document doc = Application.DocumentManager.MdiActiveDocument;
    Database db = doc.Database;

    // поле документа "Editor" понадобится нам для вывода сообщений в окно консоли AutoCAD
    Editor ed = doc.Editor;

    // имя создаваемого блока
    const string blockName = "ltBlock";
    // имя вспомогательного блока
    const string auxBlockName = "starBlock";

    // начинаем транзакцию
    Transaction tr = db.TransactionManager.StartTransaction();
    using (tr)
    {
        // открываем таблицу блоков на запись
        BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForWrite);

        //***
        // ШАГ 0 - создаем новую запись вспомогательного блока в таблице блоков (или получаем ID существующей)
        //***

        // проверяем, нет ли в таблице блока с таким именем
        // если есть - используем его ID
        ObjectId btrIdAux;
        if (bt.Has(auxBlockName))
        {
            btrIdAux = bt[auxBlockName];
        }
        else
        {
            // создаем новое определение блока, задаем ему имя
            BlockTableRecord btr = new BlockTableRecord();
            btr.Name = auxBlockName;

            // добавляем созданное определение блока в таблицу блоков, сохраняем его ID
            btrIdAux = bt.Add(btr);
            // добавляем созданное определение блока в транзакцию
            tr.AddNewlyCreatedDBObject(btr, true);

            // создаем полилинию
            Polyline poly = new Polyline();
            poly.SetDatabaseDefaults();
            poly.AddVertexAt(0, new Point2d(0, -6), 0, 0, 0);
            poly.AddVertexAt(1, new Point2d(-9, -12), 0, 0, 0);
            poly.AddVertexAt(2, new Point2d(-7, -2), 0, 0, 0);
            poly.AddVertexAt(3, new Point2d(-14, 6), 0, 0, 0);
            poly.AddVertexAt(4, new Point2d(-5, 6), 0, 0, 0);
            poly.AddVertexAt(5, new Point2d(0, 15), 0, 0, 0);
            poly.AddVertexAt(6, new Point2d(5, 6), 0, 0, 0);
            poly.AddVertexAt(7, new Point2d(14, 6), 0, 0, 0);
            poly.AddVertexAt(8, new Point2d(7, -2), 0, 0, 0);
            poly.AddVertexAt(9, new Point2d(9, -12), 0, 0, 0);
            poly.AddVertexAt(10, new Point2d(0, -6), 0, 0, 0);

            // добавляем полилинию в определение блока и в транзакцию
            btr.AppendEntity(poly);
            tr.AddNewlyCreatedDBObject(poly, true);
        }
        //***
        // определение вспомогательного блока создано (или получен ID этого определения)
        //***

        //***
        // ШАГ 1 - создаем новую запись в таблице блоков (или получаем ID существующей)
        //***

        // проверяем, нет ли в таблице блока с таким именем
        // если есть - используем его ID
        ObjectId btrId;
        if (bt.Has(blockName))
        {
            btrId = bt[blockName];
        }
        else
        {
            // создаем новое определение блока, задаем ему имя
            BlockTableRecord btr = new BlockTableRecord();
            btr.Name = blockName;

            // добавляем созданное определение блока в таблицу блоков, сохраняем его ID
            btrId = bt.Add(btr);
            // добавляем созданное определение блока в транзакцию
            tr.AddNewlyCreatedDBObject(btr, true);

            //***
            // ШАГ 2 - добавляем к созданной записи необходимые геометрические объекты
            //***

            // создаем полилинию
            Polyline polyExt = new Polyline();
            polyExt.SetDatabaseDefaults();
            polyExt.AddVertexAt(0, new Point2d(-50, -125), 0, 0, 0);
            polyExt.AddVertexAt(1, new Point2d(-50, 105), 0, 0, 0);
            polyExt.AddVertexAt(2, new Point2d(-20, 125), 0, 0, 0);
            polyExt.AddVertexAt(3, new Point2d(20, 125), 0, 0, 0);
            polyExt.AddVertexAt(4, new Point2d(50, 105), 0, 0, 0);
            polyExt.AddVertexAt(5, new Point2d(50, -125), 0, 0, 0);
            polyExt.AddVertexAt(6, new Point2d(-50, -125), 0, 0, 0);

            // добавляем полилинию в определение блока и в транзакцию
            btr.AppendEntity(polyExt);
            tr.AddNewlyCreatedDBObject(polyExt, true);

            // создаем еще одну полилинию
            Polyline polyIn = new Polyline();
            polyIn.SetDatabaseDefaults();
            polyIn.AddVertexAt(0, new Point2d(-5, -125), 0, 0, 0);
            polyIn.AddVertexAt(1, new Point2d(-5, 125), 0, 0, 0);
            polyIn.AddVertexAt(2, new Point2d(5, 125), 0, 0, 0);
            polyIn.AddVertexAt(3, new Point2d(5, -125), 0, 0, 0);

            // добавляем вторую полилинию в определение блока и в транзакцию
            btr.AppendEntity(polyIn);
            tr.AddNewlyCreatedDBObject(polyIn, true);

            // создаем окружность
            Circle cir = new Circle();
            cir.SetDatabaseDefaults();
            cir.Center = new Point3d(0, 90, 0);
            cir.Radius = 15;

            // добавляем окружность в определение блока и в транзакцию
            btr.AppendEntity(cir);
            tr.AddNewlyCreatedDBObject(cir, true);

            // добавляем звездочки в определение блока и в транзакцию
            BlockReference starLeft = new BlockReference(new Point3d(-27, -75, 0), btrIdAux);
            btr.AppendEntity(starLeft);
            tr.AddNewlyCreatedDBObject(starLeft, true);

            BlockReference starRight = new BlockReference(new Point3d(27, -75, 0), btrIdAux);
            btr.AppendEntity(starRight);
            tr.AddNewlyCreatedDBObject(starRight, true);
        }

        //***
        // ШАГ 3 - добавляем вхождение созданного блока на чертеж
        //***

        // открываем пространство модели на запись
        BlockTableRecord ms = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);

        // создаем новое вхождение блока, используя ранее сохраненный ID определения блока
        BlockReference br = new BlockReference(new Point3d(150, 150, 0), btrId);

        // добавляем вхождение блока на пространство модели и в транзакцию
        ms.AppendEntity(br);
        tr.AddNewlyCreatedDBObject(br, true);

        // фиксируем транзакцию
        tr.Commit();
    }
}

Результат:

Комментировать здесь, по большому счету, нечего: мы просто добавляем в таблицу блоков определение вспомогательного блока, а затем создаем вхождение вспомогательного блока и задаем ему координаты точки вставки.

В качестве несложного самостоятельного упражнения читателям предлагается выполнить покраску элементов блока и приведение их размеров и расположения в соответствие с Приказом № 1500.

Образец:

Зачем применять блоки?

Основных причин две… Начнем со второй.

Вторая причина

Когда на чертеже содержится большое количество однотипных объектов, использование блоков здорово сокращает размер файла чертежа. Прочувствовать это нам поможет следующий код:

Код плагина:

using System;
using System.IO;
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_SimpleBlock
{
    public class ClassMyAutoCADDLL_SimpleBlock
    {
        public class Commands : IExtensionApplication
        {
            // функция, рисующая набор геометрических фигур в заданной точке чертежа
            public void drawFigure(double x, double y)
            {
                // получаем ссылки на документ и его БД
                Document doc = Application.DocumentManager.MdiActiveDocument;
                Database db = doc.Database;

                // начинаем транзакцию
                Transaction tr = db.TransactionManager.StartTransaction();
                using (tr)
                {
                    // открываем таблицу блоков на запись
                    BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForWrite);

                    // открываем пространство модели на запись
                    BlockTableRecord ms = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);

                    // создаем полилинию
                    Polyline polyExt = new Polyline();
                    polyExt.SetDatabaseDefaults();
                    polyExt.AddVertexAt(0, new Point2d(x - 50, y - 125), 0, 0, 0);
                    polyExt.AddVertexAt(1, new Point2d(x - 50, y + 105), 0, 0, 0);
                    polyExt.AddVertexAt(2, new Point2d(x - 20, y + 125), 0, 0, 0);
                    polyExt.AddVertexAt(3, new Point2d(x + 20, y + 125), 0, 0, 0);
                    polyExt.AddVertexAt(4, new Point2d(x + 50, y + 105), 0, 0, 0);
                    polyExt.AddVertexAt(5, new Point2d(x + 50, y - 125), 0, 0, 0);
                    polyExt.AddVertexAt(6, new Point2d(x - 50, y - 125), 0, 0, 0);

                    // добавляем полилинию на пространство модели и в транзакцию
                    ms.AppendEntity(polyExt);
                    tr.AddNewlyCreatedDBObject(polyExt, true);

                    // создаем еще полилинию
                    Polyline polyIn = new Polyline();
                    polyIn.SetDatabaseDefaults();
                    polyIn.AddVertexAt(0, new Point2d(x - 5, y - 125), 0, 0, 0);
                    polyIn.AddVertexAt(1, new Point2d(x - 5, y + 125), 0, 0, 0);
                    polyIn.AddVertexAt(2, new Point2d(x + 5, y + 125), 0, 0, 0);
                    polyIn.AddVertexAt(3, new Point2d(x + 5, y - 125), 0, 0, 0);

                    // добавляем вторую полилинию на пространство модели и в транзакцию
                    ms.AppendEntity(polyIn);
                    tr.AddNewlyCreatedDBObject(polyIn, true);

                    // создаем окружность
                    Circle cir = new Circle();
                    cir.SetDatabaseDefaults();
                    cir.Center = new Point3d(x, y + 90, 0);
                    cir.Radius = 15;

                    // добавляем окружность на пространство модели и в транзакцию
                    ms.AppendEntity(cir);
                    tr.AddNewlyCreatedDBObject(cir, true);

                    // создаем звездочки, добавляем их на пространство модели и в транзакцию
                    Polyline starLeft= new Polyline();
                    starLeft.SetDatabaseDefaults();
                    starLeft.AddVertexAt(0, new Point2d(x - 27, y - 75 - 6), 0, 0, 0);
                    starLeft.AddVertexAt(1, new Point2d(x - 27 - 9, y - 75 - 12), 0, 0, 0);
                    starLeft.AddVertexAt(2, new Point2d(x - 27 - 7, y - 75 - 2), 0, 0, 0);
                    starLeft.AddVertexAt(3, new Point2d(x - 27 - 14, y - 75 + 6), 0, 0, 0);
                    starLeft.AddVertexAt(4, new Point2d(x - 27 - 5, y - 75 + 6), 0, 0, 0);
                    starLeft.AddVertexAt(5, new Point2d(x - 27, y - 75 + 15), 0, 0, 0);
                    starLeft.AddVertexAt(6, new Point2d(x - 27 + 5, y - 75 + 6), 0, 0, 0);
                    starLeft.AddVertexAt(7, new Point2d(x - 27 + 14, y - 75 + 6), 0, 0, 0);
                    starLeft.AddVertexAt(8, new Point2d(x - 27 + 7, y - 75 - 2), 0, 0, 0);
                    starLeft.AddVertexAt(9, new Point2d(x - 27 + 9, y - 75 - 12), 0, 0, 0);
                    starLeft.AddVertexAt(10, new Point2d(x - 27, y - 75 - 6), 0, 0, 0);

                    ms.AppendEntity(starLeft);
                    tr.AddNewlyCreatedDBObject(starLeft, true);

                    Polyline starRight = new Polyline();
                    starRight.SetDatabaseDefaults();
                    starRight.AddVertexAt(0, new Point2d(x + 27, y - 75 - 6), 0, 0, 0);
                    starRight.AddVertexAt(1, new Point2d(x + 27 - 9, y - 75 - 12), 0, 0, 0);
                    starRight.AddVertexAt(2, new Point2d(x + 27 - 7, y - 75 - 2), 0, 0, 0);
                    starRight.AddVertexAt(3, new Point2d(x + 27 - 14, y - 75 + 6), 0, 0, 0);
                    starRight.AddVertexAt(4, new Point2d(x + 27 - 5, y - 75 + 6), 0, 0, 0);
                    starRight.AddVertexAt(5, new Point2d(x + 27, y - 75 + 15), 0, 0, 0);
                    starRight.AddVertexAt(6, new Point2d(x + 27 + 5, y - 75 + 6), 0, 0, 0);
                    starRight.AddVertexAt(7, new Point2d(x + 27 + 14, y - 75 + 6), 0, 0, 0);
                    starRight.AddVertexAt(8, new Point2d(x + 27 + 7, y - 75 - 2), 0, 0, 0);
                    starRight.AddVertexAt(9, new Point2d(x + 27 + 9, y - 75 - 12), 0, 0, 0);
                    starRight.AddVertexAt(10, new Point2d(x + 27, y - 75 - 6), 0, 0, 0);

                    ms.AppendEntity(starRight);
                    tr.AddNewlyCreatedDBObject(starRight, true);

                    // фиксируем транзакцию
                    tr.Commit();
                }
            }

            // функция, рисующая блок в заданной точке чертежа
            public void drawBlock(double x, double y)
            {
                // получаем ссылки на документ и его БД
                Document doc = Application.DocumentManager.MdiActiveDocument;
                Database db = doc.Database;

                // поле документа "Editor" понадобится нам для вывода сообщений в окно сообщений AutoCAD
                Editor ed = doc.Editor;

                // имя создаваемого блока
                const string blockName = "ltBlock";
                const string auxBlockName = "starBlock";

                // начинаем транзакцию
                Transaction tr = db.TransactionManager.StartTransaction();
                using (tr)
                {
                    // открываем таблицу блоков на запись
                    BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForWrite);

                    //***
                    // ШАГ 0 - создаем новую запись вспомогательного блока в таблице блоков (или получаем ID существующей)
                    //***

                    ObjectId btrIdAux;
                    if (bt.Has(auxBlockName))
                    {
                        btrIdAux = bt[auxBlockName];
                    }
                    else
                    {
                        // создаем новое определение блока, задаем ему имя
                        BlockTableRecord btr = new BlockTableRecord();
                        btr.Name = auxBlockName;

                        // добавляем созданное определение блока в таблицу блоков, сохраняем его ID
                        btrIdAux = bt.Add(btr);
                        // добавляем созданное определение блока в транзакцию
                        tr.AddNewlyCreatedDBObject(btr, true);

                        // создаем полилинию
                        Polyline poly = new Polyline();
                        poly.SetDatabaseDefaults();
                        poly.AddVertexAt(0, new Point2d(0, -6), 0, 0, 0);
                        poly.AddVertexAt(1, new Point2d(-9, -12), 0, 0, 0);
                        poly.AddVertexAt(2, new Point2d(-7, -2), 0, 0, 0);
                        poly.AddVertexAt(3, new Point2d(-14, 6), 0, 0, 0);
                        poly.AddVertexAt(4, new Point2d(-5, 6), 0, 0, 0);
                        poly.AddVertexAt(5, new Point2d(0, 15), 0, 0, 0);
                        poly.AddVertexAt(6, new Point2d(5, 6), 0, 0, 0);
                        poly.AddVertexAt(7, new Point2d(14, 6), 0, 0, 0);
                        poly.AddVertexAt(8, new Point2d(7, -2), 0, 0, 0);
                        poly.AddVertexAt(9, new Point2d(9, -12), 0, 0, 0);
                        poly.AddVertexAt(10, new Point2d(0, -6), 0, 0, 0);

                        // добавляем полилинию в определение блока и в транзакцию
                        btr.AppendEntity(poly);
                        tr.AddNewlyCreatedDBObject(poly, true);
                    }

                    //***
                    // ШАГ 1 - создаем новую запись в таблице блоков (или получаем ID существующей)
                    //***

                    // проверяем, нет ли в таблице блока с таким именем
                    // если есть - используем его ID
                    ObjectId btrId;
                    if (bt.Has(blockName))
                    {
                        btrId = bt[blockName];
                    }
                    else
                    {
                        // создаем новое определение блока, задаем ему имя
                        BlockTableRecord btr = new BlockTableRecord();
                        btr.Name = blockName;

                        // добавляем созданное определение блока в таблицу блоков, сохраняем его ID
                        btrId = bt.Add(btr);
                        // добавляем созданное определение блока в транзакцию
                        tr.AddNewlyCreatedDBObject(btr, true);

                        //***
                        // ШАГ 2 - добавляем к созданной записи необходимые геометрические примитивы
                        //***

                        // создаем полилинию
                        Polyline polyExt = new Polyline();
                        polyExt.SetDatabaseDefaults();
                        polyExt.AddVertexAt(0, new Point2d(-50, -125), 0, 0, 0);
                        polyExt.AddVertexAt(1, new Point2d(-50, 105), 0, 0, 0);
                        polyExt.AddVertexAt(2, new Point2d(-20, 125), 0, 0, 0);
                        polyExt.AddVertexAt(3, new Point2d(20, 125), 0, 0, 0);
                        polyExt.AddVertexAt(4, new Point2d(50, 105), 0, 0, 0);
                        polyExt.AddVertexAt(5, new Point2d(50, -125), 0, 0, 0);
                        polyExt.AddVertexAt(6, new Point2d(-50, -125), 0, 0, 0);

                        // добавляем полилинию в определение блока и в транзакцию
                        btr.AppendEntity(polyExt);
                        tr.AddNewlyCreatedDBObject(polyExt, true);

                        // создаем еще полилинию
                        Polyline polyIn = new Polyline();
                        polyIn.SetDatabaseDefaults();
                        polyIn.AddVertexAt(0, new Point2d(-5, -125), 0, 0, 0);
                        polyIn.AddVertexAt(1, new Point2d(-5, 125), 0, 0, 0);
                        polyIn.AddVertexAt(2, new Point2d(5, 125), 0, 0, 0);
                        polyIn.AddVertexAt(3, new Point2d(5, -125), 0, 0, 0);

                        // добавляем вторую полилинию в определение блока и в транзакцию
                        btr.AppendEntity(polyIn);
                        tr.AddNewlyCreatedDBObject(polyIn, true);

                        // создаем окружность
                        Circle cir = new Circle();
                        cir.SetDatabaseDefaults();
                        cir.Center = new Point3d(0, 90, 0);
                        cir.Radius = 15;

                        // добавляем окружность в определение блока и в транзакцию
                        btr.AppendEntity(cir);
                        tr.AddNewlyCreatedDBObject(cir, true);

                        // добавляем звездочки
                        BlockReference starLeft = new BlockReference(new Point3d(-27, -75, 0), btrIdAux);
                        btr.AppendEntity(starLeft);
                        tr.AddNewlyCreatedDBObject(starLeft, true);
                        BlockReference starRight = new BlockReference(new Point3d(27, -75, 0), btrIdAux);
                        btr.AppendEntity(starRight);
                        tr.AddNewlyCreatedDBObject(starRight, true);
                    }

                    //***
                    // ШАГ 3 - добавляем вхождение созданного блока на чертеж
                    //***

                    // открываем пространство модели на запись
                    BlockTableRecord ms = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);

                    // создаем новое вхождение блока, используя сохраненный ранее ID определения блока
                    BlockReference br = new BlockReference(new Point3d(x, y, 0), btrId);

                    // добавляем вхождение блока на пространство модели и в транзакцию
                    ms.AppendEntity(br);
                    tr.AddNewlyCreatedDBObject(br, true);

                    // фиксируем транзакцию
                    tr.Commit();
                }
            }

            // эта функция будет вызываться при выполнении в AutoCAD команды "HabrCommand_DrawFigures"
            [CommandMethod("HabrCommand_DrawFigures")]
            public void HabrCommand_DrawFigures()
            {
                for (int x = 0; x < 100; x++)
                {
                    for (int y = 0; y < 100; y++)
                    {
                        drawFigure(125 * x, 300 * y);
                    }
                }
            }

            // эта функция будет вызываться при выполнении в AutoCAD команды "HabrCommand_DrawBlocks"
            [CommandMethod("HabrCommand_DrawBlocks")]
            public void HabrCommand_DrawBlocks()
            {
                for (int x = 0; x < 100; x++)
                {
                    for (int y = 0; y < 100; y++)
                    {
                        drawBlock(125 * x, 300 * y);
                    }
                }
            }

            // функции Initialize() и Terminate() необходимы, чтобы реализовать интерфейс IExtensionApplication
            public void Initialize()
            {

            }

            public void Terminate()
            {

            }
        }
    }
}

Код весьма незамысловат: имеется две процедуры — drawFigure(double x, double y) и drawBlock(double x, double y). Первая отрисовывает в заданной точке набор фигур, в точности повторяющий наш блок, а вторая создает в заданной точке вхождение нашего блока.

Далее следуют две команды — HabrCommand_DrawFigures и HabrCommand_DrawBlocks. Первая отрисовывает на чертеже 10 000 наборов фигур, вторая — 10 000 вхождений блока.

Результаты работы

Все целиком:

Чуть ближе:

Еще ближе:

Десять тысяч наборов фигур:

Десять тысяч вхождений блока:

Запустим AutoCAD, выполним первую команду, сохраним чертеж. Затем закроем AutoCAD и повторим аналогичную процедуру для второй команды. В итоге получим два dwg-файла. Сравним их размеры.

Конец немного предсказуем:

Файл с наборами фигур:

Файл с вхождениями блока:

Первая причина

Некоторое увеличение размера файла чертежа — это нехорошо, но не так уж и страшно: Россия большая, места хватит всем. Однако есть проблемы и посерьезнее.

Представим, что в вышеупомянутый Приказ №1500 внесли дополнение, жестко регламентирующее расположение пуговицы на погоне, и нам нужно перенести на всех погонах окружность несколько выше от текущего положения.

Если мы использовали блоки, то задача решается в несколько строк кода: мы просто откроем определение блока, найдем там окружность и сдвинем ее центр в нужную точку. Поскольку все вхождения блока ссылаются на одно и то же определение, после выполнения команды все они обновятся автоматически.

Код команды:

[CommandMethod("HabrCommand_MoveButton")]
public void HabrCommand_MoveButton()
{
    // получаем ссылки на документ и его БД
    Document doc = Application.DocumentManager.MdiActiveDocument;
    Database db = doc.Database;

    // поле документа "Editor" понадобится нам для вывода сообщений в окно сообщений AutoCAD
    Editor ed = doc.Editor;

    // имя нашего блока
    const string blockName = "ltBlock";

    // начинаем транзакцию
    Transaction tr = db.TransactionManager.StartTransaction();
    using (tr)
    {
        // открываем таблицу блоков на запись
        BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForWrite);

        // проверяем, есть ли в таблице блок с таким именем
        // если нет - выводим сообщение и завершаем выполнение команды
        if (!bt.Has(blockName))
        {
            ed.WriteMessage("Block not found!");
            return;
        }
        else
        {
            // открываем определение блока на запись
            BlockTableRecord btr = tr.GetObject(bt[blockName], OpenMode.ForWrite) as BlockTableRecord;

            // перебираем по очереди ID всех графических объектов блока
            foreach (ObjectId id in btr)
            {
                // для каждого ID получаем сам объект, открываем его на чтение
                DBObject obj = tr.GetObject(id, OpenMode.ForRead);
                // если тип объекта - окружность (Circle), то открываем ее на запись и задаем ей новый центр
                if (obj.GetType() == typeof(Circle))
                {
                    obj.UpgradeOpen();
                    (obj as Circle).Center = new Point3d(0, 100, 0);
                }
            }

            // фиксируем транзакцию
            tr.Commit();
        }
    }

NB:

Как читатель уже наверняка заметил, в этой команде происходит редактирование блока. Ничего запредельного в этом нет; в одной из следующих статей я постараюсь чуть подробнее осветить вопросы выбора элементов на чертеже и их редактирования. Данный пример основан на

этом

(англ.) и

этом

(англ.) источниках. По второй ссылке, кстати, можно найти целых пять разных способов убедиться в том, что объект является окружностью. Мы, разумеется, использовали самый

неэффективный

простой и понятный из них.

Маленькая деталь: AutoCAD действительно обновит все вхождения блоков, однако чтобы это увидеть, необходимо выполнить команду REGEN:

Результат:

ДО:

ПОСЛЕ:

Безусловно, подобную операцию можно провернуть и в случае геометрических фигур, а не вхождений блоков, однако тогда придется работать уже со всем чертежом и менять координаты центра окружности не один раз, а десять тысяч. Тестов я не проводил, но думаю, что эта процедура займет несколько больше времени, чем редактирование определения блока.

С другой стороны, каждая монета имеет аверс и реверс, а использование блоков накладывает свои ограничения.

Например, если потребуется заменить лейтенантский погон, расположенный в одиннадцатой строке и сорок втором столбце, на капитанский, то при использовании блоков никаким нормальным способом мы это сделать не сможем.

Придется либо создавать новый блок для капитана, либо дорисовывать звездочки вручную, либо прибегать к каким-нибудь дополнительным извращениям.

NB:

NэBpaЩeHuЯ — одна из причин, по которым мне

НЕ

нравится программировать. С некоторых пор я работаю конторским пенсионером, чему искренне рад.

Краткий вывод

(куда ж без него-то): использование блоков может сэкономить массу сил и времени, но применять их надо обоснованно, при наличии необходимости.

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

Спасибо за внимание!

Hello, Habr!

Решил рассказать о своем опыте работы с AutoCAD. Может быть, кому-то это поможет – ну или хотя бы интересным покажется.

public static string disclaimer = "Автор не является профессиональным разработчиком и не обладает глубокими знаниями AutoCAD. Этот пост – просто небольшой рассказ о начальном этапе создания плагина.";

Предыстория

Началось все достаточно просто: в очередной раз почувствовав острую нехватку денег, я решил, что пора бы уже начать их где-нибудь разыскивать. И вот после пары недель поиска на «Фрилансим» обнаружилась вакансия разработчика для создания программы, взаимодействующей с AutoCAD.

Скажу сразу: до того дня общаться с AutoCAD мне не доводилось. Однако объявление содержало в себе фразу «Опыт работы не требуется», которая наполнила мою душу надеждой. Я связался с разместившим вакансию человеком и получил тестовое задание.

Для пробы предлагалось создать на чертеже пару объектов, а также вывести текст. Несколько дней я искал информацию об API и пытался подружиться с непривычной программой. В конце концов фигуры были нарисованы, текст выведен, а тестовое задание отправлено на проверку. И через несколько дней я неожиданно узнал, что принят! Чудеса, да и только.

В следующих абзацах – мои впечатления, синяки и шишки, мысли и советы (возможно, вредные). Разработка велась под AutoCAD 2010, в качестве IDE использовалась верная Visual Studio 2013 Express. Язык разработки – C#.

1. Подготовка необходимых инструментов

1.1. Собственно AutoCAD

Тут все понятно. Качайте с официального сайта Autodesk, ставьте, 30 дней наслаждайтесь прекрасным инструментом. Потом узнайте цену покупки и повесьтесь. Для разработчиков действует специальная программа ADN, по которой можно получать девелоперские лицензии на продукты Autodesk. Стоимость базовой версии подписки, как указано на сайте, составляет от 700 долларов в год.

1.2. ObjectARX SDK – набор библиотек, необходимых для работы с AutoCAD

Последние три-четыре версии библиотек можно бесплатно скачать тут после регистрации. Более ранние придется поискать – скажем, тут. На всякий случай продублирую список прямо здесь – не такой уж он и длинный:

ссылки для загрузки ObjectARX SDK для версий AutoCAD 2000 – 2011

Версия 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. Поисковик, усидчивость, здравый смысл

У меня был не такой большой опыт программирования – я привык решать простые задачи, для которых за глаза хватало средств самой платформы .NET. И первое мое знакомство с программированием под AutoCAD вышло не очень простым. Неприятной неожиданностью оказалось то, что у классов для работы с AutoCAD:

  • нет привычных всплывающих подсказок о назначении класса, свойства или метода;
  • нет подробной справки.

В итоге информацию я черпал из файлов помощи Object ARX (у меня они установлены в папку с именем C:ObjectARX 2010docs), а также из многочисленных форумов, блогов и сообществ разработчиков AutoCAD. Признаться, больше помогало последнее, чем первое. :)

В конце этой статьи приведен список ресурсов, на которых можно позадавать вопросы и, если повезет, получить на них ответы.

2. Создание проекта библиотеки

Первые шаги вполне внятно описаны здесь. Владеющие английским могут попробовать зайти еще и сюда. Правда, в материалах по последней ссылке упор сделан на Visual Basic, плюс нужно будет установить «AutoCAD .NET Wizard» – шаблон проекта для создания плагинов под AutoCAD. Люди знающие говорят, что этот шаблон сильно упрощает жизнь; я же никогда им не пользовался, поэтому скромно промолчу.

Вкратце продублирую основные этапы:

2.1. Создать проект «Библиотека классов» («Class Library»)

Если плагин предназначен для старой версии AutoCAD, то целесообразно сразу же задать в свойствах проекта версию .NET, которую будем использовать. Например, AutoCAD 2010 не может загружать плагины, созданные с использованием .NET Framework 4, поэтому я в качестве используемой версии указываю .NET Framework 3.5.

При понижении версии .NET Framework, используемой в проекте, могут появляться сообщения об ошибках. В моем случае Visual Studio ругается на отсутствие сборки «Microsoft.CSharp» – ее просто нужно исключить из ссылок (References).

2.2. Добавить ссылки на необходимые библиотеки AutoCAD .NET API

На этом пункте стоит остановиться чуть подробнее. AutoCAD .NET API включает в себя достаточно большое количество классов, которые разнесены по разным пространствам имен (namespaces). В свою очередь, эти пространства имен разнесены по нескольким контейнерам (проще говоря, DLL-файлам).

Эти DLL-файлы находятся в папке с именем inc-<наименование_архитектуры>. Так, в моем случае я добавляю ссылки на библиотеки из папки C:ObjectARX 2010inc-win32.

NB:

у меня дома установлена 32-разрядная ОС, у заказчика – 64-разрядная. Пока серьезных проблем с совместимостью не возникало. Но однажды я все же напоролся на то, что у меня функция возвращала Int32, а у заказчика – Int64. Линковщик ОЧЕНЬ расстраивался. Нужно иметь эту особенность в виду.

Первое знакомство с API у меня заключалось в лихорадочных попытках скомпилировать хоть какой-нибудь из примеров, щедро разбросанных по Сети. И что сλка характерно, компилироваться они упорно не хотели, ругаясь на неизвестные пространства имен и классы. В попытках собрать свой первый проект я с горя включил в него чуть ли не все DLL-файлы, которые шли с ObjectARX. Плохой способ – так делать не надо.

А как надо?

Ну, это вопрос не ко мне. Я только могу сказать, что в начале примеров обычно идет перечисление используемых пространств имен – скажем, так:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using Autodesk.AutoCAD.Runtime;
using Autodesk.Windows;

То, что начинается со слова «Autodesk», – это и есть те дроиды пространства имен, которые мы ищем. Теперь осталось разыскать контейнеры, которые их содержат. Исчерпывающего перечня сопоставлений я найти не смог, поэтому все проверялось методом научного тыка. Если есть более правильный способ, было бы интересно его узнать…

А пока – вот список DLL-файлов, которые я использую в проекте, и содержащихся в них пространств имен:

Контейнер «AcMgd» (файл «AcMgd.dll»):

  • Autodesk.AutoCAD.ApplicationServices
  • Autodesk.AutoCAD.EditorInput
  • Autodesk.AutoCAD.GraphicsSystem
  • Autodesk.AutoCAD.Internal
  • Autodesk.AutoCAD.Internal.Calculator
  • Autodesk.AutoCAD.Internal.DatabaseServices
  • Autodesk.AutoCAD.Internal.Forms
  • Autodesk.AutoCAD.Internal.PreviousInput
  • Autodesk.AutoCAD.Internal.PropertyInspector
  • Autodesk.AutoCAD.Internal.Reactors
  • Autodesk.AutoCAD.Internal.Windows
  • Autodesk.AutoCAD.PlottingServices
  • Autodesk.AutoCAD.Publishing
  • Autodesk.AutoCAD.Runtime
  • Autodesk.AutoCAD.Windows
  • Autodesk.AutoCAD.Windows.Data
  • Autodesk.AutoCAD.Windows.ToolPalette

Контейнер «AcDbMgd» (файл «AcDbMgd.dll»):

  • Autodesk.AutoCAD.Colors
  • Autodesk.AutoCAD.ComponentModel
  • Autodesk.AutoCAD.DatabaseServices
  • Autodesk.AutoCAD.DatabaseServices.Filters
  • Autodesk.AutoCAD.Geometry
  • Autodesk.AutoCAD.GraphicsInterface
  • Autodesk.AutoCAD.GraphicsSystem
  • Autodesk.AutoCAD.LayerManager
  • Autodesk.AutoCAD.Runtime

Контейнер «AdWindows» (файл «AdWindows.dll»):

  • Autodesk.Internal.InfoCenter
  • Autodesk.Internal.Windows
  • Autodesk.Internal.Windows.ToolBars
  • Autodesk.Private.InfoCenter
  • Autodesk.Private.SubAwareService
  • Autodesk.Private.WebSearchService
  • Autodesk.Private.Windows
  • Autodesk.Private.Windows.ToolBars
  • Autodesk.Private.WsCommCntrLib
  • Autodesk.Windows
  • Autodesk.Windows.Common.Utilities
  • Autodesk.Windows.ToolBars

Контейнер «AcCui» (файл AcCui.dll»):

  • Autodesk.AutoCAD.Customization

NB:

имена многих классов AutoCAD .NET API совпадают с именами стандартных классов .NET, что не очень удобно. Например, если обратиться в коде к классу Application, то Visual Studio выругается на неоднозначность этого определения: класс с таким именем есть как в пространстве имен System.Windows, так и в пространстве имен Autodesk.AutoCAD.ApplicationServices. Чтобы не писать каждый раз полное имя, можно добавить в начало файла строку

using acadApp = Autodesk.AutoCAD.ApplicationServices.Application;

Теперь в любом месте этого файла можно вместо Autodesk.AutoCAD.ApplicationServices.Application писать acadApp.

Есть смысл провернуть такую операцию с наиболее часто употребляемыми классами. Непременно так делайте, код будет компактнее и понятнее. Цинизм данного совета заключается в том, что к тому моменту, когда вы наконец поймете, какие же классы являются у вас наиболее часто употребляемыми, что-то менять будет уже сильно лень.

2.3. Написать код плагина
using System.Windows.Forms;
using Autodesk.AutoCAD.Runtime;

namespace MyAutoCADDll
{
    public class Commands : IExtensionApplication
    {
        // функция инициализации (выполняется при загрузке плагина)
        public void Initialize()
        {
            MessageBox.Show("Hello!");
        }

        // функция, выполняемая при выгрузке плагина
        public void Terminate()
        {
            MessageBox.Show("Goodbye!");
        }

        // эта функция будет вызываться при выполнении в AutoCAD команды «TestCommand»
        [CommandMethod("TestCommand")]
        public void MyCommand()
        {
            MessageBox.Show("Habr!");
        }
    }
}

Все очень просто. Вначале мы указываем необходимые пространства имен. Нам потребуются два.

В первом пространстве имен (System.Windows.Forms) хранится описание класса MessageBox, с помощью которого мы будем выводить сообщения. Чтобы сделать его доступным, необходимо добавить ссылку на одноименную сборку .NET.

Во втором пространстве имен (Autodesk.AutoCAD.Runtime) определены интерфейс IExtensionApplication и атрибут CommandMethod. Причем описание IExtensionApplication находится в файле AcDBMgd.dll, а описание CommandMethod – в файле AcMgd.dll, поэтому придется добавить ссылки на обе эти библиотеки.

Таким образом, всего необходимо добавить три ссылки:

Создание плагинов для AutoCAD с помощью .NET API (часть 1 – первые шаги)


Затем мы объявляем класс Commands. Именно он и будет «отправной точкой» плагина. Наш класс унаследован от интерфейса IExtensionApplication, поэтому в нем могут быть реализованы методы Initialize и Terminate. Первый из них автоматически выполняется при загрузке плагина, второй – при выгрузке.

NB:

AutoCAD не предоставляет разработчику возможность выгрузить плагин после того, как он будет загружен. Поэтому реально метод Terminate будет вызываться только в одном случае – при закрытии самого AutoCAD.

Почитать поподробнее про методы Initialize и Terminate можно тут (rus) и там (англ.).

Наконец, мы объявляем функцию MyCommand, которая будет реализовывать команду AutoCAD. Она обязательно должна ничего не принимать на вход и ничего не возвращать на выходе (не знаю, откуда у меня взялось это убеждение, но оно есть). Внутри этой функции можно делать все, что заблагорассудится (в пределах разумного, конечно), причем есть возможность работать как с AutoCAD .NET API, так и со стандартными классами .NET. Например, можно создать обычную форму Windows с полями ввода, отобразить ее на экране с помощью ShowModal(), а затем на основе введенных пользователем данных внести изменения в открытый в AutoCAD чертеж.

Чтобы «превратить» созданный метод в команду AutoCAD, применяется атрибут CommandMethod. В скобках после него указывается имя создаваемой команды, которое можно будет использовать непосредственно в среде AutoCAD.

После сборки этого проекта у нас получится готовый к употреблению плагин.

2.4. Загрузить созданный плагин

Нужно запустить AutoCAD и выполнить команду «NETLOAD»:

Создание плагинов для AutoCAD с помощью .NET API (часть 1 – первые шаги)


Затем в открывшемся окне указать путь к файлу плагина:

Создание плагинов для AutoCAD с помощью .NET API (часть 1 – первые шаги)


После этого плагин будет загружен в AutoCAD. Мы должны увидеть первое сообщение:

Создание плагинов для AutoCAD с помощью .NET API (часть 1 – первые шаги)


Если при загрузке плагина произошла критическая ошибка, она будет выведена в консоль AutoCAD:

Создание плагинов для AutoCAD с помощью .NET API (часть 1 – первые шаги)

Сообщения обычно понятные – помогут разобраться, если случай не сильно клинический. :)

NB:

если плагин не смог загрузиться из-за ошибки, то перед тестированием очередного (исправленного) варианта нужно закрыть и заново запустить AutoCAD. В противном случае он может отказаться загружать плагин, даже если ошибок в коде уже не будет.

Теперь, когда плагин загружен, можно выполнить нашу тестовую команду:

Создание плагинов для AutoCAD с помощью .NET API (часть 1 – первые шаги)


… и увидеть результат:

Создание плагинов для AutoCAD с помощью .NET API (часть 1 – первые шаги)

Работает. Теперь можно закрывать AutoCAD.

А как же…

Да – внимательный читатель, конечно, заметит, что при закрытии AutoCAD мы почему-то не увидим сообщения «Goodbye». Как уже говорилось выше, внутри функции «Terminate» можно сделать далеко не все. Но с ее помощью вполне получится, например, создать файл при закрытии AutoCAD.

2.5. Отладить плагин (при необходимости)

Процедура запуска плагина для отладки очень хорошо расписана в этом посте Tepliuk.

Финал

Ну что же – для первого раза достаточно. Осталось привести обещанные ссылки. В посте Namolem и посте n00buK уже приведен большой объем источников; часть из них я продублирую здесь.

  1. http://adn-cis.org/forum/ (rus) – форум Сообщества программистов Autodesk в СНГ. Один из лучших русскоязычных ресурсов среди всех мною встреченных в 2013-2014 годах. Сам я туда прихожу за советом, когда совсем припекает, – и не было пока случая, чтобы мне не помогли. Хотя я, конечно, стараюсь особо не злоупотреблять.
  2. http://forums.autodesk.com/t5/russkoe-soobshchestvo/bd-p/392 (rus) – официальные форумы Autodesk – раздел русского сообщества. Можно позадавать вопросы.
  3. http://forums.autodesk.com/t5/net/bd-p/152 (англ.) – официальные форумы Autodesk – раздел, посвященный .NET API. Тоже можно задать вопрос – правда, его могут и проигнорировать.
  4. http://through-the-interface.typepad.com (англ.) – блог, который ведет Kean Walmsley, один из ведущих экспертов в разработке под AutoCAD. Ценнейший ресурс. Можно попробовать что-нибудь спросить у самого хозяина блога – и даже получить ответ, если тот будет в настроении. Впрочем, неинтересные (и простые) вопросы Kean часто игнорирует – или же предлагает вопрошающему поискать решение на официальном форуме (см. рис. 1).
  5. http://adndevblog.typepad.com (англ.) – коллективный блог разработчиков из ADN. Порой бывает полезен.
  6. http://www.theswamp.org/index.php (англ.) – еще один форум со множеством примеров и решений.

На этом моя статья закончена. Если ее признают годной – напишу еще несколько простых заметок о том, с чем сталкивался, как-то:

  • работа с лентой (Ribbon);
  • работа со слоями;
  • работа с динамическими блоками;
  • реализация пользовательского ввода;
  • взаимодействие плагина с внешним приложением.

Спасибо за внимание!

Автор: lostpassword

Источник

Страницы 1 2 Далее

Чтобы отправить ответ, вы должны войти или зарегистрироваться

RSS

Сообщения с 1 по 25 из 42

#1 26 октября 2009г. 12:26:58

  • hwd
  • Активный участник
  • На форуме с 18 сентября 2009г.
  • Сообщений: 397
  • Спасибо: 13

Тема: Локализация руководства разработчика по .Net API AutoCAD

Для того, чтобы хорошо разбираться в программировании посредством .Net API, желательно сначала прочитать руководство разработчика, после чего приступать к изучению пространств имён и их содержимому управляемой библиотеки от Autodesk, объём которой мягко говоря, впечатляет и кроме того — существенно увеличивается от версии к версии AutoCAD (раздел «AutoCAD Managed Class Reference» официальной документации по ObjecARX, из файла arxdoc.chm)…

Читая руководство разработчика, временами сталкиваюсь с такими предложениями, дословный перевод которых не совсем мне понятен, в свете чего приходится тратить некоторое время на более точный перевод. Но в случае, если со временем потребуется повторно прочитать то же предложение — мне, скорее всего, придётся так же сидеть над ним, а это не рационально…

Объём руководства разработчика (в отличие от раздела «AutoCAD Managed Class Reference») относительно невелик — учитывая это, решил, что лучше было бы сразу записывать перевод, дабы в последствии быстрее пользоваться мануалом.

Часть руководства уже перевёл, однако на перевод и оформление тратится не мало времени на самом деле (больше, чем я расчитывал изначально). Допереводить я его в любом случае доперевожу (если кирпич на голову не упадёт или меня не уволят), однако было бы неплохо, чтобы данное действие, если оно представляет интерес не только мне, было бы выполнено совместно — в этом случае руководство разработчика можно было бы перевести гораздо быстрее.

То, что имею на руках на текущий момент — опубликовано здесь.

Если есть люди, которым данная тема интересна — предлагаю объединить усилия по переводу руководства разработчика по .Net API (на перевод «AutoCAD Managed Class Reference» даже не замахиваюсь — это не подъёмно и нецелесообразно в виду частых изменений, вносимых разработчиками в состав библиотек).

#2 Ответ от Vildar 26 октября 2009г. 18:32:04

  • Vildar
  • Активный участник
  • Откуда: Саратов
  • На форуме с 15 августа 2008г.
  • Сообщений: 574
  • Спасибо: 11

Re: Локализация руководства разработчика по .Net API AutoCAD

Попробую перевести п.5.»Создание и редактирование объектов AutoCAD»

#3 Ответ от Vildar 26 октября 2009г. 19:44:24 (изменено: bandero, 27 октября 2009г. 21:23:24)

  • Vildar
  • Активный участник
  • Откуда: Саратов
  • На форуме с 15 августа 2008г.
  • Сообщений: 574
  • Спасибо: 11

Re: Локализация руководства разработчика по .Net API AutoCAD

Источник: Create and Edit AutoCAD Entities
Создание и редактирование объектов AutoCAD
  Вы можете создать любой объект, от простой линии и круга до сплайнов, эллипсов и ассоциативных штриховок. Для этого объект нужно добавить в BlockTableRecord, используя метод AppendEntity. После создания объекта вы можете изменять его свойства, такие как слой, цвет, тип линии и т.д.
  База данных чертежа Автокада похожа на все прочие базы данных. Вы можете думать об объекте Линии в Модели (Model Space), как о записи в таблице, а о Модели как о таблице базы данных. Работая с базой данных, прежде чем работать с записью вы должны ее открыть, а затем закрыть. Хранение объектов в базе данных чертежа (объект Database) ничем от этого не отличается, вы используете метод GetObject чтобы получить объект из базы данных чертежа и определяете, как хотите работать с этим объектом.

Статьи в этом разделе:
•Открытие и закрытие объектов
•Создание объектов
•Работа с Selection Set
•Редактирование неграфических и графических 2D объектов
•Использование объектов Layer, Color, and Linetype
•Сохранение и восстановление состояния слоев (LayerStateManager)
•Добавление текста в чертеж

#4 Ответ от fixo 26 октября 2009г. 20:22:32

  • fixo
  • fixo
  • Активный участник
  • Откуда: СПб
  • На форуме с 7 февраля 2009г.
  • Сообщений: 869
  • Спасибо: 41

Re: Локализация руководства разработчика по .Net API AutoCAD

bandero пишет:

Источник: здесь (http://docs.autodesk.com/ACD/2010/ENU/AutoCAD%20.NET%20Developer’s%20Guide/index.html)
Создание и редактирование объектов AutoCAD 
Вы можете создать любой объект, от простой линии и круга до сплайнов, эллипсов и ассоциативных штриховок. Для этого объект нужно добавить в BlockTableRecord, используя метод AppendEntity. После создания объекта вы можете изменять его свойства, такие как слой, цвет, тип линии и т.д.
База данных чертежа Автокада похожа на все прочие базы данных. Вы можете думать об объекте Линии в Модели (Model Space), как о записи в таблице, а о Модели как о таблице базы данных. Работая с базой данных, прежде чем работать с записью вы должны ее открыть, а затем закрыть. Хранение объектов в базе данных чертежа (объект Database) ничем от этого не отличается, вы используете метод GetObject чтобы получить объект из базы данных чертежа и определяете, как хотите работать с этим объектом.

Статьи в этом разделе:
• Открытие и закрытие объектов
• Создание объектов
• Работа с Selection Set
• Редактирование неграфических и графических 2D объектов
• Использование объектов Layer, Color, and Linetype
• Сохранение и восстановление состояния слоев (LayerStateManager)
• Добавление текста в чертеж

Я думаю многие были бы вам обоим очень благодарны за это

Успехов в переводе :)

~’J’~

#5 Ответ от Vildar 26 октября 2009г. 20:39:26

  • Vildar
  • Активный участник
  • Откуда: Саратов
  • На форуме с 15 августа 2008г.
  • Сообщений: 574
  • Спасибо: 11

Re: Локализация руководства разработчика по .Net API AutoCAD

fixo, почему обоим и почему смешно. Автор всех призывает к участию :))

#6 Ответ от kpblc 26 октября 2009г. 23:24:21

  • kpblc
  • kpblc
  • Активный участник
  • Откуда: С.-Петербург
  • На форуме с 29 ноября 2004г.
  • Сообщений: 8,348
  • Спасибо: 23

Re: Локализация руководства разработчика по .Net API AutoCAD

bandero, есть вопрос: а если есть какое-то видео? Или собственный вариант перевода куска официальной документации? Как тогда поступать?

#7 Ответ от Vildar 27 октября 2009г. 08:32:29

  • Vildar
  • Активный участник
  • Откуда: Саратов
  • На форуме с 15 августа 2008г.
  • Сообщений: 574
  • Спасибо: 11

Re: Локализация руководства разработчика по .Net API AutoCAD

Кулик Алексей aka kpblc, в этой теме мы (добровольцы перевода) действуем под редакцией (т.е. он будет выбирать окончательный вариант)
hwd,. Если хотите (не хотите давать ему свои материалы) открывайте свою тему, блог (тем более он уже есть), и публикуйте. Я у Вас на блоге не видел переводов документации в любой форме (аудио-видео-текстовой), может плохо смотрел?

#8 Ответ от hwd 27 октября 2009г. 20:45:36

  • hwd
  • Активный участник
  • На форуме с 18 сентября 2009г.
  • Сообщений: 397
  • Спасибо: 13

Re: Локализация руководства разработчика по .Net API AutoCAD

2 fixo

Спасибо, сейчас прочту раздел и сравню перевод (не то, чтобы я супер спец по английскому, а чтобы удостовериться в том, что мы одинаково поняли смысл оригинала). Перевод буду опубликовывать на том сайте. Думаю, что лучше было бы высылать переводы мне на эл. почту: bushman<точка>andrey<собачка>gmail<точка>com.

п.с. вы не верно указали ссылку на исходник. Для того, чтобы указывать правильно, нужно копировать ссылку из контекстного меню, полученного на клике раздела, выбранного в левом древовидном списке. )

Я найду, откуда текст, просто на будущее, чтобы мне не перелопачивать всё — лучше ссылки проверять. )))

2 Крыс

если есть видео, я могу попробовать вставить его туда же, тем более, что сайты гугла это, насколько я знаю, позволяют сделать (главное, чтобы оно, видео, в тему было, а если не в тему раздела — то можно создать специальный отдельный раздел), а если «свой вариант перевода» — думаю, что нужно выбирать более грамотный вариант, а это может определить, к примеру Александр Ривилис (если у него будет на это время и желание). Для решения таких моментов, я могу создать новую ветку, и назвать её «Спорные варианты перевода» и в нем размещать варианты, а человек, более сведущий в этих вопросах сможет указать более точный вариант, или вообще дать свой.

#9 Ответ от Vildar 27 октября 2009г. 21:16:17

  • Vildar
  • Активный участник
  • Откуда: Саратов
  • На форуме с 15 августа 2008г.
  • Сообщений: 574
  • Спасибо: 11

Re: Локализация руководства разработчика по .Net API AutoCAD

5.1.Открытие и закрытие объектов
Источник: http://docs.autodesk.com/ACD/2010/ENU/AutoCAD%20.NET%20Developer’s%20Guide/files/WS73099cc142f48755f2fc9df120970276f77e6.htm
Работаете ли вы с объектом линии, круга, полилинии или с символьной таблицей (?) и ее записями, вы должны открыть объект для чтения или записи. Для просмотра нужного объекта, вы открываете его для чтения, а для внесения изменений — открываете его для записи.
Статьи в этом разделе:
•Работа с ObjectIds
•Использование Транзакций  с помощью Менеджера Транзакций
•Открытие и закрытие Объектов без помощи Менеджера Транзакций
•Upgrade и Downgrade открытых Объектов

Работа кипит :)

О, на Fixo весь гнев пал, красота )

Наверно нехорошо все в один топик пихать. Можно для перевода каждого пункта руководства создавать отдельную тему.

Про ObjectIds щас дальше буду коверкать перевод )))

#10 Ответ от Vildar 27 октября 2009г. 21:17:34

  • Vildar
  • Активный участник
  • Откуда: Саратов
  • На форуме с 15 августа 2008г.
  • Сообщений: 574
  • Спасибо: 11

Re: Локализация руководства разработчика по .Net API AutoCAD

Блина, что со ссылкой происходит. Щас исправлю, в обоих местах.

#11 Ответ от hwd 27 октября 2009г. 21:29:44 (изменено: hwd, 27 октября 2009г. 21:36:01)

  • hwd
  • Активный участник
  • На форуме с 18 сентября 2009г.
  • Сообщений: 397
  • Спасибо: 13

Re: Локализация руководства разработчика по .Net API AutoCAD

bandero пишет:

О, на Fixo весь гнев пал, красота )

очепятка )
добавил в 5-й раздел перевод (чутка подправил). Ривилиса попросил глянуть (сомневаюсь кое в чём). Сейчас поищу, где на сайте включается возможность, чтобы оставляли коментарии — тогда можно в коментариях прямо по разделам переводы оставлять, а я буду их затем в топик перетаскивать.

хм… в настройках страниц везде установлена опция, что комментарии доступны…

т.е. вы берёте на себя полностью 5-й раздел? я тогда пока 3-й буду добивать.

#12 Ответ от Vildar 27 октября 2009г. 21:36:58

  • Vildar
  • Активный участник
  • Откуда: Саратов
  • На форуме с 15 августа 2008г.
  • Сообщений: 574
  • Спасибо: 11

Re: Локализация руководства разработчика по .Net API AutoCAD

Ok, буду туда пИсать )

#13 Ответ от hwd 27 октября 2009г. 21:47:58

  • hwd
  • Активный участник
  • На форуме с 18 сентября 2009г.
  • Сообщений: 397
  • Спасибо: 13

Re: Локализация руководства разработчика по .Net API AutoCAD

2 bandero

5.1. проверил, подправил и опубликовал.

#14 Ответ от Vildar 27 октября 2009г. 22:24:18

  • Vildar
  • Активный участник
  • Откуда: Саратов
  • На форуме с 15 августа 2008г.
  • Сообщений: 574
  • Спасибо: 11

Re: Локализация руководства разработчика по .Net API AutoCAD

hwd пишет:

т.е. вы берёте на себя полностью 5-й раздел? я тогда пока 3-й буду добивать.

Фигасе, ну так постепенно…

#15 Ответ от Александр Ривилис 28 октября 2009г. 08:55:16

  • Александр Ривилис
  • Александр Ривилис
  • Активный участник
  • Откуда: Украина / Киев
  • На форуме с 15 апреля 2005г.
  • Сообщений: 8,661
  • Спасибо: 158

Re: Локализация руководства разработчика по .Net API AutoCAD

hwd пишет:

Ривилиса попросил глянуть (сомневаюсь кое в чём)

Где попросил? Или еще собираешься спросить? smile

hwd пишет:

а это может определить, к примеру Александр Ривилис (если у него будет на это время и желание)

Я не против, но если не сложно то порциями шли мне на e-mail. Я по мере возможности буду редактировать (вставлять замечания) и отсылать обратно. E-mail: Alexander<тчк>Rivilis<сбк>gmail<тчк>com

#16 Ответ от hwd 28 октября 2009г. 10:03:39 (изменено: hwd, 28 октября 2009г. 10:08:57)

  • hwd
  • Активный участник
  • На форуме с 18 сентября 2009г.
  • Сообщений: 397
  • Спасибо: 13

Re: Локализация руководства разработчика по .Net API AutoCAD

Александр Ривилис пишет:

hwd пишет:

Ривилиса попросил глянуть (сомневаюсь кое в чём)

Где попросил? Или еще собираешься спросить? 

hwd пишет:

а это может определить, к примеру Александр Ривилис (если у него будет на это время и желание)

Я не против, но если не сложно то порциями шли мне на e-mail. Я по мере возможности буду редактировать (вставлять замечания) и отсылать обратно. E-mail: Alexander<тчк>Rivilis<сбк>gmail<тчк>com

Просил здесь )
А может лучше, чтобы всё делалось посредством комментариев? Кто-то что-то перевёл — добавил перевод в коммент, вы увидели несоответствие — в след. комменте указали на ошибку. А я бы из комментариев вытаскивал и опубликовывал. ?
Локализация руководства разработчика по .Net API AutoCAD

#17 Ответ от Александр Ривилис 28 октября 2009г. 10:56:12 (изменено: Александр Ривилис, 28 октября 2009г. 11:08:20)

  • Александр Ривилис
  • Александр Ривилис
  • Активный участник
  • Откуда: Украина / Киев
  • На форуме с 15 апреля 2005г.
  • Сообщений: 8,661
  • Спасибо: 158

Re: Локализация руководства разработчика по .Net API AutoCAD

Не видел.

hwd пишет:

А может лучше, чтобы всё делалось посредством комментариев?

Тоже вариант. Попробую. Просто как результат я предлагаю тебе в отличие от Autodesk оформить это все в chm/pdf-файле. Намного удобнее потом пользоваться.

P.S.: Попробовал. Возможности комментировать не увидел:
Локализация руководства разработчика по .Net API AutoCAD

#18 Ответ от hwd 28 октября 2009г. 15:25:06

  • hwd
  • Активный участник
  • На форуме с 18 сентября 2009г.
  • Сообщений: 397
  • Спасибо: 13

Re: Локализация руководства разработчика по .Net API AutoCAD

Александр Ривилис,
Отправил вам на почту приглашение (там насчет соавторства какие-то права даются). Может оно позволит добавлять комментарии… Гляньте плиз.

#19 Ответ от hwd 28 октября 2009г. 15:31:52

  • hwd
  • Активный участник
  • На форуме с 18 сентября 2009г.
  • Сообщений: 397
  • Спасибо: 13

Re: Локализация руководства разработчика по .Net API AutoCAD

Александр Ривилис пишет:

Просто как результат я предлагаю тебе в отличие от Autodesk оформить это все в chm/pdf-файле. Намного удобнее потом пользоваться.

Согласен. Как вариант — оформлю конечный результат и в виде pdf-файла. Но до этого пока далеко )

#20 Ответ от Александр Ривилис 28 октября 2009г. 16:26:33

  • Александр Ривилис
  • Александр Ривилис
  • Активный участник
  • Откуда: Украина / Киев
  • На форуме с 15 апреля 2005г.
  • Сообщений: 8,661
  • Спасибо: 158

Re: Локализация руководства разработчика по .Net API AutoCAD

hwd пишет:

Отправил вам на почту приглашение (там насчет соавторства какие-то права даются). Может оно позволит добавлять комментарии… Гляньте плиз.

Да. Теперь можно не только добавлять комментарии, но и редактировать. smile

#21 Ответ от hwd 28 октября 2009г. 16:41:29

  • hwd
  • Активный участник
  • На форуме с 18 сентября 2009г.
  • Сообщений: 397
  • Спасибо: 13

Re: Локализация руководства разработчика по .Net API AutoCAD

Александр Ривилис пишет:

не только добавлять комментарии, но и редактировать.

ужос… )))) надеюсь, не воспользуетесь этим, чтобы там анекдоты писать ))))

#22 Ответ от Александр Ривилис 28 октября 2009г. 17:03:50

  • Александр Ривилис
  • Александр Ривилис
  • Активный участник
  • Откуда: Украина / Киев
  • На форуме с 15 апреля 2005г.
  • Сообщений: 8,661
  • Спасибо: 158

Re: Локализация руководства разработчика по .Net API AutoCAD

hwd пишет:

ужос… )))) надеюсь, не воспользуетесь этим, чтобы там анекдоты писать ))))

Нет. Пару грамматических ошибок (описок) исправил, а все остальное в комментариях.

#23 Ответ от hwd 28 октября 2009г. 17:53:28 (изменено: hwd, 28 октября 2009г. 17:54:35)

  • hwd
  • Активный участник
  • На форуме с 18 сентября 2009г.
  • Сообщений: 397
  • Спасибо: 13

Re: Локализация руководства разработчика по .Net API AutoCAD

Александр Ривилис,
нашел три ваших комментария, по двум исправил (прим. переводчика и Локальная копия) и удалил, т.к. изменения внесены. Но случайно грохнул и третий (как раз тот, в котором вы внесли изменение в переводе)…((((( не туда мышью клацнул. Если не сложно, могли бы скопировать его…

дико извиняюсь за содеянное (я не нарочно)…

#24 Ответ от Александр Ривилис 28 октября 2009г. 18:06:06

  • Александр Ривилис
  • Александр Ривилис
  • Активный участник
  • Откуда: Украина / Киев
  • На форуме с 15 апреля 2005г.
  • Сообщений: 8,661
  • Спасибо: 158

Re: Локализация руководства разработчика по .Net API AutoCAD

Там было исправление грамматической ошибки (описки). В режиме редактирования у меня FireFox эти ошибки сам подчеркивает. Позднее пройдусь по всему тексту внимательней.

#25 Ответ от hwd 28 октября 2009г. 18:09:52

  • hwd
  • Активный участник
  • На форуме с 18 сентября 2009г.
  • Сообщений: 397
  • Спасибо: 13

Re: Локализация руководства разработчика по .Net API AutoCAD

Александр Ривилис пишет:

Там было исправление грамматической ошибки (описки). В режиме редактирования у меня FireFox эти ошибки сам подчеркивает. Позднее пройдусь по всему тексту внимательней.

Спасибо.

Страницы 1 2 Далее

Чтобы отправить ответ, вы должны войти или зарегистрироваться

Понравилась статья? Поделить с друзьями:
  • Книги руководства mazda demio
  • Угломер электронный с магнитным основанием digital ip54 инструкция
  • Руководство компрессор doosan
  • Листья ортосифона тычиночного почечного чая инструкция по применению
  • Сервис мануал для принтеров epson