Руководство разработчика AutoCAD .NET API

Настоящее руководство является вольным переводом на русский язык справочника arxmgd_dev.chm , он же "Managed .NET Developer's Guide (.NET)" из состава AutoCAD SDK.

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

Раздел "Getting Started with Microsoft Visual Studio (.NET)" оригинального справочника полностью отсутствует; по мнению автора в нем нет смысла, там не содержатся специфичные для AutoCAD материалы, эту информацию можно почерпнуть из множества иных источников.

Также в настоящем справочнике отсутствуют примеры и в целом упоминание VB.NET, как отжившего своё. Ресурсов на его перенос не было. Вставки на VBA (COM API) также не локализованы, они пойдут в другой справочник по ActiveX API.

В некоторых статьях приводятся примечания, на что обратить внимание при разработке приложения также под nanoCAD - что там реализовано иначе.

Исходные материалы настоящего руководства расположены в https://github.com/GeorgGrebenyuk/acadDevDocsRu/tree/main/docs/acad/net

Общие сведения

В данном разделе описывается принцип взаимодействия с приложением AutoCAD и его объектами при помощи .NET API. В данном разделе рассматривается использование языка программирования Microsoft® Visual C#.

Вступление

AutoCAD .NET API позволяет взаимодействовать с приложением AutoCAD и файлами чертежей с помощью настоящих библиотек (входящих в состав .NET API). К API можно получить доступ с помощью различных языков программирования (C#, VB.NET) и сред разработки (MS Visual Studio, JetBrains Rider и пр.). Реализация API .NET для AutoCAD имеет следующие особенности:

  • Программный доступ к чертежам для большего числа сред программирования;
  • Интеграция с другими приложениями на базе Windows упрощается благодаря использованию родного API .NET или открытой библиотеки ActiveX/COM.
  • За счет большей простоты языка (здесь, C#), чем в C++ (ObjectARX) возможна более гибкая реализация некоторых задач;

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

  • Графические объекты, такие как линии, дуги, текст и размеры;
  • Параметры стилей, например стили текста и размеров;
  • Набор объектов, такие как слои, группы и блоки;
  • Отображения чертежа, такие как вид и видовой экран;
  • Чертеж и приложение AutoCAD.

Состав .NET API

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

  • AcDbMgd.dll Используется при работе с объектами, хранящимися в файле чертежа;
  • AcCoreMgd.dll Используется для взаимодействия с приложением AutoCAD;
  • AcMgd.dll Взаимодействие с приложением AutoCAD и пользовательским интерфейсом;
  • AcCui.dll При работе с файлами адаптации приложений (cuix);

Библиотеки необходимо подключить к проекту, чтобы воспользоваться функциональностью содержащихся в них классов, методов и структур. В последние годы при написании приложений на .NET API целесообразнее подключать библиотеки с помощью NuGet-пакетов в альтернативу их ручного подключения из папок установки программы.

О Visual Studio, взаимодействие с другим API

В данной статье приведена информация о совместимости отдельных версий Microsoft Visual Studio (MS VS далее) с соответствующими версиями AUtoCAD .NET API. MS VS может взаимодействовать одновременно с .NET API и COM (ActiveX) API.

Версия AutoCADRelease-numПоддерживаемые версии SDKВерсия .NET
AutoCAD 2027?AutoCAD 202710.0(?)
AutoCAD 202625.1AutoCAD 2026, AutoCAD 20258.0
AutoCAD 202525.0AutoCAD 20258.0
AutoCAD 202424.3AutoCAD 2024, AutoCAD 2023, AutoCAD 2022, AutoCAD 20214.8
AutoCAD 202324.2AutoCAD 2023, AutoCAD 2022, AutoCAD 20214.8
AutoCAD 202224.1AutoCAD 2022, AutoCAD 20214.8
AutoCAD 202124.0AutoCAD 20214.8
AutoCAD 202023.1AutoCAD 2020, AutoCAD 20194.7
AutoCAD 201923.0AutoCAD 20194.7
AutoCAD 201822.0AutoCAD 20184.6
AutoCAD 201721.0AutoCAD 20174.6
AutoCAD 201620.1AutoCAD 2015, AutoCAD 20164.5
AutoCAD 201520.0AutoCAD 20154.5
AutoCAD 201419.1AutoCAD 2013, AutoCAD 20144.0
AutoCAD 201319.0AutoCAD 20134.0
AutoCAD 201218.2AutoCAD 2010, AutoCAD 2011, AutoCAD 20123.51 SP1
AutoCAD 201118.1AutoCAD 2010, AutoCAD 20113.51 SP1
AutoCAD 201018.0AutoCAD 20103.51 SP1
AutoCAD 200917.2AutoCAD 2007, AutoCAD 2008, AutoCAD 20093.0
AutoCAD 200817.1AutoCAD 2007, AutoCAD 20082.0
AutoCAD 200717.0AutoCAD 20072.0
AutoCAD 200616.2AutoCAD 2005, AutoCAD 20061.1 SP1
AutoCAD 200516.1AutoCAD 20051.1

Рекомендуемая версия MS VS для разработки 2022, в связи с тем, что под ней возможно загрузить .NET 8.0, используемый в AutoCAD с 2025й версии. Вместе с тем, возможно при помощи установщика MS VS загрузить целевую версию .NET 8, а разработку вести из-под Visual Studio 2019.

С версии AutoCAD 2027 и средой .NET 10 вести разработку вероятнее всего придется на MS VS 2026.

Для взаимодействия с AutoCAD .NET API необходимо подключить к целевому проекту библиотеки (перечень см. в пункте раннее) из папки установки AutoCADили через NuGet-пакеты от Autodesk. Версии пакетов см. по колонке "Release number".

Взаимодействие с C++ API

Из-под приложений на .NET API можно получать доступ к объектам, созданным на стороне неуправяемого кода (здесь, на ObjectARX); так как некоторые объекты AutoCAD не имеют управляемых оберток. Создать управляемый объект из неуправляемого объекта с помощью метода DisposableWrapper.Create(). Указатель на базовый неуправляемый объект из управляемого объекта можно получить с помощью свойства UnmanagedObject.

Взаимодействие с COM

Библиотеки COM (ActiveX) API в AutoCAD представлены Autodesk.AutoCAD.Interop.dll, Autodesk.AutoCAD.Interop.Common.dll

Существует 3 способа получения COM-интерфейса со стороны .NET API:

  • Сущность "Приложение AutoCAD";
  • Сущность "Документ AutoCAD" и "База данных документа AutoCAD";
  • Сущность "Объект AutoCAD";

На листинге ниже приведены основные приведения, доступные в .NET API для перехода к COM-интерфейсам

var acApp = Autodesk.AutoCAD.ApplicationServices.Application.AcadApplication as AutoCAD.AcadApplication;
Autodesk.AutoCAD.ApplicationServices.Document doc;
var acDoc = doc.GetAcadDocument() as AutoCAD.AcadDocument;
var acDb = doc.Database.AcadDatabase as AutoCAD.AcadDatabase;
Autodesk.AutoCAD.DatabaseServices.Entity ent;
var acEntity = ent.AcadObject as AutoCAD.AcadEntity;

Загрузка библиотек и данные приложения

Загрузка библиотек

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

Инициализация управляемых приложений

Для корректной загрузки .NET-приложений в одном из классов необходимо реализовать интерфейс Autodesk.AutoCAD.Runtime.IExtensionApplication (предоставляет методы Initialize() и Terminate()). Поскольку управляемые приложения нельзя выгрузить вручную, любая реализация метода Terminate() вызывается при закрытии AutoCAD.

Если ваше приложение определяет большое количество типов данных, вы можете оптимизировать его производительность при загрузке, реализовав IExtensionApplication и используя 2 необязательных пользовательских атрибута: ExtensionApplication и CommandClass - помогают AutoCAD найти процедуру инициализации приложения и обработчики команд. Любые управляемые приложения могут использовать эти атрибуты. Однако эффект их использования будет заметен только в приложениях со сложной логикой.

Использование ExtensionApplication и CommandClass

Когда программа AutoCAD загружает управляемое приложение, она запрашивает сборку приложения на наличие атрибута ExtensionApplication. Если такой атрибут найден, AutoCAD устанавливает связанный с ним тип в качестве точки входа приложения. Если такой атрибут не найден, AutoCADищет во всех экспортируемых типах реализацию IExtensionApplication. Если реализация не найдена, программа AutoCAD пропускает шаг инициализации, специфичный для приложения. Атрибут ExtensionApplication может быть присоединен только к одному классу. Класс, к которому он присоединен, должен реализовывать интерфейс IExtensionApplication. Помимо поиска реализации IExtensionApplication в приложении, AutoCAD запрашивает сборку приложения на наличие одного или нескольких атрибутов CommandClass. Если экземпляры этого атрибута найдены, программа AutoCAD ищет методы команд только в связанных с ними типах. В противном случае выполняется поиск во всех экспортируемых типах. Атрибут CommandClass может быть объявлен для любого класса, содержащего обработчики команд AutoCAD. Ниже описано, как используются эти атрибуты.

  1. Определите тип, реализующий Autodesk.AutoCAD.Runtime.IExtensionApplication. Если вам не нужно выполнять задачи инициализации или завершения, оставьте пустые реализации методов интерфейса;

  2. В контексте сборки (пространстве имен любого файла) объявите атрибут ExtensionApplication;

  3. Передайте тип, реализующий интерфейс IExtensionApplication, в атрибут ExtensionApplication.

  4. В контексте сборки объявите атрибут CommandClass для каждого класса, определяющего методы команд AutoCAD.

  5. Передайте тип класса метода команды в атрибут CommandClass.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Interop;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

[assembly: ExtensionApplication(typeof(Loader))]
[assembly: CommandClass(typeof(HelloWorldCommands))]
public class Loader : IExtensionApplication
{
    public class HelloWorldCommands
    {
        [CommandMethod("HELLO1")]
        public void HelloCommand1()
        {
            Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("HELLO1");
        }
    }
    public void Initialize()
    {
    }
    public void Terminate()
    {
    }
}
public class HelloWorldCommands
{
    [CommandMethod("HELLO2")]
    public void HelloCommand2()
    {
        Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("HELLO2");
    }
}

Пользовательские данные для документа

В AutoCAD .NET API имеется возможность сохранить какие-либо данные на уровне чертежа.

При загрузке исполняемой библиотеки в AutoCAD, запрашиваются классы, помеченные атрибутом PerDocumentClass. Если был найден такой класс (классы), то для каждого открытого документа (Document) создается экземпляр класса данного типа; далее в сессии приложения для каждого нового документа повторяется создание таких классов. Тип (класса), связанный с атрибутом PerDocumentClass должен удовлетворять следующим правилам (на выбор):

  • иметь публичный конструктор, принимающий в качестве аргумента объект Autodesk.AutoCAD.ApplicationServices.Document;
  • иметь публичный статический метод Create, также принимающий в качестве аргумента объект Autodesk.AutoCAD.ApplicationServices.Document и возвращающий новый объект данного типа. Если имеется статический метод Create, он будет использован предпочтительно конструктору (если он также существует).

Создаваемый экземпляр класса будет "знать", к какому документ он будет привязан (так как документ фигурирует в аргументах для создания экземпляра класса). Следующая процедура описывает использование атрибута PerDocumentClass. В исходном коде загружаемой сборки объявите атрибут PerDocumentClass для каждого класса, который вы хотите связать с каждым из документов.

Базовые основы AutoCAD .NET API

В данном разделе приведено краткое описание объектной модели AutoCAD для понимания, как с ней взаимодействовать со стороны настоящего .NET API.

Введение в иерархию объектов AutoCAD

Минимальная структурная единица модели AutoCAD -- это объект. Некоторые из объектов, с которыми вы будете сталкиваться при работе с настоящим .NET API это:

  • Графические объекты: окружности, текст, полилинии, размеры и пр.;
  • Настройки стилей и свойств документа :: слои, типы линий, типы размеров и т.д.;
  • Коллекции или инструменты компоновки объектов :: слои, группы, блоки;
  • Отображение части модели :: листы и видовые экраны;
  • Сущность документа и приложения AutoCAD.
  • и множество иных категорий объектов;

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

Верхнеуровневый элемент - это сущность приложения Autodesk.AutoCAD.ApplicationServices.Application.

На схеме ниже показаны связи между приложением Application, документом и объектами, находящимися в составе BlockTableRecord, например пространством модели.

Приложение AutoCAD

Объект Application является корневым объектом AutoCAD .NET API. Из-под объекта Application можно получить доступ к главному окну, а также к любому открытому чертежу. После открытия чертежа можно также получить доступ к его объектам.

Важно иметь в виду, что в AutoCAD .NET API приложение Application не является объектом, с которым можно взаимодействовать. В отличие от Document, который можно получить, например, как Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument, приложение невозможно получить никаким образом, оно существует в виде статического класса, у которого можно только вызывать его методы и свойства. Например, у объекта Application есть свойство DocumentManager, которое возвращает объект Autodesk.AutoCAD.ApplicationServices.DocumentCollection. Этот объект предоставляет доступ к чертежам, которые в данный момент открыты в сессии AutoCAD; кроме того, он позволяет создавать новые документы, сохранять и открывать файлы чертежей. Другие свойства объекта Application предоставляют доступ к данным, специфичным для приложения.

Большинство свойств объекта Application предоставляют доступ к объектам AutoCAD .NET API, однако некоторые из них ссылаются на объекты AutoCADActiveX® Automation. К таким свойствам относятся COM-версия объекта приложения (AcadApplication), менюбар (MenuBar), загруженные группы меню (MenuGroups) и настройки профиля (Preferences). Получить COM-объект приложения можно при помощи конструкции:

var nApp = Autodesk.AutoCAD.ApplicationServices.Application.AcadApplication as AutoCAD.AcadApplication;

Можно отметить следующие свойства приложения:

  • DocumentManager: предоставляет доступ к взаимодействию с отдельными чертежами для данной сессии AutoCAD;
  • DocumentWindowCollection: коллекция всех окон документов (каждый из документов хранится в DocumentManager), в основном окна характеризуются различными свойствами размеров и координат;
  • MainWindow: доступ к имени, размеру, расположению и видимости окна приложения AutoCAD;
  • UserConfigurationManager: управление профилями настройки приложения;

Документ AutoCAD

Объект Document, который фактически является чертежом AutoCAD, обрабатывается через DocumentCollection. Объекты DocumentExtension и DocumentCollectionExtention используются для создания, открытия и закрытия файлов чертежей. Объект Document предоставляет доступ к объекту Database, который содержит все графические и большинство неграфических объектов AutoCAD. Вместе с тем, база данных чертежа Database может быть получена и без открытия документа при помощи следующей процедуры:

Database db = new Database(false, true);
dwgDb.ReadDwgFile(pathToDwg, FileOpenMode.OpenForReadAndReadShare, false, null);

Наряду с объектом базы данных объект Document предоставляет доступ к строке состояния, окну, в котором открыт документ, специальным объектам Editor и TransactionManager. Класс Editor предоставляет доступ к функциям ввода-вывода данных от пользователя -- выбор точки, объектов, вывод сообщения в консоль и т.д.

Класс TransactionManager используется для доступа к нескольким объектам базы данных в рамках одной операции, называемой транзакцией. Транзакции могут быть вложенными, а по завершении транзакции вы можете зафиксировать (Commit) или отменить (Abort) внесенные изменения. ActiveX-интерфейс, описывающий документ, называется AutoCAD.AcadDocument и может быть получен следующим приведением:

Autodesk.AutoCAD.ApplicationServices.Document doc;
AutoCAD.AcadDocument docCOM = doc.GetAcadDocument() as AutoCAD.AcadDocument;

База данных чертежа

Объект Database содержит все графические и большинство неграфических объектов AutoCAD. К объектам, содержащимся в базе данных, относятся сущности, таблицы символов и именованные словари. Сущности в базе данных представляют собой графические объекты чертежа. Линии, окружности, дуги, текст, штриховка и полилинии являются примерами графических объектов. Пользователь может видеть графический объект на экране и работать с ним. Доступ к объекту базы данных для текущего документа осуществляется через свойство-член Database объекта Document.

Autodesk.AutoCAD.ApplicationServices.Document doc;
Database db = doc.Database;

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

Database db = new Database(false, true);
dwgDb.ReadDwgFile(pathToDwg, FileOpenMode.OpenForReadAndReadShare, false, null);

ActiveX-интерфейс, описывающий Базу данных, называется OdaX.AcadDatabase и может быть получен следующим приведением:

Autodesk.AutoCAD.DatabaseServices.Database db;
AutoCAD.AcadDatabase dbCOM = db.AcadDatabase as AutoCAD.AcadDatabase;

Таблицы символов и словари

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

  • LayerTable : Таблица слоёв;
  • BlockTable Таблица блоков;
  • AbstractViewTable : базовый класс:таблица для ViewTable : таблица с коллекцией видов, ViewportTable : таблица с коллекцией видовых экранов;
  • DataTable : вспомогательная таблица для хранения в виде двумерной таблицы числовых и текстовых данных, 3D:точек и идентификаторов объектов;
  • LinetypeTable : таблица типов линий;
  • DimStyleTable : таблица размерных стилей;
  • DrawOrderTable : вспомогательная таблица для управления порядком отрисовки объектов, принадлежащих записи таблицы блоков;
  • RegAppTable : таблица символов APPID, которая представляет зарегистрированные имена приложений для расширенных данных объекта чертежа в базе данных;
  • UcsTable : таблица для хранения в базе данных чертежа пользовательских систем координат (UCS);
  • TextStyleTable : таблица со стилями текста;

Для каждой таблицы существуют классы:обработчики записей таблицы : они имеют суффикс Record. Подробнее о таблицах объектов см. статью Коллекции объектов. Отдельно в AutoCAD имеется PlotStyleTable - таблица параметров печати; это специфичный объект AutoCAD, для неё не предусмотрено класса-обработчика записей в составе таблицы. Все графические объекты (линии, окружности, дуги и так далее) принадлежат записям таблицы блоков (BlockTable). По умолчанию каждый чертеж содержит предопределенные таблицы блоков (BlockTable) для пространства Model и каждого из листов PaperSpace. Словари :: это специальный объект, описываемый соответствующим набором классов, который представляют собой расширенные описания объекта. У словарей есть ограничение на емкость и свои особенности поведения. Словари могут быть как у графических, так и у неграфичеких объектов AutoCAD, включая и саму базу данных чертежа, а также у записей XRecord, их количество не ограниченно, их можно свободно создавать, редактировать, удалять (кроме нескольких "служебных" словарей у объектов чертежа). Словари могут быть и вложенными друг в друга. Хранится словари могут в двух местах:

  • в именованном словаре Базы данных (объекта, получаемого через свойство Database.NamedObjectsDictionaryId);
  • в словаре:расширении у таблицы символов или конечного графического примитива (оба упомянутых объекта наследуют класс DBObject со свойством ExtensionDictionary);

Графические и неграфические объекты

Графические объекты

Графические объекты, также известные как сущности (Entity), - это видимые объекты (линии, окружности, растровые изображения и т. д.), образующие чертеж. Добавление графических объектов в чертеж осуществляется путем получения ссылки на соответствующую запись таблицы блоков (здесь, пространство модели, лист или конкретное определение Блока), а затем с помощью метода AppendEntity с передачей ему нового объекта нужного класса для добавления сущности в чертеж.

Чтобы изменить или получить информацию о графическом объекте, получите ссылку на объект из соответствующей записи таблицы блоков (среди объектов ModelSpace и т.д.), а затем используйте методы или свойства самого объекта, приводя полученный DBObject к соответствующему классу. В C# это делается с помощью ключевого слова "as". Каждый графический объект имеет методы, реализованные на стороне пользовательского интерфейса, такие как копирование, стирание, перемещение, зеркальное отображение за некоторым исключением (например, не для всех объектов доступна разбивка на примитивы (Explode) и т.д.).

Кроме того, эти объекты имеют методы для получения расширенных данных (xdata), выделения и снятия выделения с объекта, а также установки свойств другого объекта. Большинство графических объектов имеют некоторые общие свойства, такие как LayerId, LinetypeId, Color и Handle, как наследники класса Autodesk.AutoCAD.DatabaseServices.Entity. Каждый графический объект также имеет специфические свойства, такие как Center или TextString -- в зависимости от своего типа -- здесь, Center у окружности Circle или дуги Arc, TextString у однострочного текста DBText и т.д.

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

Autodesk.AutoCAD.DatabaseServices.Arc someArcObject;
AutoCAD.AcadEntity entityCOM = someArcObject.AcadObject as AutoCAD.AcadEntity;

Также имеется возможность приведения COM-интерфейса объекта к специфичному COM-интерфейсу, описывающему данный объект со своей стороны. Например, выше мы получили общий интерфейс AutoCAD.AcadEntity у объекта "Дуга", а могли бы привести его к AutoCAD.AcadArc и были бы правы.

Неграфические объекты

Неграфические объекты - это невидимые (информационные) объекты, являющиеся частью чертежа, такие как слои, типы линий, стили размеров, стили таблиц и так далее. Чтобы создать новую запись в таблице символов, используйте метод Add у таблицы-контейнера данных объектов (перечень см. в статей "Объект базы данных чертежа") или метод SetAt для добавления словаря в словарь именованных объектов. Чтобы изменить или получить какую-либо информацию об объекте, используйте методы или свойства самого объекта. Каждый неграфический объект имеет методы и свойства, специфичные для его класса; все они имеют методы для получения расширенных данных (xdata) и своего удаления. Подробнее о них см. раздел.

Коллекции объектов

В AutoCAD большинство объектов (графических и неграфических) хранятся в составе коллекций (специальные объекты-контейнеры). Хотя коллекции и содержат различные типы данных, их можно обрабатывать с помощью схожих методов. Каждая коллекция имеет метод для добавления объекта в коллекцию или получения элемента из коллекции. Большинство коллекций используют методы Add (обычные коллекции -- Table) или SetAt (словари) для добавления объекта в коллекцию. На уровне исходного кода, классы-коллекции являются наследниками интерфейсов IEnumerable, ICollection. Большинство коллекций имеют схожие методы и свойства, что упрощает их использование и изучение; например, для перебора коллекции удобно использовать цикл foreach. Примерами членов коллекции в AutoCAD .NET API являются:

  • LayerTableRecord в таблице свойств LayerTable (как и аналогичные элементы любой таблицы имеющие суффикс Record);

  • Лист в словаре ACAD_LAYOUT;

  • Document в коллекции DocumentCollection;

  • Атрибуты Вхождения блока; Полный список подобных объектов представлен ниже:

НазваниеСоответствующий .NET-классКомментарий
Block Table Record - запись таблицы блоков
Получается через перебор объектов BlockTable
DatabaseServices.BlockTableRecordСодержит все графические сущности в определении данного Блока (напомним, Блоком в объектной модели DWG являются именованные пространства -- модели, листов, а также специальных объектов, имеющих Вхождения блоков).
Block Table - таблица блоков
Получается через транзакцию для Database.BlockTableId;
DatabaseServices.BlockTableСодержит все Блоки чертежа (пояснение см. выше)
Named Objects Dictionary - именованный словарь объектов
Получается через транзакцию для Database.NamedObjectsDictionaryId
DatabaseServices.DBDictionaryСодержит все словари чертежа
Dimension Style Table - таблица размерных стилейDatabaseServices.DimStyleTableПолучается через транзакцию для Database.DimStyleTableId
Document Collection - коллекция открытых чертежейApplicationServices.DocumentCollectionПолучается через ApplicationServices.Application.DocumentManager
Group Dictionary - коллекция всех Групп чертежаDatabaseServices.DBDictionaryПолучается через транзакцию для Database.GroupDictionaryId
Hyperlink Collection - коллекция гиперссылок объектаDatabaseServices.HyperLinkCollectionПолучается у объекта графической сущности DatabaseServices.Entity в рамках транзакции как свойство Hyperlinks
Layer Table - таблица всех слоев чертежаDatabaseServices.LayerTableПолучается через транзакцию для Database.LayerTableId
Layout Dictionary - перечень всех листов документаDatabaseServices.DBDictionaryПолучается через транзакцию для Database.LayoutDictionaryId
Linetype Table - перечень всех типов линий документаDatabaseServices.LinetypeTableПолучается через транзакцию для Database.LinetypeTableIdId
MenuBar Collection - коллекция всех классических менюAutoCAD.AcadMenuBar(ActiveX)Получается как ApplicationServices.Application.MenuBar с последующим приведением к интерфейсу AutoCAD.AcadMenuBar
MenuGroup Collection - коллекция всех ленточных менюAutoCAD.AcadMenuGroups (ActiveX)Получается как ApplicationServices.Application.MenuGroups с последующим приведением к интерфейсу AutoCAD.AcadMenuGroups
Plot Configuration Dictionary - именованные параметры печати чертежаDatabaseServices.DBDictionaryПолучается через транзакцию для Database.PlotSettingsDictionaryId
Registered Application Table - таблица зарегистрированных приложенийDatabaseServices.RegAppTableПолучается через транзакцию для Database.RegAppTableId
Text Style Table - перечень текстовых стилейDatabaseServices.TextStyleTableПолучается через транзакцию для Database.TextStyleTableId
UCS Table - перечень всех определений ПСК (пользовательских систем координат)DatabaseServices.UcsTableПолучается через транзакцию для Database.UcsTableId
View Table - перечень всех видовDatabaseServices.ViewTableПолучается через транзакцию для Database.ViewTableId
Viewport Table - перечень всех видовых экрановDatabaseServices.ViewportTableПолучается через транзакцию для Database.ViewportTableId

Получение доступа к коллекции

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

// Get the current document and start the Transaction Manager
Database acCurDb = Application.DocumentManager.MdiActiveDocument.Database;
using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
{
    // This example returns the layer table for the current database
    LayerTable acLyrTbl;
    acLyrTbl = acTrans.GetObject(acCurDb.LayerTableId,
                                 OpenMode.ForRead) as LayerTable;
 
     // Dispose of the transaction
}

Перебор объектов коллекции

Для получения конкретного объекта коллекции, представленной таблицей можно использовать обращение к ней, как к словарю, указывая в качестве поискового строкового ключа наименование объекта. Для предварительной проверки, имеется ли в коллекции определение объекта целесообразно использование метода Has. Для словарей (DBDictionary) имеется только метод GetAt.

//Обращение к таблице слоев
ObjectId acObjId;
if (acLyrTbl.Has("MyLayer")) acObjId = acLyrTbl["MyLayer"];

Для итеративного перебора объектов коллекции возможно использование цикла foreach. Существует устойчивое название "запись", которым обозначают объект коллекции, хранящейся в Таблице данных (подробнее о таблицах см. соответствующий раздел в статье База данных чертежа. Подобные объекты-записи имеют специфичный тип, равный названию класса таблицы + суффикс Record. Для каждой из 11 таблиц существуют соответствующие классы-записи с суффиксами Record. Рассмотрим перебор слоев таблицы слоев, а также проверку, имеется ли среди таблицы слоев слой с фиксированным именем:

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

[CommandMethod("AddMyLayer")]
public static void AddMyLayer()
{
  // Get the current document and database, and start a transaction
  Document acDoc = Application.DocumentManager.MdiActiveDocument;
  Database acCurDb = acDoc.Database;

  using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
  {
      // Returns the layer table for the current database
      LayerTable acLyrTbl;
      acLyrTbl = acTrans.GetObject(acCurDb.LayerTableId,
                                   OpenMode.ForRead) as LayerTable;

      // Check to see if MyLayer exists in the Layer table
      if (acLyrTbl.Has("MyLayer") != true)
      {
          // Open the Layer Table for write
          acTrans.GetObject(acCurDb.LayerTableId, OpenMode.ForWrite);

          // Create a new layer table record and name the layer "MyLayer"
          using (LayerTableRecord acLyrTblRec = new LayerTableRecord())
          {
              acLyrTblRec.Name = "MyLayer";

              // Add the new layer table record to the layer table and the transaction
              acLyrTbl.Add(acLyrTblRec);
              acTrans.AddNewlyCreatedDBObject(acLyrTblRec, true);
          }

          // Commit the changes
          acTrans.Commit();
      }

      // Dispose of the transaction
  }
}

Добавление объекта в коллекцию

Для добавления нового объекта используйте метод Add для коллекций-таблиц или метод SetAt для словарей. Код ниже создает новый слой в таблицу LayerTable:

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
 
[CommandMethod("AddMyLayer")]
public static void AddMyLayer()
{
  // Get the current document and database, and start a transaction
  Document acDoc = Application.DocumentManager.MdiActiveDocument;
  Database acCurDb = acDoc.Database;
 
  using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
  {
      // Returns the layer table for the current database
      LayerTable acLyrTbl;
      acLyrTbl = acTrans.GetObject(acCurDb.LayerTableId,
                                   OpenMode.ForRead) as LayerTable;
 
      // Check to see if MyLayer exists in the Layer table
      if (acLyrTbl.Has("MyLayer") != true)
      {
          // Open the Layer Table for write
          acTrans.GetObject(acCurDb.LayerTableId, OpenMode.ForWrite);
 
          // Create a new layer table record and name the layer "MyLayer"
          using (LayerTableRecord acLyrTblRec = new LayerTableRecord())
          {
              acLyrTblRec.Name = "MyLayer";
 
              // Add the new layer table record to the layer table and the transaction
              acLyrTbl.Add(acLyrTblRec);
              acTrans.AddNewlyCreatedDBObject(acLyrTblRec, true);
          }

          // Commit the changes
          acTrans.Commit();
      }
 
      // Dispose of the transaction
  }
}

Удаление объекта из коллекции

Члены объекта коллекции могут быть удалены с помощью метода Erase. Например, следующий код стирает слой MyLayer из объекта LayerTable. Прежде чем стирать слой с чертежа, необходимо убедиться, что его можно безопасно удалить. Чтобы определить, можно ли стирать слой или другой именованный объект, например блок или текстовый стиль, следует использовать метод Purge (на стороне UI).


using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
 
[CommandMethod("RemoveMyLayer")]
public static void RemoveMyLayer()
{
  // Get the current document and database, and start a transaction
  Document acDoc = Application.DocumentManager.MdiActiveDocument;
  Database acCurDb = acDoc.Database;
 
  using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
  {
      // Returns the layer table for the current database
      LayerTable acLyrTbl;
      acLyrTbl = acTrans.GetObject(acCurDb.LayerTableId,
                                   OpenMode.ForRead) as LayerTable;
 
      // Check to see if MyLayer exists in the Layer table
      if (acLyrTbl.Has("MyLayer") == true)
      {
          LayerTableRecord acLyrTblRec;
          acLyrTblRec = acTrans.GetObject(acLyrTbl["MyLayer"],
                                          OpenMode.ForWrite) as LayerTableRecord;
 
          try
          {
              acLyrTblRec.Erase();
              acDoc.Editor.WriteMessage("\n'MyLayer' was erased");
 
              // Commit the changes
              acTrans.Commit();
          }
          catch
          {
              acDoc.Editor.WriteMessage("\n'MyLayer' could not be erased");
          }
      }
      else
      {
          acDoc.Editor.WriteMessage("\n'MyLayer' does not exist");
      }
 
      // Dispose of the transaction
  }
}

Доступ к объектам в иерархии

При работе с объектами в AutoCAD .NET API возможно обращаться к некоторым объектам напрямую или использовать пользовательские переменные для целевого объекта. Например, для вставки в данный чертеж внешней ссылки необходимо последовательно пройтись от Приложения, Документа, Базы данных модели к методу, позволяющему осуществить вставку внешней ссылки. Либо можно сократить часть вызовов, работая с переменной, содержащей информацию о текущей базе данных.

string strFName = "C:\\Work\\drawing.dwg";
Autodesk.AutoCAD.DatabaseServices.ObjectId objId;
//Прямой вызов методов
objId = Application.DocumentManager.MdiActiveDocument.Database.AttachXref(strFName, "drawing1");
//Вызов с использованием переменных
Autodesk.AutoCAD.DatabaseServices.Database db = Application.DocumentManager.MdiActiveDocument.Database;
objId = db.AttachXref(strFName, "drawing1");

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

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

[CommandMethod("ListEntities")]
public static void ListEntities()
{
    // Get the current document and database, and start a transaction
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table record for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                     OpenMode.ForRead) as BlockTable;
        // Open the Block table record Model space for read
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForRead) as BlockTableRecord;
        int nCnt = 0;
        acDoc.Editor.WriteMessage("\\nModel space objects: ");
        // Step through each object in Model space and
        // display the type of object found
        foreach (ObjectId acObjId in acBlkTblRec)
        {
            acDoc.Editor.WriteMessage("\\n" + acObjId.ObjectClass.DxfName);
            nCnt = nCnt + 1;
        }
        // If no objects are found then display a message
        if (nCnt == 0)
        {
            acDoc.Editor.WriteMessage("\\n  No objects found");
        }
        // Dispose of the transaction
    }
}

Понятие свойств и методов

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

Например, у объекта окружности Circle есть свойство Center. Это свойство представляет собой точку в мировой системе координат (WCS) в центре этой окружности. Чтобы изменить центр окружности, просто установите свойству Center новое значение точки (экземпляр класса Point3d). У объекта Circle также есть метод GetOffsetCurves. Этот метод создает кривые на заданном расстоянии смещения от существующей окружности. К слову, метод GetOffsetCurves применим не только к окружности, но также и ко всем графическим примитивам, созданным на основе кривых (полилинии, сплайны, отрезки и пр.).

Чтобы просмотреть список всех свойств и методов объекта Circle, обратитесь к справке по объекту Circle или воспользуйтесь браузером объектов в Microsoft Visual Studio.

Межпроцессорное взаимодействие

При разработке нового приложения (на поддерживаемых языках программирования) оно может выполняться как в основном процессе AutoCAD, так и вне процесса. Настоящее .NET API предназначено только для работы в одном процессе с AutoCAD; вместе с тем использование ActiveX Automation позволяет работать как в данном процессе, так и вне его.

Для создания нового процесса AutoCAD необходимо создать новый экземпляр приложения AutoCAD или обратиться к уже запущенному приложению помимо данного. После получения ссылки на запущенный экземпляр AutoCAD, при помощи ActiveX Automation производим загрузку в него целевой .NET-библиотеки с помощью метода SendCommand, который является членом документа AutoCAD, возвращаемым через свойство ActiveDocument приложения AcadApplication.

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

using System;
using System.Runtime.InteropServices;

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Interop;

public class Loader
{
    [CommandMethod("ConnectToNcad")]
    public static void ConnectToNcad()
    {
        AcadApplication acAppComObj = null;
        const string strProgId = "AutoCAD.Application.24.2";
        // Get a running instance of nanoCAD
        try
        {
            acAppComObj = (AcadApplication)Marshal.GetActiveObject(strProgId);
        }
        catch // An error occurs if no instance is running
        {
            try
            {
                // Create a new instance of AutoCAD
                acAppComObj = (AcadApplication)Activator.CreateInstance(Type.GetTypeFromProgID(strProgId), true);
            }
            catch
            {
                // If an instance of AutoCAD is not created then message and exit
                System.Windows.Forms.MessageBox.Show("Instance of 'AutoCAD.Application' could not be created.");
                return;
            }
        }
        // Display the application and return the name and version
        acAppComObj.Visible = true;
        System.Windows.Forms.MessageBox.Show("Now running " + acAppComObj.Name + "version " + acAppComObj.Version);
        // Get the active document
        AcadDocument acDocComObj;
        acDocComObj = acAppComObj.ActiveDocument;
        // Optionally, load your assembly and start your command or if your assembly
        // is demandloaded, simply start the command of your in-process assembly.
        acDocComObj.SendCommand("(command " + (char)34 + "NETLOAD" + (char)34 + " " +
                                (char)34 + "c:/myapps/mycommands.dll" + (char)34 + ") ");
        acDocComObj.SendCommand("MyCommand ");
    }
}

Примечание: при использовании .NET 8+ метода System.Runtime.InteropServices.Marshal.GetActiveObject в системной библиотеке нет. Вместо него можно использовать различные реализации, например, код ниже:

//From https://stackoverflow.com/a/65496277
public static class Marshal2
{
    \internal const String OLEAUT32 = "oleaut32.dll";
    \internal const String OLE32 = "ole32.dll";
    [System.Security.SecurityCritical]  // auto-generated_required
    public static Object GetActiveObject(String progID)
    {
        Object obj = null;
        Guid clsid;
        // Call CLSIDFromProgIDEx first then fall back on CLSIDFromProgID if
        // CLSIDFromProgIDEx doesn't exist.
        try
        {
            CLSIDFromProgIDEx(progID, out clsid);
        }
        //            catch
        catch (System.Exception)
        {
            CLSIDFromProgID(progID, out clsid);
        }
        GetActiveObject(ref clsid, IntPtr.Zero, out obj);
        return obj;
    }
    //[DllImport(Microsoft.Win32.Win32Native.OLE32, PreserveSig = false)]
    [DllImport(OLE32, PreserveSig = false)]
    [System.Runtime.Versioning.ResourceExposure(System.Runtime.Versioning.ResourceScope.None)]
    [System.Security.SuppressUnmanagedCodeSecurity]
    [System.Security.SecurityCritical]  // auto-generated
    private static extern void CLSIDFromProgIDEx([MarshalAs(UnmanagedType.LPWStr)] String progId, out Guid clsid);
    //[DllImport(Microsoft.Win32.Win32Native.OLE32, PreserveSig = false)]
    [DllImport(OLE32, PreserveSig = false)]
    [System.Runtime.Versioning.ResourceExposure(System.Runtime.Versioning.ResourceScope.None)]
    [System.Security.SuppressUnmanagedCodeSecurity]
    [System.Security.SecurityCritical]  // auto-generated
    private static extern void CLSIDFromProgID([MarshalAs(UnmanagedType.LPWStr)] String progId, out Guid clsid);
    //[DllImport(Microsoft.Win32.Win32Native.OLEAUT32, PreserveSig = false)]
    [DllImport(OLEAUT32, PreserveSig = false)]
    [System.Runtime.Versioning.ResourceExposure(System.Runtime.Versioning.ResourceScope.None)]
    [System.Security.SuppressUnmanagedCodeSecurity]
    [System.Security.SecurityCritical]  // auto-generated
    private static extern void GetActiveObject(ref Guid rclsid, IntPtr reserved, [MarshalAs(UnmanagedType.Interface)] out Object ppunk);
}

Команды и LISP-функции

Отдельные команды и LISP-функции реализуются в .NET API при помощи соответствующих атрибутов -- CommandMethod и LispFunction. Один их данных атрибутов размещается перед целевым методом класса для возможности последующего вызова команды из-под запущенного AutoCAD. Методы не должны содержать каких-либо аргументов, включая опциональные. Единственное исключение -- для LISP-функций может быть задан единственный аргумент с типом ResultBuffer.

Определение командных методов

При определении команды используется атрибут CommandMethod (полное имя -- Autodesk.AutoCAD.Runtime.CommandMethod). Атрибут CommandMethod ожидает строковое значение, которое будет использоваться в качестве глобального имени определяемой команды. Наряду с глобальным именем команды атрибут CommandMethod может принимать следующие значения (значения приведены с указанием имени свойства класса CommandMethodAttribute):

  • Flags (Флаги команды) : Определяют поведение команды, записываются в виде CommandFlags.UsePickSet | CommandFlags.NoBlockEditor | и т.д.;
  • GroupName (Имя группы команд);
  • LocalizedNameId (Локализованное имя) : локальное имя команды, обычно специфичное для используемой языковой локализации;
  • HelpTopic : Идентификатор раздела справки, который откроется при нажатии на F1 (обычно представляет собой относительный путь к странице в составе CHM:справочника или URL:адрес к страничке с документацией, развернутой локально или на публичном ресурсе);
  • ContextMenuExtensionType : Определяет поведение контекстного меню, когда команда активна (исполняется);
  • HelpFileName : Файловый путь к файлу справки, содержащий справочную информацию, которая будет отображаться при нажатии на F1 во время выполнения команды;

Примеры использования аргументов:

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.EditorInput;

[CommandMethod("Test")]
public void TestNotInGroup()
{
      Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
      ed.WriteMessage("Test Command NOT in group.\\n");
}

[CommandMethod("CmdGroup", "Test", CommandFlags.Modal)]
public void TestInGroup()
{
      Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
      ed.WriteMessage("Test Command in CmdGroup.\\n");
}

Выше представлены 2 метода. Оба имеют внутреннее имя "Test", но при вводе в командную строку AutoCAD команды по имени "Test" или ".Test" запустится только первая (объявляющая метод TestNotInGroup). При вводе в командную строку AutoCAD команды по имени "CmdGroup.Test" запустится уже вторая команда (объявляющая метод TestInGroup). Специфика первого аргумента в атрибуте CommandMethod заключается в названии группы (groupName), в которой будет лежать объявление целевого метода (объявленного вторым аргументом : globalName). Полный список поддерживаемых конструкторов из справки:

public CommandMethodAttribute(ref String globalName);
public CommandMethodAttribute(ref String globalName, CommandFlags flags);
public CommandMethodAttribute(ref String groupName, ref String globalName, ref String localizedNameId, CommandFlags flags);
public CommandMethodAttribute(ref String groupName, ref String globalName, ref String localizedNameId, CommandFlags flags, ref String helpTopic);
public CommandMethodAttribute(ref String groupName, ref String globalName, ref String localizedNameId, CommandFlags flags, ref Type contextMenuExtensionType);
public CommandMethodAttribute(ref String groupName, ref String globalName, ref String localizedNameId, CommandFlags flags, ref Type contextMenuExtensionType, ref String helpFileName, ref String helpTopic);

Доступные типы CommandFlags

Таблица ниже содержит перечень доступных к использованию флагов команд CommandFlags:

Enum ValueDescription
ActionMacroКоманда может быть записана при помощи утилиты Action Recorder (не доступно в AutoCAD)
DefunКоманда может быть вызвана как LISP-функция с возможностью использования acedGetArgs() для получения аргументов из LISP и функции acedRetXxx() для возврата значений в LISP. Этот флаг может быть установлен только движком Visual LISP.
DocExclusiveLockПри вызове и работе команды документ будет заблокирован.
DocReadLockDocument will be read locked when command is invoked.
InterruptibleВыполнение команды может быть прервано, если будет запрошен пользовательский ввод (через командную строку)
ModalКоманда не будет вызвана, пока активна какая-либо другая команда
NoActionRecordingКоманда не может быть записана при помощи утилиты Action Recorder
NoBlockEditorКоманда не может быть запущена или использована внутри Редактора блоков
NoHistoryКоманда не будет добавлена в историю "последних команд"
NoInferConstraintКоманда не может быть использована, если установлены ограничения AutoCAD
NoInternalLockПри выполнении команды документ не сможет быть внутренне заблокирован (инструкции по блокировке документа внутри метода не будут выполнены)
NoMultipleКоманда не будет поддерживать перегрузки метода через символы "*" как аргументы макроса для запуска команды
NoNewStackКоманда не будет создавать новый элемент во внутреннем стеке.
NoOEMКоманда не будет доступна из среды OEM-программы.
NoPaperSpaceКоманда не может быть запущена из пространства Листа
NoPerspectiveКоманда не может быть запущена, если значение переменной PERSPECTIVE = 1
NoTileModeКоманда не может быть запущена, если значение переменной TILEMODE = 1
NoUndoMarkerКоманда не будет поддерживать маркеры отмены. Это предназначено для команд, которые не изменяют базу данных, и, следовательно, не должны фигурировать в файле отмены.
RedrawПри изменении набора выделенных объектов команда запускается заново.
SessionКоманда будет выполняться в среде сессии AutoCAD, а не только для данного документа
TempShowDynDimensionКоманда разрешает отображение динамического ввода при выборе объектов
TransparentКоманда может использоваться одновременно с другой запущенной командой.
UndefinedКоманда может быть использована (запущена) только по своему Глобальному имени (то есть без привязки к Категории)
UsePickSetКоманда срабатывает по отношению к первому выделенному объекту в рамках пользовательского выбора.

Для задания нескольких флагов одновременно используйте в C# символ | .

[CommandMethod("CheckForPickfirstSelection", CommandFlags.UsePickSet |
                                             CommandFlags.NoBlockEditor)]
public static void CheckForPickfirstSelection()
{
 //...
}

Статические и нестатические методы

Командные методы могут быть объявлены как статические или нестатические (они же так называемые "either instance" или "instance commands") публичные методы класса. Статические командные методы объявляются с ключевым словом static в C# или с ключевым словом Shared в VB .NET.

Для нестатического командного метода, реализуемый метод устанавливается отдельно для каждого открытого документа. Это означает, что каждый документ получает частную копию данных экземпляра команды (метода под атрибутом CommandMethod). Таким образом, нет опасности перезаписать данные конкретного документа, когда пользователь переключается между документами. Если методу экземпляра требуется глобальный обмен данными, он может сделать это, объявив методы статическими или используя общие переменные/поля класса.

Для статического командного метода управляемому модулю (загруженной библиотеке) не нужно загружать данный метод во все экземпляры документов. Используется единственная копия данных метода, независимо от контекста документа. Статические команды обычно не используют данные, относящиеся к документу, и не требуют специального обработчика для режима MDI (Multiple Document Interface).

Экземплярные (нестатические) и статические методы могут быть определены с помощью флагов команд для специфичных задач. Например, метод экземпляра может быть объявлен с атрибутом, устанавливающим флаг CommandFlags.Session. Это означает, что команда работает в контексте выполнения приложения, но при этом сохраняет данные для каждого документа. Примером такой команды в AutoCAD является команда PROPERTIES. Аналогично, статический метод может быть объявлен без флага CommandFlags.Session. Такая комбинация полезна для команд, которые выполняются в контексте документа, но не нуждаются в сохранении данных, относящихся к документу.

Определение LISP-функций

При определении функции AutoLISP (LISP) используется атрибут LispFunction (полное имя Autodesk.AutoCAD.Runtime.LispFunctionAttribute). Атрибут LispFunction ожидает строковое значение для использования в качестве глобального имени определяемой функции AutoLISP. Наряду с глобальным именем функции структура LispFunction может принимать те же значения, что и CommandMethod (о нём см. статью раннее).

[LispFunction("DisplayFullName")]
public static void DisplayFullName(ResultBuffer rbArgs)
{
  //...
}

Вызов из-под LISP:

(displayfullname "First" "Last")

Получение значений, переданных в LISP-функцию

Используйте цикл foreach для перебора значений, возвращаемых в ResultBuffer функцией AutoLISP. ResultBuffer - это коллекция объектов TypedValue. Свойство TypeCode объекта TypedValue можно использовать для определения типа значения для каждого значения, переданного в функцию AutoLISP. Свойство Value используется для возврата значения объекта TypedValue. Поддерживаются следующие типы данных:

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

public class Loader
{
    [LispFunction("DisplayFullName")]
    public static void DisplayFullName(ResultBuffer rbArgs)
    {
        if (rbArgs != null)
        {
            string strVal1 = "";
            string strVal2 = "";
            int nCnt = 0;
            foreach (TypedValue rb in rbArgs)
            {
                if (rb.TypeCode == (int)Autodesk.AutoCAD.Runtime.LispDataType.Text)
                {
                    switch (nCnt)
                    {
                        case 0:
                            strVal1 = rb.Value.ToString();
                            break;
                        case 1:
                            strVal2 = rb.Value.ToString();
                            break;
                    }
                    nCnt = nCnt + 1;
                }
            }
            Application.DocumentManager.MdiActiveDocument.Editor.
               WriteMessage("\\nName: " + strVal1 + " " + strVal2);
        }
    }
}

Пример кода выше определяет LISP-функцию с именем DisplayFullName. В то время как в .NET-проекте метод принимает только одно значение (ResultBuffer), соответствующая функция в AutoLISP ожидает 2 строковых значения для получения правильного вывода в .NET (см. реализацию). Загрузите скомпилированную библиотеку классов в AutoCAD и затем обратитесь к команде из-под LISP:

(displayfullname "First" "Last")

Выводом в консоль будут следующие данные:

Name: First Last

Взаимодействие со средой AutoCAD

В этой главе описываются основы разработки приложений, которое выполняется в запущенном AutoCAD. В ней объясняются многие концепции управления и эффективной работы со средой AutoCAD.

Взаимодействие с окном приложения AutoCAD

Иногда исполняемому приложению бывает необходимо свернуть окно AutoCAD или запросить его состояние. Для этого используются методы статического класса Application. С их помощью можно изменять положение, размер и видимость окна приложения, также можно использовать свойство WindowState для получения и задания текущего состояния окна приложения.

Примечание: Следующие примеры требуют наличия в проекте ссылки на библиотеку PresentationCore (PresentationCore.dll). Воспользуйтесь диалоговым окном Add Reference и выберите PresentationCore на вкладке .NET (при использовании .NET Framework). При использовании .NET6 в свойствах проекта csproj внесите строку UseWPF = True. Некоторые примеры также требуют ссылки на библиотеки WindowsForms, добавьте в проект на .NET6+ UseWindowsForms = True.

  <PropertyGroup>
    <TargetFramework>net6.0-windows</TargetFramework>
    <UseWP>true</UseWPF>
    <UseWindowsForms>true</UseWindowsForms>
  </PropertyGroup>

Установка положения и размера окна приложения

Свойства Application.MainWindow.DeviceIndependentLocation, Application.MainWindow.DeviceIndependentSize в текущей версии .NET API не доступны для редактирования (в отличие от AutoCAD .NET API).

Разворачивание на полный экран и свертывание приложения

using Autodesk.AutoCAD.Windows;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Runtime;

[CommandMethod("MinMaxApplicationWindow")]
public static void MinMaxApplicationWindow()
{
    //Minimize the Application window
    Application.MainWindow.WindowState = Window.State.Minimized;
    System.Windows.Forms.MessageBox.Show("Minimized", "MinMax",
                System.Windows.Forms.MessageBoxButtons.OK,
                System.Windows.Forms.MessageBoxIcon.None,
                System.Windows.Forms.MessageBoxDefaultButton.Button1,
                System.Windows.Forms.MessageBoxOptions.ServiceNotification);
    //Maximize the Application window
    Application.MainWindow.WindowState = Window.State.Maximized;
    System.Windows.Forms.MessageBox.Show("Maximized", "MinMax");
}

Получение текущего состояния приложения

Пример ниже получает текущее состояние окна приложения и выводит в консоль AutoCAD информацию:

[CommandMethod("CurrentWindowState")]
public static void CurrentWindowState()
{
    System.Windows.Forms.MessageBox.Show("The application window is " +
                                            Application.MainWindow.WindowState.ToString(),
                                            "Window State");
}

Управление видимостью окна приложения

Пример ниже использует свойство Visible для установки приложения сперва невидимым (скрытым), а затем снова видимым.

[CommandMethod("HideWindowState")]
public static void HideWindowState()
{
    //Hide the Application window
    Application.MainWindow.Visible = false;
    System.Windows.Forms.MessageBox.Show("Invisible", "Show/Hide");
    //Show the Application window
    Application.MainWindow.Visible = true;
    System.Windows.Forms.MessageBox.Show("Visible", "Show/Hide");
}

Взаимодействие с окном чертежа

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

Управление положением и размером окна чертежа

Используйте свойство Window у экземпляра класса Document для редактирования его положения и размера. Окно документа, как и окно приложения, можно раскрыть или скрыть, изменяя свойство WindowState

Разворачивание на полный экран и свертывание окна документа

[CommandMethod("MinMaxDocumentWindow")]
public static void MinMaxDocumentWindow()
{
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    //Minimize the Document window
    acDoc.Window.WindowState = Window.State.Minimized;
    System.Windows.Forms.MessageBox.Show("Minimized" , "MinMax");
    //Maximize the Document window
    acDoc.Window.WindowState = Window.State.Maximized;
    System.Windows.Forms.MessageBox.Show("Maximized" , "MinMax");
}

Получение текущего состояния окна документа

[CommandMethod("CurrentDocWindowState")]
public static void CurrentDocWindowState()
{
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    System.Windows.Forms.MessageBox.Show("The document window is " +
    acDoc.Window.WindowState.ToString(), "Window State");
}

Масштабирование и панорамирование текущего вида

Вид - это заданные параметры масштабирования, положения и ориентации ПСК для отображения области чертежа. Изменяя высоту, ширину и точку вида соответствующим образом меняется отображение чертежа. Увеличивая или уменьшая размеры рамки вида вы меняете размер пространства, в котором отображается текущий чертеж. Панорамирование - это изменение положения вида путем редактирования центра вида. С видом связано понятие "Видового экрана". Это объект, размещаемый на листе, позволяющий отобразить объекты в модели (фактически, отобразить настроенный вид). Также видовым экраном является видимая область чертежа из пространства модели (или несколько таких областей, если они заданы через меню Вид - Видовые экраны модели - Конфигурация)

Управление текущим видом

Доступ к текущему виду видового экрана (ВЭ далее) в пространстве модели или листа осуществляется с помощью метода Editor.GetCurrentView. Метод GetCurrentView возвращает объект ViewTableRecord, у которого можно отредактировать масштабирование, положение и ориентацию вида. Для применения новых настроек, измененный объект ViewTableRecord необходимо задать текущему виду в активном ВЭ с помощью метода Editor.SetCurrentView. Для изменения доступны следующие настройки вида:

  • CenterPoint : Центр вида в координатах DCS (см. термины);

  • Height : Высота вида в координатах DCS (см. термины);

  • Target : Целевая точка перспективной проекции для данного ВЭ;

  • ViewDirection : вектор от точки Target до камеры вида в координатах DCS (см. термины);

  • ViewTwist : угол поворота в радианах для вида;

  • Width : Ширина вида в координатах DCS (см. термины); Увеличение высоты (Height) или ширины (Width) приводит к уменьшению масштаба отображения объектов; уменьшение :: к увеличению масштаба.

  • Функции API для управления текущим видом

    Пример ниже содержит общие функции, используемые в примерах далее. Функция Zoom принимает четыре параметра для выполнения масштабирования по заданной границе (точки pMin, pMax), точке центра вида чертежа, а также параметр масштабирования вида чертежа в заданном масштабном значении. Процедура Zoom ожидает, что все значения координат будут предоставлены в координатах WCS (см. термины). В коде содержится инструкция чтения свойства Database.TileMode (оно же значение системной переменной TILEMODE), указывающей какое пространство сейчас активно (модель = 1 (true), лист = 0 (false)). Также в коде используется значение переменной CVPORT, возвращающей идентификатор текущего ВЭ. Если её значение = 1, то это признак общего ВЭ (т.е. когда экран не разделен никаким образом на несколько экранов).

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Geometry;
 
static void Zoom(Point3d pMin, Point3d pMax, Point3d pCenter, double dFactor)
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    int nCurVport = System.Convert.ToInt32(Application.GetSystemVariable("CVPORT"));

    // Get the extents of the current space when no points 
    // or only a center point is provided
    // Check to see if Model space is current
    if (acCurDb.TileMode == true)
    {
        if (pMin.Equals(new Point3d()) == true && 
            pMax.Equals(new Point3d()) == true)
        {
            pMin = acCurDb.Extmin;
            pMax = acCurDb.Extmax;
        }
    }
    else
    {
        // Check to see if Paper space is current
        if (nCurVport == 1)
        {
            // Get the extents of Paper space
            if (pMin.Equals(new Point3d()) == true && 
                pMax.Equals(new Point3d()) == true)
            {
                pMin = acCurDb.Pextmin;
                pMax = acCurDb.Pextmax;
            }
        }
        else
        {
            // Get the extents of Model space
            if (pMin.Equals(new Point3d()) == true && 
                pMax.Equals(new Point3d()) == true)
            {
                pMin = acCurDb.Extmin;
                pMax = acCurDb.Extmax;
            }
        }
    }

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Get the current view
        using (ViewTableRecord acView = acDoc.Editor.GetCurrentView())
        {
            Extents3d eExtents;

            // Translate WCS coordinates to DCS
            Matrix3d matWCS2DCS;
            matWCS2DCS = Matrix3d.PlaneToWorld(acView.ViewDirection);
            matWCS2DCS = Matrix3d.Displacement(acView.Target - Point3d.Origin) * matWCS2DCS;
            matWCS2DCS = Matrix3d.Rotation(-acView.ViewTwist, 
                                            acView.ViewDirection, 
                                            acView.Target) * matWCS2DCS;

            // If a center point is specified, define the min and max 
            // point of the extents
            // for Center and Scale modes
            if (pCenter.DistanceTo(Point3d.Origin) != 0)
            {
                pMin = new Point3d(pCenter.X - (acView.Width / 2),
                                    pCenter.Y - (acView.Height / 2), 0);

                pMax = new Point3d((acView.Width / 2) + pCenter.X,
                                    (acView.Height / 2) + pCenter.Y, 0);
            }

            // Create an extents object using a line
            using (Line acLine = new Line(pMin, pMax))
            {
                eExtents = new Extents3d(acLine.Bounds.Value.MinPoint,
                                            acLine.Bounds.Value.MaxPoint);
            }

            // Calculate the ratio between the width and height of the current view
            double dViewRatio;
            dViewRatio = (acView.Width / acView.Height);

            // Tranform the extents of the view
            matWCS2DCS = matWCS2DCS.Inverse();
            eExtents.TransformBy(matWCS2DCS);

            double dWidth;
            double dHeight;
            Point2d pNewCentPt;

            // Check to see if a center point was provided (Center and Scale modes)
            if (pCenter.DistanceTo(Point3d.Origin) != 0)
            {
                dWidth = acView.Width;
                dHeight = acView.Height;

                if (dFactor == 0)
                {
                    pCenter = pCenter.TransformBy(matWCS2DCS);
                }

                pNewCentPt = new Point2d(pCenter.X, pCenter.Y);
            }
            else // Working in Window, Extents and Limits mode
            {
                // Calculate the new width and height of the current view
                dWidth = eExtents.MaxPoint.X - eExtents.MinPoint.X;
                dHeight = eExtents.MaxPoint.Y - eExtents.MinPoint.Y;

                // Get the center of the view
                pNewCentPt = new Point2d(((eExtents.MaxPoint.X + eExtents.MinPoint.X) * 0.5),
                                            ((eExtents.MaxPoint.Y + eExtents.MinPoint.Y) * 0.5));
            }

            // Check to see if the new width fits in current window
            if (dWidth > (dHeight * dViewRatio)) dHeight = dWidth / dViewRatio;

            // Resize and scale the view
            if (dFactor != 0)
            {
                acView.Height = dHeight * dFactor;
                acView.Width = dWidth * dFactor;
            }

            // Set the center of the view
            acView.CenterPoint = pNewCentPt;

            // Set the current view
            acDoc.Editor.SetCurrentView(acView);
        }

        // Commit the changes
        acTrans.Commit();
    }
}

Задание области экрана

Для определения области чертежа, которая должна отображаться на виде, необходимо знать габаритные точки необходимой области. По ним будут рассчитаны высота и ширина вида, координаты центральной точки вида. Ниже приведен код, устанавливающий вид для заданного пространства модели, ограниченного двумя точками (левый верхний угол и правый нижний угол). Процедуре Zoom передаются координаты (1.3,7.8,0) и (13.7,-2.6,0) в качестве первых двух аргументов, чтобы определить область для отображения. Центральная точка не нужна, поэтому процедуре передается экземпляр определения Point3d по умолчанию. Последний аргумент используется для масштабирования нового вида. Масштабирование вида можно использовать для создания "зазора" между отображаемой областью и краем окна рисования.

[CommandMethod("ZoomWindow")]
static public void ZoomWindow()
{
    // Zoom to a window boundary defined by 1.3,7.8 and 13.7,-2.6
    Point3d pMin = new Point3d(1.3, 7.8, 0);
    Point3d pMax = new Point3d(13.7, -2.6, 0);
    Zoom(pMin, pMax, new Point3d(), 1);
}

Масштабирование вида

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

  • Относительно пределов чертежа (limits);
  • Относительно текущего вида;
  • Относительно единиц длины на листе; Пример ниже показывает, как уменьшить текущий вид на 50%. Процедуре Zoom передаются 4 значения: первые 2 : это экземпляры определения Point3d по умолчанию (не используются), третье значение :: текущие координаты центра вида и четвертое значения :: масштабный коэффициент, используемый при определения размера вида.
[CommandMethod("ZoomScale")]
static public void ZoomScale()
{
    // Get the current document
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    // Get the current view
    using (ViewTableRecord acView = acDoc.Editor.GetCurrentView())
    {
        // Get the center of the current view
        Point3d pCenter = new Point3d(acView.CenterPoint.X,
                                      acView.CenterPoint.Y, 0);
        // Set the scale factor to use
        double dScale = 0.5;
        // Scale the view using the center of the current view
        Zoom(new Point3d(), new Point3d(), pCenter, 1 / dScale);
    }
}

Приближение к объектам

Для позиционирования вида на нужном пространстве чертежа необходимо задать пользовательское значение центральной точки. При изменении только центральной точки (свойство CentralPoint) и сохранении размера и масштаба вида, вид перемещается параллельно экрану (т.е. на величину сдвига без поворота и масштабирования графики). Код ниже перемещает центр вида в определенную точку модели. В функцию Zoom передаются 2 аргумента по умолчанию, координаты целевой точки и значение масштаба = 1, чтобы не трогать текущие настройки вида.

[CommandMethod("ZoomCenter")]
static public void ZoomCenter()
{
  // Center the view at 5,5,0
  Zoom(new Point3d(), new Point3d(), new Point3d(5, 5, 0), 1);
}

Отображение всего чертежа

Границы пространтв или значение лимитов чертежа также могут использоваться для определения зоны на виде

Расчет границ текущего пространства

Границы текущего пространства могут быть получены используя свойства у объекта класса Database:

  • Extmin и Extmax возвращают границы пространства модели;
  • Pextmin и Pextmax возвращают границы текущего листа (если текущее пространство = лист); Как только границы определены, можно рассчитать новые значения Ширины и Высоты вида. Новое значение ширины и высоты может быть определено используя код ниже:
dWidth = MaxPoint.X : MinPoint.X
dHeight = MaxPoint.Y : MinPoint.Y

Координаты центральной точки могут быть определены как:

dCenterX = (MaxPoint.X + MinPoint.X) * 0.5
dCenterY = (MaxPoint.Y + MinPoint.Y) * 0.5

Расчет пределов (limits) текущего пространства

Чтобы изменить отображение чертежа на основе пределов (limits) текущего пространства, используются свойства Limmin и Limmax , а также Plimmin и Plimmax объекта Database. После возвращения точек, определяющих границы текущего пространства, вы можете использовать ранее упомянутые формулы для расчета ширины, высоты и координат центральной точки нового вида. Код ниже позволяет установить параметры отображения вида для заданных границ и лимитов. Для случая границ используется только пользовательское значение масштаба, немного большее 1. Для лимитов используются свойства Database для установки минимальной и максимальной точек вида.

[CommandMethod("ZoomExtents")]
static public void ZoomExtents()
{
    // Zoom to the extents of the current space
    Zoom(new Point3d(), new Point3d(), new Point3d(), 1.01075);
}
[CommandMethod("ZoomLimits")]
static public void ZoomLimits()
{
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;
    // Zoom to the limits of Model space
    Zoom(new Point3d(acCurDb.Limmin.X, acCurDb.Limmin.Y, 0),
         new Point3d(acCurDb.Limmax.X, acCurDb.Limmax.Y, 0),
         new Point3d(), 1);
}

Именованные виды

Любой вид возможно сохранить под заданным именем (Именованный вид) и при необходимости их далее использовать. Созданные виды также можно удалить. Именованные виды хранятся в общей таблице видов (ViewTable) базы данных чертежа. Создаются новые виды при помощи вызова метода ViewTable.Add. При создании нового именованного вида ему автоматически назначается вид по умолчанию для пространства модели. Имя вида может содержать до 255 символов и содержать буквы, числа и следующие специальные символы: доллар-символ ($), тире (-), and нижнее подчеркивание (_). Удаление именованного вида (объекта ViewTableRecord) будет осуществляться через метод ViewTable.Erase.

Создание нового именованного вида

Код ниже создает новый именованный вид и делает его текущим:

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
 
[CommandMethod("CreateNamedView")]
public static void CreateNamedView()
{
    // Get the current database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the View table for read
        ViewTable acViewTbl;
        acViewTbl = acTrans.GetObject(acCurDb.ViewTableId,
                                        OpenMode.ForRead) as ViewTable;

        // Check to see if the named view 'View1' exists
        if (acViewTbl.Has("View1") == false)
        {
            // Open the View table for write
            acTrans.GetObject(acCurDb.ViewTableId, OpenMode.ForWrite);

            // Create a new View table record and name the view 'View1'
            using (ViewTableRecord acViewTblRec = new ViewTableRecord())
            {
                acViewTblRec.Name = "View1";

                // Add the new View table record to the View table and the transaction
                acViewTbl.Add(acViewTblRec);
                acTrans.AddNewlyCreatedDBObject(acViewTblRec, true);

                // Set 'View1' current
                acDoc.Editor.SetCurrentView(acViewTblRec);
            }

            // Commit the changes
            acTrans.Commit();
        }

        // Dispose of the transaction
    }
}

Удаление имеющегося именованного вида

Код ниже удаляет существующий (созданный в примере выше) именованный вид:

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
 
[CommandMethod("EraseNamedView")]
public static void EraseNamedView()
{
    // Get the current database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the View table for read
        ViewTable acViewTbl;
        acViewTbl = acTrans.GetObject(acCurDb.ViewTableId,
                                        OpenMode.ForRead) as ViewTable;

        // Check to see if the named view 'View1' exists
        if (acViewTbl.Has("View1") == true)
        {
            // Open the View table for write
            acTrans.GetObject(acCurDb.ViewTableId, OpenMode.ForWrite);

            // Get the named view
            ViewTableRecord acViewTblRec;
            acViewTblRec = acTrans.GetObject(acViewTbl["View1"],
                                                OpenMode.ForWrite) as ViewTableRecord;

            // Remove the named view from the View table
            acViewTblRec.Erase();

            // Commit the changes
            acTrans.Commit();
        }

        // Dispose of the transaction
    }
}

Режим нескольких видовых экранов

Обычно чертеж содержит один видовой экран, заполняющий всю область чертежа. Тем не менее при помощи стандартной команды Вид - Видовые экраны модели - Конфигурация можно установить другой режим нескольких видовых экранов. С каждым из разделенных ВЭ можно будет выполнять следующие действия:

  • Масштабировать, задавать настройки привязки, включать\отключать видимость сетки, задавать ПСК, а также использовать именованные виды в отдельных видовых экранах.
  • Переходить из одного видового экрана в другой при выполнении команд;
  • Присваивать имена отдельным видовым экранам для возможности их использования потом; Возможно отобразить видовые экраны в самых различных сценариях помимо базовых шаблонов в меню Вид : Видовые экраны модели : Конфигурация. Разделенные ВЭ будут храниться в таблице ViewportTable. Каждый из разделенных ВЭ представляет собой отдельную запись ViewportTableRecord и, в отличие от других записей таблицы, в ней может быть несколько записей ViewportTable с одним и тем же именем. Например, записи таблицы Viewport с именем "*Active" представляют плиточные (разделенные) видовые экраны, которые в данный момент отображаются на вкладке Model.

Определение и управление активным ВЭ

Активный ВЭ хранится в таблице Viewports (ViewportTable) как запись с именем "*Active", которая не является уникальным именем, поскольку все плиточные видовые экраны, отображаемые в данный момент в пространстве модели, имеют имя "*Active". Каждому отображаемому видовому экрану присваивается номер. Номер активного видового экрана можно получить следующим образом:

  • Используя значение системной переменной CVPORT;
  • Свойство Number у ViewportTableRecord (его можно получить используя свойство Editor.ActiveViewportId);

Примечание: свойство Number у ViewportTableRecord в nanoCAD .NET API не реализовано.

Получив активный ВЭ, можно управлять его параметрами отображения, активировать режимы видимости сетки, изменять размер самого ВЭ. Разделенные (плиточные) ВЭ определяются двумя угловыми точками: левым нижним и правым верхним углами; их значения хранятся соответственно в изменяемых свойствах LowerLeftCorner и UpperRightCorner соответственно. Одиночный ВЭ по умолчанию имеет LowerLeftCorner со значением (0,0) и UpperRightCorner = (1,1). Левый нижний угол окна чертежа обычно представляется точкой (0,0), а правый верхний :: точкой (1,1) независимо от числа разделенных ВЭ на пространстве модели. Когда отображено более одного ВЭ, левый нижний и правый верхний углы могут иметь отличные значения, но в одном видовом экране всегда нижний левый угол будет равен (0,0), а в другом : верхний правый угол равен (1,1). Для случая расположения сетки экранов 2х2 эти свойства будут определяться следующим образом:

По данному примеру:

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("CreateModelViewport")]
public static void CreateModelViewport()
{
    // Get the current database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Viewport table for read
        ViewportTable acVportTbl;
        acVportTbl = acTrans.GetObject(acCurDb.ViewportTableId,
                                        OpenMode.ForRead) as ViewportTable;

        // Check to see if the named view 'TEST_VIEWPORT' exists
        if (acVportTbl.Has("TEST_VIEWPORT") == false)
        {
            // Open the View table for write
            acTrans.GetObject(acCurDb.ViewportTableId, OpenMode.ForWrite);

            // Add the new viewport to the Viewport table and the transaction
            using (ViewportTableRecord acVportTblRecLwr = new ViewportTableRecord())
            {
                acVportTbl.Add(acVportTblRecLwr);
                acTrans.AddNewlyCreatedDBObject(acVportTblRecLwr, true);

                // Name the new viewport 'TEST_VIEWPORT' and assign it to be
                // the lower half of the drawing window
                acVportTblRecLwr.Name = "TEST_VIEWPORT";
                acVportTblRecLwr.LowerLeftCorner = new Point2d(0, 0);
                acVportTblRecLwr.UpperRightCorner = new Point2d(1, 0.5);

                // Add the new viewport to the Viewport table and the transaction
                using (ViewportTableRecord acVportTblRecUpr = new ViewportTableRecord())
                {
                    acVportTbl.Add(acVportTblRecUpr);
                    acTrans.AddNewlyCreatedDBObject(acVportTblRecUpr, true);

                    // Name the new viewport 'TEST_VIEWPORT' and assign it to be
                    // the upper half of the drawing window
                    acVportTblRecUpr.Name = "TEST_VIEWPORT";
                    acVportTblRecUpr.LowerLeftCorner = new Point2d(0, 0.5);
                    acVportTblRecUpr.UpperRightCorner = new Point2d(1, 1);

                    // To assign the new viewports as the active viewports, the 
                    // viewports named '*Active' need to be removed and recreated
                    // based on 'TEST_VIEWPORT'.

                    // Step through each object in the symbol table
                    foreach (ObjectId acObjId in acVportTbl)
                    {
                        // Open the object for read
                        ViewportTableRecord acVportTblRec;
                        acVportTblRec = acTrans.GetObject(acObjId,
                                                            OpenMode.ForRead) as ViewportTableRecord;

                        // See if it is one of the active viewports, and if so erase it
                        if (acVportTblRec.Name == "*Active")
                        {
                            acTrans.GetObject(acObjId, OpenMode.ForWrite);
                            acVportTblRec.Erase();
                        }
                    }

                    // Clone the new viewports as the active viewports
                    foreach (ObjectId acObjId in acVportTbl)
                    {
                        // Open the object for read
                        ViewportTableRecord acVportTblRec;
                        acVportTblRec = acTrans.GetObject(acObjId,
                                                            OpenMode.ForRead) as ViewportTableRecord;

                        // See if it is one of the active viewports, and if so erase it
                        if (acVportTblRec.Name == "TEST_VIEWPORT")
                        {
                            ViewportTableRecord acVportTblRecClone;
                            acVportTblRecClone = acVportTblRec.Clone() as ViewportTableRecord;

                            // Add the new viewport to the Viewport table and the transaction
                            acVportTbl.Add(acVportTblRecClone);
                            acVportTblRecClone.Name = "*Active";
                            acTrans.AddNewlyCreatedDBObject(acVportTblRecClone, true);
                        }
                    }

                    // Update the display with the new tiled viewports arrangement
                    acDoc.Editor.UpdateTiledViewportsFromDatabase();
                }
            }

            // Commit the changes
            acTrans.Commit();
        }

        // Dispose of the transaction
    }
}

Задание разделенного ВЭ текущим

Чтобы сделать видовой экран текущим, установите значение системной переменной CVPORT в виде номера целевого видового экрана, который вы хотите сделать текущим.

Вы можете перебирать существующие видовые экраны, чтобы найти определенный видовой экран. Для этого определите записи таблицы Viewport с именем «*Active» с помощью свойства Name. Пример ниже разделяет активный видовой экран на два горизонтальных окна. Затем он перебирает все разделенные видовые экраны на чертеже и отображает имя видового экрана, а также нижний левый и верхний правый угол для каждого видового экрана.

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("SplitAndIterateModelViewports")]
public static void SplitAndIterateModelViewports()
{
    // Get the current database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Viewport table for write
        ViewportTable acVportTbl;
        acVportTbl = acTrans.GetObject(acCurDb.ViewportTableId,
                                        OpenMode.ForWrite) as ViewportTable;

        // Open the active viewport for write
        ViewportTableRecord acVportTblRec;
        acVportTblRec = acTrans.GetObject(acDoc.Editor.ActiveViewportId,
                                            OpenMode.ForWrite) as ViewportTableRecord;

        using (ViewportTableRecord acVportTblRecNew = new ViewportTableRecord())
        {
            // Add the new viewport to the Viewport table and the transaction
            acVportTbl.Add(acVportTblRecNew);
            acTrans.AddNewlyCreatedDBObject(acVportTblRecNew, true);

            // Assign the name '*Active' to the new Viewport
            acVportTblRecNew.Name = "*Active";

            // Use the existing lower left corner for the new viewport
            acVportTblRecNew.LowerLeftCorner = acVportTblRec.LowerLeftCorner;

            // Get half the X of the existing upper corner
            acVportTblRecNew.UpperRightCorner = new Point2d(acVportTblRec.UpperRightCorner.X,
                                                            acVportTblRec.LowerLeftCorner.Y +
                                                            ((acVportTblRec.UpperRightCorner.Y -
                                                                acVportTblRec.LowerLeftCorner.Y) / 2));

            // Recalculate the corner of the active viewport
            acVportTblRec.LowerLeftCorner = new Point2d(acVportTblRec.LowerLeftCorner.X,
                                                        acVportTblRecNew.UpperRightCorner.Y);

            // Update the display with the new tiled viewports arrangement
            acDoc.Editor.UpdateTiledViewportsFromDatabase();

            // Step through each object in the symbol table
            foreach (ObjectId acObjId in acVportTbl)
            {
                // Open the object for read
                ViewportTableRecord acVportTblRecCur;
                acVportTblRecCur = acTrans.GetObject(acObjId,
                                                        OpenMode.ForRead) as ViewportTableRecord;

                if (acVportTblRecCur.Name == "*Active")
                {
                    Application.SetSystemVariable("CVPORT", acVportTblRecCur.Number);

                    Application.ShowAlertDialog("Viewport: " + acVportTblRecCur.Number +
                                                " is now active." +
                                                "\nLower left corner: " +
                                                acVportTblRecCur.LowerLeftCorner.X + ", " +
                                                acVportTblRecCur.LowerLeftCorner.Y +
                                                "\nUpper right corner: " +
                                                acVportTblRecCur.UpperRightCorner.X + ", " +
                                                acVportTblRecCur.UpperRightCorner.Y);
                }
            }
        }

        // Commit the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Регенерация геометрии чертежа

Многие действия, выполняемые с помощью AutoCAD .NET API, изменяют отображаемую в области чертежа информацию. Не все эти действия сразу же обновляют отображение чертежа. Это сделано для того, чтобы можно было внести несколько изменений в чертеж, не дожидаясь обновления отображения после каждого действия. Вместо этого вы можете объединить все действия и сделать один вызов для обновления графики по завершению операций. Методы API, которые обновляют дисплей, - это UpdateScreen (объекты Application и Editor) и Regen (объект Editor). Метод UpdateScreen действует для окна приложения или документа. Метод Regen регенерирует графические примитивы объекты в окне чертежа, а также пересчитывает экранные координаты и масштаб для всех объектов. Он также делает индексацию базу данных чертежа для оптимального отображения и выбора объектов.

[CommandMethod("DEMO_Regen")]
public static void Regen()
{
    // Redraw the drawing
    Application.UpdateScreen();
    Application.DocumentManager.MdiActiveDocument.Editor.UpdateScreen();
    // Regenerate the drawing
    Application.DocumentManager.MdiActiveDocument.Editor.Regen();
}

Операции открытия, сохранения и закрытия чертежей

Работа с файлами документов осуществляется через методы классов DocumentCollection, Document, и Database.

Создание и открытие чертежей

Для создания нового чертежа или открытия существующего используйте методы класса DocumentCollection (возвращается как свойство Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager). Метод Add создает новый чертеж из файла шаблона DWT, перегрузки методов Open предназначены для открытия существующего DWG-файла.

Создание нового чертежа

В примере ниже новый чертеж создается и становится активным. Если указанный шаблон в аргументе templateFileName не был найден, то чертеж будет создан на базе шаблона по умолчанию.

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
 
[CommandMethod("NewDrawing", CommandFlags.Session)]
public static void NewDrawing()
{
    // Specify the template to use, if the template is not found
    // the default settings are used.
    string strTemplatePath = "acad.dwt";

    DocumentCollection acDocMgr = Application.DocumentManager;
    Document acDoc = acDocMgr.Add(strTemplatePath);

    acDocMgr.MdiActiveDocument = acDoc;
}

Открытие существующего чертежа

Код в примере открывает существующий файл чертежа, проверяя перед этим, существует ли файл по указанному пути:

using System.IO;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
 
[CommandMethod("OpenDrawing", CommandFlags.Session)]
public static void OpenDrawing()
{
    string strFileName = "C:\\campus.dwg";
    DocumentCollection acDocMgr = Application.DocumentManager;

    if (File.Exists(strFileName))
    {
        acDocMgr.Open(strFileName, false);
    }
    else
    {
        acDocMgr.MdiActiveDocument.Editor.WriteMessage("File " + strFileName +
                                                        " does not exist.");
    }
}

Сохранение и закрытие чертежа

Для сохранения содержимого файла используйте метод Database.SaveAs. При использовании метода SaveAs можно указать новый путь для сохранения файла, а при флаге bBakAndRename = true соответствующие временные файлы BAK также будут переименованы для нового файлового пути. Определить, используется ли в базе данных чертежа имя по умолчанию можно, проверив значение системной переменной DWGTITLED. Если DWGTITLED равна 0, чертеж не был переименован пользователем и имеет имя по умолчанию.

Иногда возникает необходимость проверить, нет ли в чертеже несохраненных изменений. Делать это следует перед выходом из сеанса AutoCAD или перед началом работы над новым чертежом. Чтобы проверить, был ли изменен файл чертежа, необходимо проверить значение системной переменной DBMOD.

Закрытие чертежа

Методы Document.CloseAndSave и Document.CloseAndDiscard используются для закрытия открытого чертежа с сохранением изменений и без сохранения соответственно. Также имеется метод DocumentCollection.CloseAll, закрывающий все открытые чертежи в данной сессии AutoCAD.

Сохранение активного чертежа

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

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Runtime;
 
[CommandMethod("SaveActiveDrawing")]
public static void SaveActiveDrawing()
{
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    string strDWGName = acDoc.Name;
 
    object obj = Application.GetSystemVariable("DWGTITLED");
 
    // Check to see if the drawing has been named
    if (System.Convert.ToInt16(obj) == 0)
    {
        // If the drawing is using a default name (Drawing1, Drawing2, etc)
        // then provide a new name
        strDWGName = "c:\\MyDrawing.dwg";
    }
 
    // Save the active drawing
    acDoc.Database.SaveAs(strDWGName, true, DwgVersion.Current,
                          acDoc.Database.SecurityParameters);
}

Проверка, имеются ли не сохраненные изменения

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

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
 
[CommandMethod("DrawingSaved")]
public static void DrawingSaved()
{
    object obj = Application.GetSystemVariable("DBMOD");
 
    // Check the value of DBMOD, if 0 then the drawing has no unsaved changes
    if (System.Convert.ToInt16(obj) != 0)
    {
        if (System.Windows.Forms.MessageBox.Show("Do you wish to save this drawing?",
                                  "Save Drawing",
                                  System.Windows.Forms.MessageBoxButtons.YesNo,
                                  System.Windows.Forms.MessageBoxIcon.Question)
                                  == System.Windows.Forms.DialogResult.Yes)
        {
            Document acDoc = Application.DocumentManager.MdiActiveDocument;
            acDoc.Database.SaveAs(acDoc.Name, true, DwgVersion.Current,
                                  acDoc.Database.SecurityParameters);
        }
    }
}

Действия при отсутствии открытых документов

Приложение AutoCAD при открытии запускается с новым чертежом или с конкретным чертежом, открытым в нем. Тем не менее, можно закрыть все открытые документы в данной сессии. Если сделать это на стороне пользовательского интерфейса, то в окне приложения произойдут некоторые изменения: в частности, панель инструментов быстрого доступа и меню приложения будут иметь ограниченные возможности и отсутствовать командная строка. Чтобы отловить момент закрытия всех документов следует использовать событие DocumentDestroyed и свойство DocumentManager.Count. DocumentDestroyed срабатывает при закрытии открытого документа. Счетчик документов при закрытии последнего документа будет равен 1. Для определения количества открытых документов на момент срабатывания события DocumentDestroyed используйте свойство DocumentManager.Count. Доступными действиями при работе с приложением, когда отсутствуют какие-либо открытые документы будут:

  • Создание нового чертежа или открытие существующего;
  • Закрытие AutoCAD;

TODO: написать про Customize the application menu

Блокировка и разблокировка документов

Запросы на изменение объектов чертежа или получение доступа к приложению AutoCAD могут возникать от различных загруженных приложений. Для избегания конфликтов одновременного доступа к данным имеется возможность блокировки документа перед его изменением. Несмотря на то, что блокируется фактически база данных, термин блокировки связан с документом. Если этого не сделать, то при выполнении некоторых операций могут произойти ошибки синхронизации базы данных чертежа, что может привести к потере данных или появлению фатальной ошибки. Рекомендуется использовать блокировку документа во всех следующих случаях:

  • Взаимодействие с AutoCAD из-под модальных диалоговых окон;
  • Доступ к другому документу из-под текущего документа;
  • Использование COM API;
  • В командах с флагом CommandFlags.Session;

Например, при добавлении сущности в пространство модели или листа документа, отличного от текущего, целевой документ необходимо заблокировать. Для этого используется метод Document.LockDocument для документа, который вы хотите заблокировать. При вызове метода LockDocument возвращается объект DocumentLock. После того как вы закончили изменять заблокированный документ (вернее, его базу данных), вам нужно разблокировать его. Чтобы разблокировать документ, вызовите метод Dispose у объекта DocumentLock. Вы также можете использовать оператор using с объектом DocumentLock, когда оператор using завершится, база данных будет разблокирована.

Document acNewDoc;
using (DocumentLock acLckDoc = acNewDoc.LockDocument())
{ ... }

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

Блокировка БД чертежа перед вставкой объекта в модель

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

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("LockDoc", CommandFlags.Session)]
public static void LockDoc()
{
    // Create a new drawing
    DocumentCollection acDocMgr = Application.DocumentManager;
    Document acNewDoc = acDocMgr.Add("acad.dwt");
    Database acDbNewDoc = acNewDoc.Database;

    // Lock the new document
    using (DocumentLock acLckDoc = acNewDoc.LockDocument())
    {
        // Start a transaction in the new database
        using (Transaction acTrans = acDbNewDoc.TransactionManager.StartTransaction())
        {
            // Open the Block table for read
            BlockTable acBlkTbl;
            acBlkTbl = acTrans.GetObject(acDbNewDoc.BlockTableId,
                                            OpenMode.ForRead) as BlockTable;

            // Open the Block table record Model space for write
            BlockTableRecord acBlkTblRec;
            acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                            OpenMode.ForWrite) as BlockTableRecord;

            // Create a circle with a radius of 3 at 5,5
            using (Circle acCirc = new Circle())
            {
                acCirc.Center = new Point3d(5, 5, 0);
                acCirc.Radius = 3;

                // Add the new object to Model space and the transaction
                acBlkTblRec.AppendEntity(acCirc);
                acTrans.AddNewlyCreatedDBObject(acCirc, true);
            }

            // Save the new object to the database
            acTrans.Commit();
        }

        // Unlock the document
    }

    // Set the new document current
    acDocMgr.MdiActiveDocument = acNewDoc;
}

Установка параметров приложения

AutoCAD .NET API не предоставляет полного доступа к редактированию параметров приложения, как со стороны UI, доступных в окне _options. Доступ к этим параметрам осуществляется через ActiveX API для объекта, возвращаемого из свойства Preferences объекта Application.

AutoCAD.AcadPreferences pref = Application.Preferences as AutoCAD.AcadPreferences;

Получив COM-объект Preferences, можно получить доступ к девяти объектам, описываемых соответствующими интерфейсами, каждый из которых представляет собой вкладку диалогового окна Options. Эти объекты предоставляют доступ ко всем хранящимся в реестре параметрам диалогового окна «Параметры». С помощью свойств этих объектов можно настроить многие параметры AutoCAD. К этим объектам относятся (в скобках приведены свойства AutoCAD.AcadPreferences, по которым можно получить соответствующие объекты):

  • PreferencesDisplay (Display);
  • PreferencesDrafting (Drafting);
  • PreferencesFiles (Files);
  • PreferencesOpenSave (OpenSave);
  • PreferencesOutput (Output);
  • PreferencesProfiles (Profiles);
  • PreferencesSelection (Selection);
  • PreferencesSystem (System);
  • PreferencesUser (User);

Код ниже меняет цвет фона в пространстве модели на новый цвет

using Autodesk.AutoCAD.ApplicationServices;

[CommandMethod("EditColor")]
public static void EditColor()
{
    // Access the Preferences object
    AutoCAD.AcadApplication acApp = Application.AcadApplication as AutoCAD.AcadApplication;
    AutoCAD.AcadPreferences acPrefComObj = acApp.Preferences;
    //Edit GraphicsWinModelBackgrndColor
    AutoCAD.AcadPreferencesDisplay PreferencesDisplay = acPrefComObj.Display;
    PreferencesDisplay.GraphicsWinModelBackgrndColor = 182070;
}

Код ниже задает полный размер перекрестию курсора

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Interop;
 
[CommandMethod("PrefsSetCursor")]
public static void PrefsSetCursor()
{
    // This example sets the crosshairs for the drawing window
    // to full screen.
 
    // Access the Preferences object
    AcadPreferences acPrefComObj = (AcadPreferences)Application.Preferences;
 
    // Use the CursorSize property to set the size of the crosshairs
    acPrefComObj.Display.CursorSize = 100;
}

Работа с системными переменными

Доступ к чтению и записи системных переменных осуществляется через методы статического класса Autodesk.AutoCAD.ApplicationServices.Application:

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;

[CommandMethod("TestSysVars")]
public static void TestSysVars()
{
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Application.SetSystemVariable("ANNOTATIVEDWG", 0);
    acDoc.Editor.WriteMessage("Current ANNOTATIVEDWG = " + Application.GetSystemVariable("ANNOTATIVEDWG"));
    Application.SetSystemVariable("ANNOTATIVEDWG", 1);
    aDoc.Editor.WriteMessage("New ANNOTATIVEDWG = " + Application.GetSystemVariable("ANNOTATIVEDWG"));
}

Внутренние расчетные процедуры

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

Настройки объектной привязки и ориентации опорной сетки

Опорная сеть (GRID) - это вспомогательный инструмент для визуального измерения длины объектов (в AutoCAD включается клавишами F7 или Ctrl+G или командой GRID). При помощи API возможно настроить шаг сетки, шаг и тип привязки (ортогональная, изометрическая), После изменения настроек привязки и сетки для активного видового экрана следует использовать метод Editor.UpdateTiledViewportsFromDatabase для обновления отображения текущей области рисования.

Примечание: Настройки привязки и сетки не влияют на точки, указанные через .NET API, но влияют на точки, указанные в области чертежа пользователем, если ему предлагается ввести данные с помощью таких методов, как GetPoint или GetEntity.

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

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

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Geometry;
 
[CommandMethod("ChangeGridAndSnap")]
public static void ChangeGridAndSnap()
{
  // Get the current database
  Document acDoc = Application.DocumentManager.MdiActiveDocument;
  Database acCurDb = acDoc.Database;
 
  // Start a transaction
  using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
  {
      // Open the active viewport
      ViewportTableRecord acVportTblRec;
      acVportTblRec = acTrans.GetObject(acDoc.Editor.ActiveViewportId,
                                        OpenMode.ForWrite) as ViewportTableRecord;
 
      // Turn on the grid for the active viewport
      acVportTblRec.GridEnabled = true;
 
      // Adjust the spacing of the grid to 1, 1
      acVportTblRec.GridIncrements = new Point2d(1, 1);
 
      // Turn on the snap mode for the active viewport
      acVportTblRec.SnapEnabled = true;
 
      // Adjust the snap spacing to 0.5, 0.5
      acVportTblRec.SnapIncrements = new Point2d(0.5, 0.5);
 
      // Change the snap base point to 1, 1
      acVportTblRec.SnapBase = new Point2d(1, 1);
 
      // Change the snap rotation angle to 30 degrees (0.524 radians)
      acVportTblRec.SnapAngle = 0.524;
 
      // Update the display of the tiled viewport
      acDoc.Editor.UpdateTiledViewportsFromDatabase();
 
      // Commit the changes and dispose of the transaction
      acTrans.Commit();
  }
}

Использование ОРТО-режима

При рисовании линий или перемещении объектов можно использовать режим Ortho, чтобы ограничить курсор горизонтальной или вертикальной осью. Ортогональное выравнивание зависит от текущего угла привязки и настроек ПСК. Режим Ortho работает с действиями, требующими указания второй точки, например, при использовании методов GetDistance или GetAngle. Режим Ortho можно использовать не только для установки вертикального или горизонтального выравнивания, но и для обеспечивания параллельности линий или создания новых линий на смещении от данной. С режимом Ortho рисование примитивов значительно ускоряется, особенно если требуется их перпендикулярность. Следующий код включает режим Ortho. В отличие от настроек сетки и привязки, информация о режиме Ortho хранится в базе данных чертежа, а не в активном видовом экране, поэтому специальных действий по обновлению выполнять не требуется.

using Autodesk.AutoCAD.ApplicationServices;

[CommandMethod("TurnOnOrthoMode")]
public static void TurnOnOrthoMode()
{
    Application.DocumentManager.MdiActiveDocument.Database.Orthomode = true;
}
[CommandMethod("TurnOffOrthoMode")]
public static void TurnOffOrthoMode()
{
    Application.DocumentManager.MdiActiveDocument.Database.Orthomode = false;
}

Расчет точек и значений

Методы класса Editor, а также классов из пространств имён Autodesk.AutoCAD.Geometry, Autodesk.AutoCAD.Runtime позволяют быстро работать с выборкой точек из чертежа и/или производить некоторые математические вычисления. Среди них:

  • Получение расстояния между двумя плоскими или трехмерными точками через методы GetDistanceTo или DistanceTo;
  • Получение угла относительно оси X используя 2 плоские точки и метод GetVectorTo со свойством Angle для возвращаемого значения;
  • Преобразование значения угла из строкового в число double с методом StringToAngle;
  • Преобразование значения угла из числа double в строку с методом AngleToString;
  • Преобразование расстояния из строки в число double с помощью метода StringToDistance;
  • Вычисление расстояния между двумя точками, введенными пользователем, с помощью метода GetDistance;

Примечание: AutoCAD .NET API не содержит методов для расчета точки для заданных удаления и углу поворота (полярные координаты) и для преобразования координат между разными ПСК. Для этого используйте ActiveX API: методы Utility.PolarPoint и Utility.TranslateCoordinates, либо их численные реализации на стороне .NET (см. второй пример ниже).

Получение угла относительно оси X

В примере ниже идет расчет вектора между двумя точками и определение угла относительно оси X

[CommandMethod("AngleFromXAxis")]
public static void AngleFromXAxis()
{
    Point2d pt1 = new Point2d(2, 5);
    Point2d pt2 = new Point2d(5, 2);
    Application.ShowAlertDialog("Angle from XAxis: " +
                                pt1.GetVectorTo(pt2).Angle.ToString());
}

Определение полярных координат точки

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

using System;

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Geometry;

using Autodesk.AutoCAD.Interop;

static Point2d PolarPoints(Point2d pPt, double dAng, double dDist)
{
    return new Point2d(pPt.X + dDist * Math.Cos(dAng),
                       pPt.Y + dDist * Math.Sin(dAng));
}
static Point3d PolarPoints(Point3d pPt, double dAng, double dDist)
{
    return new Point3d(pPt.X + dDist * Math.Cos(dAng),
                       pPt.Y + dDist * Math.Sin(dAng),
                       pPt.Z);
}
//Реализация через расчетные методы
[CommandMethod("PolarPoints")]
public static void PolarPoints()
{
    Point2d pt1 = PolarPoints(new Point2d(5, 2), 0.785398, 12);
    Application.ShowAlertDialog("\\nPolarPoint: " +
                                "\\nX = " + pt1.X +
                                "\\nY = " + pt1.Y);
    Point3d pt2 = PolarPoints(new Point3d(5, 2, 0), 0.785398, 12);
    Application.ShowAlertDialog("\\nPolarPoint: " +
                                "\\nX = " + pt2.X +
                                "\\nY = " + pt2.Y +
                                "\\nZ = " + pt2.Z);
}
//Реализация через ActiveX
[CommandMethod("PolarPoints2")]
public static void PolarPoints2()
{
    double[] basePnt = new double[] { 5, 2, 0 };
    double angle = 0.785398;
    double distance = 12;
    Document doc = Application.DocumentManager.MdiActiveDocument;
    AcadDocument docCOM = doc.GetAcadDocument() as AcadDocument;
    var polarPnt = (double[])docCOM.Utility.PolarPoint(basePnt, angle, distance);
    Application.ShowAlertDialog("\\nPolarPoint: " +
                                "\\nX = " + polarPnt[0] +
                                "\\nY = " + polarPnt[1] +
                                "\\nZ = " + polarPnt[2]);
}

Вычисление расстояния между двумя точками по методу GetDistance

[CommandMethod("GetDistanceBetweenTwoPoints")]
public static void GetDistanceBetweenTwoPoints()
{
  Document acDoc = Application.DocumentManager.MdiActiveDocument;
  PromptDoubleResult pDblRes;
  pDblRes = acDoc.Editor.GetDistance("\\nPick two points: ");
  Application.ShowAlertDialog("\\nDistance between points: " +
                              pDblRes.Value.ToString());
}

Расчет площадей

С помощью свойства Area можно определить площадь дуги, окружности, эллипса, полилинии (будь она замкнутой), региона (Region), штриховки, плоско-замкнутого сплайна или любого другого объекта, наследующего класс Curve. Если вам нужно вычислить общую площадь нескольких объектов, вы можете суммировать получаемые значения для отдельных объектов или использовать метод Boolean для объединения нескольких объектов в один. Вычисляемая площадь зависит от типа объекта. Если площадь считается для фигуры, состоящей из заданных Пользователем точек, то можно создать в памяти временный объект, например, полилинию и посчитать её площадь. После выполнения расчета временный объект можно будет удалить. Ниже описывается подробно такой механизм действий:

  • Используйте метод GetPoint в цикле для получения точек от пользователя;
  • Создайте определение полилинии (Polyline) из точек, указанных пользователем. Для этого создайте новый объект Polyline, затем укажите количество вершин и точки, в которых они должны находиться;
  • Используйте свойство Area, чтобы получить площадь созданной полилинии;
  • Удалите полилинию с помощью метода Dispose.

В примере ниже пользователю предлагается ввести 5 точек. По введенным точкам создается полилиния, далее она замыкается, считается её площадь и вводится в диалоговом окне. Так как полилиния не будет записана в пространство блока, необходимо её удалить до завершения команды.

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;

[CommandMethod("CalculateDefinedArea")]
public static void CalculateDefinedArea()
{
    // Prompt the user for 5 points
    Document acDoc = Application.DocumentManager.MdiActiveDocument;

    PromptPointResult pPtRes;
    Point2dCollection colPt = new Point2dCollection();
    PromptPointOptions pPtOpts = new PromptPointOptions("");

    // Prompt for the first point
    pPtOpts.Message = "\nSpecify first point: ";
    pPtRes = acDoc.Editor.GetPoint(pPtOpts);
    colPt.Add(new Point2d(pPtRes.Value.X, pPtRes.Value.Y));

    // Exit if the user presses ESC or cancels the command
    if (pPtRes.Status == PromptStatus.Cancel) return;

    int nCounter = 1;

    while (nCounter <= 4)
    {
        // Prompt for the next points
        switch(nCounter)
        {
            case 1:
                pPtOpts.Message = "\nSpecify second point: ";
                break;
            case 2:
                pPtOpts.Message = "\nSpecify third point: ";
                break;
            case 3:
                pPtOpts.Message = "\nSpecify fourth point: ";
                break;
            case 4:
                pPtOpts.Message = "\nSpecify fifth point: ";
                break;
        }

        // Use the previous point as the base point
        pPtOpts.UseBasePoint = true;
        pPtOpts.BasePoint = pPtRes.Value;

        pPtRes = acDoc.Editor.GetPoint(pPtOpts);
        colPt.Add(new Point2d(pPtRes.Value.X, pPtRes.Value.Y));

        if (pPtRes.Status == PromptStatus.Cancel) return;

        // Increment the counter
        nCounter = nCounter + 1;
    }

    // Create a polyline with 5 points
    using (Polyline acPoly = new Polyline())
    {
        acPoly.AddVertexAt(0, colPt[0], 0, 0, 0);
        acPoly.AddVertexAt(1, colPt[1], 0, 0, 0);
        acPoly.AddVertexAt(2, colPt[2], 0, 0, 0);
        acPoly.AddVertexAt(3, colPt[3], 0, 0, 0);
        acPoly.AddVertexAt(4, colPt[4], 0, 0, 0);

        // Close the polyline
        acPoly.Closed = true;

        // Query the area of the polyline
        Application.ShowAlertDialog("Area of polyline: " +
                                    acPoly.Area.ToString());

        // Dispose of the polyline
    }
}

Пользовательский ввод данных

Класс Editor содержит методы пользовательского ввода данных. Методы ввода отображают подсказку в командной строке AutoCAD и/или во всплывающем окне и позволяют вводить данные различных типов, включая непосредственно рисование временной графики в области чертежа и выбор конкретных объектов. В случае, если логика приложения требует вводить много различных данных, целесообразно реализовать ввод численно\строковых данных через отдельное окно\форму\виджет, а не с помощью методов класса Editor.

Большинство методов пользовательского ввода имеют 2 перегрузки -- одна из них принимает на вход строку-необязательную подсказку, выводимую в командой строке; другая перегрузка принимает на вход объект класса Prompt***Options, задающий некоторые настройки ввода данных (наименование класса различно для соответствующих типов данных -- например, PromptStringOptions, PromptSelectionOptions, PromptKeywordOptions и пр.). Все методы ввода возвращают значение, соответствующее типу запрашиваемого объекта. Например, метод GetString возвращает PromptResult, который позволяет определить статус метода GetString и получить строку, введенную пользователем. Каждый из методов пользовательского ввода имеет определенное возвращаемое значение.

Чтобы текстовое сообщение отображалось на отдельных строках используйте символ перевода каретки на новую строку «\n».

Запрос строки

Метод GetString запрашивает у пользователя строку в командной строке. Объект PromptStringOptions позволяет управлять вводом и отображением сообщения подсказки. Свойство AllowSpaces объекта PromptStringOptions определяет, разрешены или нет пробелы во вводимой Пользователем строке. Если установлено значение false, нажатие клавиши пробела завершает ввод. Пример ниже демонстрирует использование данного метода; требуется, чтобы ввод данных пользователем был завершен нажатием клавиши Enter (в строке ввода допускаются пробелы). Введенная строка отобразится в окне сообщения.

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
 
[CommandMethod("GetStringFromUser")]
public static void GetStringFromUser()
{
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
 
    PromptStringOptions pStrOpts = new PromptStringOptions("\nEnter your name: ");
    pStrOpts.AllowSpaces = true;
    PromptResult pStrRes = acDoc.Editor.GetString(pStrOpts);
 
    Application.ShowAlertDialog("The name entered was: " +
                                pStrRes.StringResult);
}

Запрос точки

Метод GetPoint позволяет указать точку в пространстве чертежа. Настройки PromptPointOptions позволяют контролировать вводимое значение. Свойства UseBasePointи BasePoint определяют, будет ли строится пунктирная линия от UseBasePoint к BasePoint. Свойство Keywords позволяет определить ключевые слова (см. следующую статью "Запрос ключевых слов", которые можно вводить в командной строке в дополнение к указанию точки.

Пример ниже показывает, как у Пользователя запрашиваются 2 точки, затем по ним рисуется отрезок

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;

[CommandMethod("GetPointsFromUser")]
public static void GetPointsFromUser()
{
    // Get the current database and start the Transaction Manager
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    PromptPointResult pPtRes;
    PromptPointOptions pPtOpts = new PromptPointOptions("");

    // Prompt for the start point
    pPtOpts.Message = "\nEnter the start point of the line: ";
    pPtRes = acDoc.Editor.GetPoint(pPtOpts);
    Point3d ptStart = pPtRes.Value;

    // Exit if the user presses ESC or cancels the command
    if (pPtRes.Status == PromptStatus.Cancel) return;

    // Prompt for the end point
    pPtOpts.Message = "\nEnter the end point of the line: ";
    pPtOpts.UseBasePoint = true;
    pPtOpts.BasePoint = ptStart;
    pPtRes = acDoc.Editor.GetPoint(pPtOpts);
    Point3d ptEnd = pPtRes.Value;

    if (pPtRes.Status == PromptStatus.Cancel) return;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        BlockTable acBlkTbl;
        BlockTableRecord acBlkTblRec;

        // Open Model space for write
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Define the new line
        using (Line acLine = new Line(ptStart, ptEnd))
        {
            // Add the line to the drawing
            acBlkTblRec.AppendEntity(acLine);
            acTrans.AddNewlyCreatedDBObject(acLine, true);
        }

        // Zoom to the extents or limits of the drawing
        acDoc.SendStringToExecute("._zoom _all ", true, false, false);

        // Commit the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Запрос ключевых слов

Метод GetKeywords запрашивает у пользователя ввод ключевого слова в командной строке. Ключевым называется слово из предопределенного списка (заданного в свойстве Keywords объекта класса PromptKeywordOptions).

Примечание: Символ подчеркивания («_») относится к служебным символам и не может использоваться в качестве ключевого слова или его части. В примере ниже запрашивается выбор ключевого слова Пользователем с запретом пропуска выбора (AllowNone = false), то есть нажатия Enter. Свойство Keywords используется для задания перечня ключевых слов.

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
 
[CommandMethod("GetKeywordFromUser")]
public static void GetKeywordFromUser()
{
    Document acDoc = Application.DocumentManager.MdiActiveDocument;

    PromptKeywordOptions pKeyOpts = new PromptKeywordOptions("");
    pKeyOpts.Message = "\nEnter an option ";
    pKeyOpts.Keywords.Add("Line");
    pKeyOpts.Keywords.Add("Circle");
    pKeyOpts.Keywords.Add("Arc");
    pKeyOpts.AllowNone = false;

    PromptResult pKeyRes = acDoc.Editor.GetKeywords(pKeyOpts);

    Application.ShowAlertDialog("Entered keyword: " +
                                pKeyRes.StringResult);
}

Удобнее использовать предопределенное значение ключевого слова (на случай, если пользователь нажмет на Enter без выбора значения) в этом случае рекомендуется в подсказке к методу, отображаемой в командной строке, указывать, какое из ключевых слов будет считаться по умолчанию. Более удобной для пользователя является подсказка с ключевым словом, которая предоставляет значение по умолчанию, если пользователь нажимает Enter (ввод NULL). Обратите внимание на незначительные изменения в следующем примере. Он отличается от предыдущего только наличием заданного свойство Keywords.Default и AllowNone = false:

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
 
[CommandMethod("GetKeywordFromUser2")]
public static void GetKeywordFromUser2()
{
    Document acDoc = Application.DocumentManager.MdiActiveDocument;

    PromptKeywordOptions pKeyOpts = new PromptKeywordOptions("");
    pKeyOpts.Message = "\nEnter an option ";
    pKeyOpts.Keywords.Add("Line");
    pKeyOpts.Keywords.Add("Circle");
    pKeyOpts.Keywords.Add("Arc");
    pKeyOpts.Keywords.Default = "Arc";
    pKeyOpts.AllowNone = true;

    PromptResult pKeyRes = acDoc.Editor.GetKeywords(pKeyOpts);

    Application.ShowAlertDialog("Entered keyword: " +
                                pKeyRes.StringResult);
}

Контроль ввода данных

При запросе данных от Пользователя необходимо ограничить тип вводимой им информации, чтобы получить корректный ответ. В некоторых методах ввода можно получать как конкретное значение в зависимости от типа используемого метода, так и ключевое слово (используя свойство Keywords настроек ввода данных). Например, можно использовать метод GetPoint, чтобы пользователь указал точку или ответил ключевым словом. Именно так работают такие команды, как LINE, CIRCLE и PLINE.

Пример ниже содержит инструкцию ввода численных данных от пользователя с помощью метода GetInteger, где может быть введено как число, так и выбрано ключевое слово из числа предопределенных. В AutoCAD при вызове этого метода командная строка будет выглядеть "Enter the size or [Big/Small/Regular] <Regular>:"

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
 
[CommandMethod("GetIntegerOrKeywordFromUser")]
public static void GetIntegerOrKeywordFromUser()
{
    Document acDoc = Application.DocumentManager.MdiActiveDocument;

    PromptIntegerOptions pIntOpts = new PromptIntegerOptions("");
    pIntOpts.Message = "\nEnter the size or ";

    // Restrict input to positive and non-negative values
    pIntOpts.AllowZero = false;
    pIntOpts.AllowNegative = false;

    // Define the valid keywords and allow Enter
    pIntOpts.Keywords.Add("Big");
    pIntOpts.Keywords.Add("Small");
    pIntOpts.Keywords.Add("Regular");
    pIntOpts.Keywords.Default = "Regular";
    pIntOpts.AllowNone = true;

    // Get the value entered by the user
    PromptIntegerResult pIntRes = acDoc.Editor.GetInteger(pIntOpts);

    if (pIntRes.Status == PromptStatus.Keyword)
    {
        Application.ShowAlertDialog("Entered keyword: " +
                                    pIntRes.StringResult);
    }
    else
    {
        Application.ShowAlertDialog("Entered value: " +
                                    pIntRes.Value.ToString());
    }
}

Получение контура

Отдельного внимания заслуживает метод TraceBoundary класса Editor, позволяющий получить полилинию, внутри некоторого контура образованного одним или несколькими объектами. Метод возвращает набор полилиний в виде DBObjectCollection, объекты которых необходимо самостоятельно добавить в модель. Пример вызова данного метода приведен в коде ниже

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

[CommandMethod("InitBoundary")]
public void InitBoundary()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;
    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;
        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;
        var point = acDoc.Editor.GetPoint("Укажите точку внутри контура");
        if (point.Status != Autodesk.AutoCAD.EditorInput.PromptStatus.OK) return;
        var plines = acDoc.Editor.TraceBoundary(point.Value, false);
        if (plines == null || plines.Count \< 1) return;
        foreach (DBObject createdPline in plines)
        {
            Polyline? boundaryAsPline = createdPline as Polyline;
            if (boundaryAsPline == null) continue;
            acBlkTblRec.AppendEntity(boundaryAsPline);
            acTrans.AddNewlyCreatedDBObject(boundaryAsPline, true);
        }
        acTrans.Commit();
    }
}

Запрос одного объекта

Для того, чтобы попросить Пользователя выбрать объект в чертеже имеется метод Editor.GetEntity с несколькими перегрузками. Он возвращает экземпляр класса PromptEntityResult, свойство которого ObjectId возвращает идентификатор выбранного объекта.

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

Добавление фильтра по типу осуществляется с помощью метода AddAllowedClass. Можно задавать фильтр как строго для данного объекта, так и для всех типов, наследующих данный класс. К примеру, если AddAllowedClass(typeof(Curve), false), то выберутся все полилинии, отрезки и иные объекты, описываемые классами, производными от Curve, а если второй аргумент будет true, то в выбор не попадут никакие объекты, так как класс Curve является абстрактным.

В примере ниже задается фильтр на выбор полилинии и выводится её длина

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;

[CommandMethod("SelectPolyline")]
public void SelectPolyline()
{
    Document doc = Application.DocumentManager.MdiActiveDocument;
    PromptEntityOptions selOpts = new PromptEntityOptions("Select polyline");
    selOpts.SetRejectMessage("Object is not polyline");
    selOpts.AddAllowedClass(typeof(Polyline), true);

    PromptEntityResult selResult = doc.Editor.GetEntity(selOpts);
    if (selResult.Status != PromptStatus.OK) return;
    using (Polyline plineObj = selResult.ObjectId.Open(OpenMode.ForRead, true) as Polyline)
    {
        doc.Editor.WriteMessage($"\nPolyline's length: {plineObj.Length}\n");
    }
}

Примечание: в AutoCAD .NET API при использовании AddAllowedClass важно также задать RejectMessage, иначе при использовании метода будет ошибка, что это свойство не задано. В nanoCAD, например, это не обязательно.

Запрос нескольких объектов

Выбор нескольких объектов осуществляется с помощью метода Editor.GetSelection с перегрузкой для SelectionFilter.

SelectionFilter задается с помощью массива TypedValue, как и ResultBuffer. Если необходимо выбрать несколько типов, то их надо задавать либо в 1 строку для DxfCode.Start с перечислением запятыми, либо с помощью логических операторов. Выбор для нескольких условий см. в статье про SelectionFilter.

Для быстрого перехода от типа объекта (typeof(Polyline)) к соответствующему ему DxfName для задания в фильтре TypedValue) можно воспользоваться следующим приведением:

Type t;
string dxfName = Autodesk.AutoCAD.Runtime.RXObject.GetClass(t).DxfName;

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

using System.Linq;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;

[CommandMethod("SelectPolylines")]
public void SelectPolylines()
{
    Document doc = Application.DocumentManager.MdiActiveDocument;
    Type[] typesToSelect = new Type[]
    {
                typeof(Polyline), typeof(Circle)
    };

    string[] entNamesArr = typesToSelect.Select(t => RXObject.GetClass(t).DxfName).Distinct().ToArray();
    TypedValue[] tmpFilterArgs = new TypedValue[]
    {
                new TypedValue((int)DxfCode.Start, string.Join(",", entNamesArr))
    };

    SelectionFilter filter = new SelectionFilter(tmpFilterArgs.ToArray());
    PromptSelectionOptions settings = new PromptSelectionOptions()
    {
        MessageForAdding = "Select polylines and circles"
    };
    PromptSelectionResult result = doc.Editor.GetSelection(settings, filter);
    if (result.Status != PromptStatus.OK) return;
    doc.Editor.WriteMessage($"\nThere were selected: {result.Value.Count}" + " entities\n");
}

Доступ к командной строке

Возможно отправлять команды непосредственно в командную строку используя метод SendStringToExecute (он отправляет на исполнение по одной строке). Передаваемая методу строка обязательно должна содержать аргументы (командной строки), перечисленные в порядке, ожидаемом последовательностью ввода данных со стороны выполняемой команды. Пустой пробел или ASCII-эквивалент возврата каретки в строке эквивалентен нажатию Enter на клавиатуре. В отличие от среды AutoLISP, вызов метода SendStringToExecute без аргумента недопустим. Команды, выполняемые с помощью SendStringToExecute, являются асинхронными и не вызываются до тех пор, пока команда .NET не завершится. Если вам нужно выполнить команду немедленно (синхронно), вы должны:

  • Использовать метод SendCommand (ActiveX API, метод у AcadDocument);
  • Вызвать нативный метод acedCommand или acedCmd из ObjectARX для команд из библиотек .NET или ObjectARX;
  • Вызвать нативный метод acedInvoke из ObjectARX для команд из AutoLISP;

В примере ниже рисуется окружность с центром (2, 2, 0) и радиусом "4". Затем чертеж центрируется на всей видимой геометрии. Обратите внимание, что в конце строки есть пробел, который означает завершающий Enter для начала выполнения команды.

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Runtime;
 
[CommandMethod("SendACommandToAutoCAD")]
public static void SendACommandToAutoCAD()
{
  Document acDoc = Application.DocumentManager.MdiActiveDocument;
 
  // Draws a circle and zooms to the extents or 
  // limits of the drawing
  acDoc.SendStringToExecute("._circle 2,2,0 4 ", true, false, false);
  acDoc.SendStringToExecute("._zoom _all ", true, false, false);
}

Примечание: Обратите внимание, что запись команды будет отличаться от nanoCAD.

Расширение интерфейса AutoCAD

В дополнение к стандартным окнам и диалогам AutoCAD, через API можно реализовать создание различных окон для ввода и отображения каких-либо данных. Классы из пространства имён Autodesk.Windows позволяют получать доступ к некоторым стандартным окнам для выбор слоя, типа линии и т.д. Эти классы предоставляют метод ShowDialog, который отображает форму. При использовании этих стандартных классов AutoCAD автоматически задает им размер и положение на экране.

Пользовательские диалоговые окна могут быть созданы на основе System.Windows.Forms (Windows Forms) или System.Windows.Window (WPF). Несмотря на возможность открытия форм с помощью метода ShowDialog им не рекомендуется пользоваться, так как это может привести к неожиданному поведению. Вместо этого следует использовать специальные методы у статического класса Application: ShowModalDialog или ShowModelessDialog для Windows Forms и ShowModalWindow или ShowModelessWindow для WPF. Они открывают окна в специальном модальном режиме. При желании создать пользовательскую палитру, содержащую форму можно воспользоваться следующим минималистичным примером. Добавление форм на основе WPF осуществляется через метод AddVisual; форм на основе WinForms -- через метод Add.

using System;
using System.Drawing;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;

public class ObjectsAttributes_Palette
{
    private static Guid ps_Attrs_id = Guid.Parse("{750d30d1-c5fc-4ae4-99f1-6cc59417d33b}");
    static Autodesk.AutoCAD.Windows.PaletteSet? ps_Attrs;
    public static void CreatePalette()
    {
        if (ps_Attrs == null)
        {
            //use constructor with Guid so that we can save/load user data
            Autodesk.AutoCAD.Windows.PaletteSet
            ps_Attrs = new Autodesk.AutoCAD.Windows.PaletteSet("ObjectProps", ps_Attrs_id);
            ps_Attrs.MinimumSize = new Size(241, 300);
            ps_Attrs.Size = new Size(241, 300);
            ps_Attrs.AddVisual("WPF-форма", new PropertyPalette_WPF());
            ps_Attrs.Add("WinForms-форма", new PropertyPalette_WinForms())
            }
        ps_Attrs.Visible = true;
    }
    [CommandMethod("ShowPalette")]
    public static void ShowPalette()
    {
        CreatePalette();
    }
}

Примечания для nanoCAD

Возможности расширить интерфейс nanoCAD за счет добавления своих меню и ленты при помощи .NET API нет; эти настройки задаются только с помощью CFG и CUIX-файлов (для них имеются различные пользовательские реализации, например, https://github.com/doctorRaz/MenuFilesGen, https://github.com/GeorgGrebenyuk/ncad_UI_creator);

При разработке под nanoCAD на WPF рекомендуется использовать промежуточное представление System.Windows.Forms.Integration.ElementHost, поскольку при попытке добавления контрола WPF стандартной командой AddVisual он не будет полностью отрисован. Дополняя пример выше, для вставки WPF-контрола код будет выглядеть как:

var hostView = new ElementHost
{
    AutoSize = false,
    Dock = System.Windows.Forms.DockStyle.Fill,
    Child = new AnyWPFcontrol()
};
palset.Add("WPF-control", hostView);

Для AutoCAD это изменение не повлечет никаких последствий

Создание и редактирование объектов AutoCAD

Вы можете создавать при помощи API различные объекты -- от простых линий и окружностей до сплайнов, эллипсов и штриховок для нескольких контуров. В общем, добавление нового графического объекта в Блок (объект таблицы блоков BlockTableRecord) осуществляется при помощи метода AppendEntity.

После того, как объект был создан, вы можете изменить его свойства: слой, цвет, тип линии и т.д. Для наглядности, базу данных чертежа можно представлять следующим образом: полагать, что любой графический примитив, например, "Отрезок", являются записью таблицы Model, где сама таблица Model -- одна из нескольких таблиц БД Блоков модели. Как и в обычных базах данных вы должны сперва получить данные перед их чтением или редактированием. Объекты, хранящиеся в объекте Database, ничем не отличаются друг от друга -- все они хранятся с ключами, равными ObjectId; вы используете функцию GetObject, чтобы получить объект из базы данных по его ObjectId и задать режим работы с ним (чтение; запись), как вы хотите работать с этим объектом.

Об открытии и закрытии объектов

Вне зависимости, с какими объектами или таблицами символов и их записями вы работаете, вам необходимо сперва открыть объект для чтения или записи. В случае, если вам необходимо произвести изменение с объектом, вы должны открыть его в режиме записи; для чтения можете использовать оба режима -- чтение или запись, но всё же предпочтительно использовать чтение, так вы будете уверены, что случайно не измените какие-либо данные объекта

Понятие ObjectId

Каждый содержащийся в базе данных объект связан с несколькими уникальными идентификаторами:

  • Entity handle;
  • ObjectId;
  • Instance pointer (указатель на объект);

Наиболее распространенным способом получения доступа к объекту является ObjectId. Идентификаторы на основе ObjectId также удобны, когда ваш проект использует одновременно .NET API и ActiveX (COM) API. В случае создания AutoLISP-функций предпочтительнее будет использовать Handle-идентификаторы.

Указатели (свойство UnmanagedObject) уместно применять только при вызове функций из ARX-API через механизм P/Invoke; Идентификаторы-handle сохраняются между сеансами AutoCAD для каждого из чертежей, поэтому они являются лучшим способом доступа к объектам, если необходимо экспортировать информацию о чертеже во внешний файл, который впоследствии может быть использован для обновления чертежа. ObjectId объекта в базе данных существует только пока база данных загружена в память. После закрытия базы данных ObjectId, присвоенный объекту, перестаёт существует и может стать другим для данного объекта при следующем открытии базы данных.

Получение ObjectId

Перед тем как работать с каким-либо объектом необходимо получить его идентификатор ObjectId; он будет назначен существующему объекту в базе данных модели, при открытии файла чертежа. Новые объекты получают ObjectId после своего создания (при добавлении в модель AddEntityToDatabase). Есть 2 основных пути получения ObjectId для существующих объектов в БД чертежа:

  • Использовать соответствующие свойства у объектов БД чертежа: например, свойство Database.Clayer возвращает ObjectId активного слоя чертежа;

  • Использовать итеративный перебор таблицы символов, например, таблицы слоев Layer;

    Открытие объекта

    Как только ObjectId был получен, можно использовать функцию GetObject для открытия объекта из-под транзакции, связанного с данным ObjectId. Объект может быть открыт в одном из следующих режимов:

  • Read. Объект открыт для чтения;

  • Write. Объект открывается для записи (также для чтения), если он не был открыт ранее;

  • Notify. Объект открывается для информирования в случаях, когда объект закрыт или уже открыт для чтении или записи, но когда не был открыт для данного режима Notify. Этот режим предназначен для использования в тех случаях, когда объект может изменять себя из собственного кода, например при определении пользовательского объекта, который не поддерживается управляемым AutoCAD .NET API;

Следует открывать объект в том режиме, для которого нужен соответствующий доступ. Несмотря на удобство режима Write для чтения и записи, при его использовании затрачивается больше ресурсов на стороне нативного кода. Если вы не уверены, что открываемый объект : это тот, с которым вы хотите работать, откройте его сперва в режиме чтения, а затем переведите объект из режима чтения в режим записи. Дополнительные сведения об изменении способа открытия объекта см. в разделе. Обе функции GetObject и Open возвращают объект (подробнее о каждой см. раздел). При разработке на C# вам необходимо будет дополнительно приводить тип возвращаемого объекта к нужному классу с помощью модификатора as.

При работе с Dynamic Runtime Language (DLR) вам нет необходимости беспокоиться, открыт объект дли записи или для чтения. Открытие объекта происходит автоматически и незаметно для пользователя, как и процесс фиксации изменений, внесенных в объект, без использования транзакций. Подробнее см. статью. В следующих примерах показано, как получить запись LayerTableRecord для нулевого слоя текущей базы данных. В следующем примере транзакция удаляется вручную после того, как она больше не нужна.

Document acCurDb = Application.DocumentManager.MdiActiveDocument.Database;
Transaction acTrans = acCurDb.TransactionManager.StartTransaction();

LayerTableRecord acLyrTblRec;
acLyrTblRec = acTrans.GetObject(acCurDb.LayerZero,
                                OpenMode.ForRead) as LayerTableRecord;

acTrans.Dispose();

В следующем примере используется оператор using для утилизации транзакции после того, как она больше не нужна. Конструкция using является предпочтительной.

Document acCurDb = Application.DocumentManager.MdiActiveDocument.Database;
using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
{
    LayerTableRecord acLyrTblRec;
    acLyrTblRec = acTrans.GetObject(acCurDb.LayerZero,
                                    OpenMode.ForRead) as LayerTableRecord;
}

Использование транзакций

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

При работе с объектами, открытыми с помощью GetObject, менеджер транзакций отслеживает изменения, которые вносятся в объект. Все новые объекты, которые вы создаете и добавляете в базу данных, также должны быть добавлены в транзакцию с помощью функции AddNewlyCreatedDBObject. После того как полученные объекты были отредактированы или добавлены в базу данных, вы можете сохранить изменения в базе данных и закрыть все открытые объекты с помощью функции Commit() объекта Transaction, созданном с помощью Transaction Manager. Для отмены изменений используйте метод Abort(). После завершения транзакции вызовите функцию Dispose, чтобы закрыть транзакцию (либо используйте открытие транзакции в теле using, чтобы не производить Dispose вручную).

Использование транзакций для получения доступа к объектам

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

  • StartTransaction : Запускает новую транзакцию, создавая новый экземпляр объекта Transaction. Используйте этот метод, когда вам нужно редактировать объект несколько раз в течение транзакции и иметь возможность применить или откатить изменения на любом шаге в ходе вложенных транзакций (если таковые будут);
  • StartOpenCloseTransation : создает объект OpenCloseTransaction, который ведет себя аналогично объекту Transaction, но дополнительно оборачивает объекты методами Open и Close объекта, что упрощает закрытие всех открытых объектов вместо необходимости явного закрытия каждого открытого объекта. Рекомендуется для использования во вспомогательных или служебных функциях, которые могут быть вызваны неизвестное количество раз, а также при работе с большинством из имеющихся обработчиками событий;

Получив объект Transaction или OpenCloseTransaction, используйте метод GetObject для открытия хранящегося в БД чертежа объекта для чтения или записи. Метод GetObject вернет объект типа DBObject и вам необходимо будет самостоятельно привести его к нужному типу при помощи приведения as. Все объекты, открытые во время транзакции, закрываются в конце транзакции. Чтобы завершить транзакцию, вызовите метод Dispose объекта транзакции. Если вы используете объект транзакции в составе конструкции using, вам не нужно вызывать метод Dispose. Перед уничтожением транзакции необходимо зафиксировать все внесенные изменения с помощью метода Commit. Если изменения не будут зафиксированы (применен метод Commit) до уничтожения транзакции, все сделанные изменения будут откачены к состоянию, в котором они находились до начала транзакции. К этому же поведению приведет и использование метода Abort (его вы можете использовать, например, во вложенных транзакциях или на определенном шаге в текущей транзакции. Можно запустить более одной транзакции.

Количество активных транзакций можно получить с помощью свойства NumberOfActiveTransactions объекта TransactionManager, а самую последнюю транзакцию можно получить с помощью свойства TopTransaction. Транзакции могут быть вложены одна в другую, чтобы откатить некоторые изменения, сделанные во время выполнения ранних процедур.

Запрос объектов

В примере ниже показывается, как открывать и считывать данные об объектах при помощи транзакции. Сперва метод GetObject используется для получения таблицы блоков BlockTable, а из неё -- записи, соответствующей пространству модели.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

[CommandMethod("StartTransactionManager")]
public static void StartTransactionManager()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                     OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for read
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForRead) as BlockTableRecord;

        // Step through the Block table record
        foreach (ObjectId asObjId in acBlkTblRec)
        {
            acDoc.Editor.WriteMessage("\nDXF name: " + asObjId.ObjectClass.DxfName);
            acDoc.Editor.WriteMessage("\nObjectID: " + asObjId.ToString());
            acDoc.Editor.WriteMessage("\nHandle: " + asObjId.Handle.ToString());
            acDoc.Editor.WriteMessage("\n");
        }

        // Dispose of the transaction
    }
}

Добавление нового объекта в БД

В примере ниже показывается, как добавить в БД определение окружности. Сперва метод GetObject используется для получения таблицы блоков BlockTable, а из неё -- записи, соответствующей пространству модели с режимом записи; для ней вызываются методы AppendEntity и AddNewlyCreatedDBObject для добавления новой окружности в пространство модели.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("AddNewCircleTransaction")]
public static void AddNewCircleTransaction()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartOpenCloseTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a circle with a radius of 3 at 5,5
        using (Circle acCirc = new Circle())
        {
            acCirc.Center = new Point3d(5, 5, 0);
            acCirc.Radius = 3;

            // Add the new object to Model space and the transaction
            acBlkTblRec.AppendEntity(acCirc);
            acTrans.AddNewlyCreatedDBObject(acCirc, true);
        }

        // Commit the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Варианты завершения транзакций

При завершении транзакции вы вправе решить, будут ли сохранены изменения в базе данных чертежа. Для сохранения изменений, внесенных в объекты, открытые в транзакции, используйте метод Commit. Если ваша программа столкнется с ошибкой или по какой-либо иной причине вы захотите отменить изменения, сделанные в транзакции, используйте метод Abort. Если до вызова Dispose не был вызван Commit, все изменения, сделанные в теле транзакции, откатываются. Независимо от того, вызывается ли Commit или Abort, необходимо вызвать Dispose, чтобы сигнализировать о завершении транзакции. Если объект транзакции запускался с помощью оператора Using, вызывать Dispose не нужно.

Освобождение памяти от объектов

При создании новых объектов в .NET необходимо правильно освобождать их из памяти. При использовании метода Dispose или при выполнении действий над объектов в теле оператора Using, системный сборщик мусора понимает, когда вы хотите освободиться от объекта. Предпочтительнее использовать конструкцию using, поскольку она выполняет все необходимые вызовы системных процедур для закрытия и утилизации объекта, когда он больше не нужен. Необходимо освобождать память от объекта (dispose) во всех приведенных ниже случаях:

  • Всегда при работе с объектами Transaction или DocumentLock;
  • Всегда с созданными объектами базы данных чертежа, производными от DBObject, которые добавляются в транзакцию;
  • Всегда с созданными объектами базы данных чертежа, производными от DBObject, которые не добавляются в базу данных чертежа;
  • Не обязательно с существующими объектами базы данных чертежа, объектами, производными от DBObject, открытыми с помощью Transaction.GetObject;

Вложенные транзакции

Транзакции могут быть вложены одна в другую. У вас может быть внешняя (основная) транзакция для отмены всех изменений, сделанных одной или несколькими внутренними транзакциями, как и внутренние транзакции могут использоваться для отмены только части сделанных изменений. При работе с вложенными транзакциями, они фигурируют в коде в теле родительской транзакции. Когда вы начинаете новые транзакции, они добавляются в предыдущую транзакцию. Вложенные транзакции должны быть зафиксированы (Commit) или прерваны (Abort) в порядке, обратном порядку их создания. Так, если у вас есть три транзакции, вы должны сперва закрыть третью, самую внутреннюю, потом вторую и, наконец, первую. Если вы прервете первую транзакцию, изменения, внесенные всеми тремя транзакциями, будут отменены. На следующей иллюстрации показано, как выглядят вложенные транзакции.

Использование вложенных транзакций для создания и редактирования объектов

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

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.EditorInput;
 
[CommandMethod("NestedTransactions")]
public static void NestedTransactions()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Create a reference to the Transaction Manager
    Autodesk.AutoCAD.DatabaseServices.TransactionManager acTransMgr;
    acTransMgr = acCurDb.TransactionManager;

    // Create a new transaction
    using (Transaction acTrans1 = acTransMgr.StartTransaction())
    {
        // Print the current number of active transactions
        acDoc.Editor.WriteMessage("\nNumber of transactions active: " +
                                    acTransMgr.NumberOfActiveTransactions.ToString());

        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans1.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans1.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                            OpenMode.ForWrite) as BlockTableRecord;

        // Create a circle with a radius of 3 at 5,5
        using (Circle acCirc = new Circle())
        {
            acCirc.Center = new Point3d(5, 5, 0);
            acCirc.Radius = 3;

            // Add the new object to Model space and the transaction
            acBlkTblRec.AppendEntity(acCirc);
            acTrans1.AddNewlyCreatedDBObject(acCirc, true);

            // Create the second transaction
            using (Transaction acTrans2 = acTransMgr.StartTransaction())
            {
                acDoc.Editor.WriteMessage("\nNumber of transactions active: " +
                                            acTransMgr.NumberOfActiveTransactions.ToString());

                // Change the circle's color
                acCirc.ColorIndex = 5;

                // Get the object that was added to Transaction 1 and set it to the color 5
                using (Line acLine = new Line(new Point3d(2, 5, 0), new Point3d(10, 7, 0)))
                {
                    acLine.ColorIndex = 3;

                    // Add the new object to Model space and the transaction
                    acBlkTblRec.AppendEntity(acLine);
                    acTrans2.AddNewlyCreatedDBObject(acLine, true);
                }

                // Create the third transaction
                using (Transaction acTrans3 = acTransMgr.StartTransaction())
                {
                    acDoc.Editor.WriteMessage("\nNumber of transactions active: " +
                                                acTransMgr.NumberOfActiveTransactions.ToString());

                    // Change the circle's color
                    acCirc.ColorIndex = 3;

                    // Update the display of the drawing
                    acDoc.Editor.WriteMessage("\n");
                    acDoc.Editor.Regen();

                    // Request to keep or discard the changes in the third transaction
                    PromptKeywordOptions pKeyOpts = new PromptKeywordOptions("");
                    pKeyOpts.Message = "\nKeep color change ";
                    pKeyOpts.Keywords.Add("Yes");
                    pKeyOpts.Keywords.Add("No");
                    pKeyOpts.Keywords.Default = "No";
                    pKeyOpts.AllowNone = true;

                    PromptResult pKeyRes = acDoc.Editor.GetKeywords(pKeyOpts);

                    if (pKeyRes.StringResult == "No")
                    {
                        // Discard the changes in transaction 3
                        acTrans3.Abort();
                    }
                    else
                    {
                        // Save the changes in transaction 3
                        acTrans3.Commit();
                    }

                    // Dispose the transaction
                }

                acDoc.Editor.WriteMessage("\nNumber of transactions active: " +
                                            acTransMgr.NumberOfActiveTransactions.ToString());

                // Keep the changes to transaction 2
                acTrans2.Commit();
            }
        }

        acDoc.Editor.WriteMessage("\nNumber of transactions active: " +
                                    acTransMgr.NumberOfActiveTransactions.ToString());

        // Keep the changes to transaction 1
        acTrans1.Commit();
    }
}

Открытие и закрытие объектов без транзакций

Транзакции облегчают процесс получения и работы с несколькими объектами, но они не являются единственным способом обеспечения доступа к объектам. Кроме использования транзакций, вы также можете открывать и закрывать объекты с помощью методов Open и Close.

Для использования метода Open вам, также как и для транзакций, необходимо получить идентификатор объекта ObjectId. Как и в методе GetObject, используемом с транзакциями, вам нужно указать режим открытия и осуществить приведение возвращаемого объекта DBObject к целевому типу.

Если вы внесли изменения в объект после того, как открыли его с помощью метода Open, вы можете использовать метод Cancel для отката всех изменений, сделанных с момента открытия объекта. Cancel следует вызывать для каждого объекта, в котором вы хотите сделать откат. После закрытия объекта также обязательно надо освободить от них память с помощью метода Dispose, либо вы можете использовать оператор using для закрытия и освобождение памяти от объекта.

Примечание: Открытые объекты должны быть закрыты. Если вы используете метод Open без оператора Using, то для открытого объекта необходимо вызвать метод Close или Cancel. Если не закрыть объект, это приведет к нарушению доступа объекта для чтения и дальнейшей нестабильной работе приложения. Если вам нужно работать с одним объектом, использование методов Open и Close может сократить количество строк кода, которые в противном случае пришлось бы писать, по сравнению с работой с менеджером транзакций. Тем не менее, использование транзакций предпочтительнее для открытия и закрытия объектов.

Важно: При использовании транзакций не следует использовать методы Open и Close, так как объекты могут открыться и/или закрыться менеджером транзакций не корректно, что может привести к нестабильной работе AutoCAD. Вместо этого используйте метод StartOpenCloseTransation для создания объекта OpenCloseTransaction, который позволяет безопасно работать с методами Open и Close.

Примечание: в AutoCAD и nanoCAD .NET API методы Open и Close помечены как устаревшие (Obsolete), вместо них рекомендуется использовать транзакции. Тем не менее их использование всё же возможно, но для некоторых случаев они не реализованы -- например, закрытие таблицы объектов в nanoCAD.

Запрос объектов (открытие и закрытие вручную)

В примере ниже показано, как вручную открыть и закрыть объекты без использования транзакции и метода GetObject.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

[CommandMethod("OpenCloseObjectId")]
public static void OpenCloseObjectId()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Open the Block table for read
    BlockTable acBlkTbl = null;

    try
    {
        acBlkTbl = acCurDb.BlockTableId.Open(OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for read
        BlockTableRecord acBlkTblRec = null;

        try
        {
            acBlkTblRec = acBlkTbl[BlockTableRecord.ModelSpace].Open(OpenMode.ForRead) as BlockTableRecord;

            // Step through the Block table record
            foreach (ObjectId acObjId in acBlkTblRec)
            {
                acDoc.Editor.WriteMessage("\nDXF name: " + acObjId.ObjectClass.DxfName);
                acDoc.Editor.WriteMessage("\nObjectID: " + acObjId.ToString());
                acDoc.Editor.WriteMessage("\nHandle: " + acObjId.Handle.ToString());
                acDoc.Editor.WriteMessage("\n");
            }
        }
        catch (Autodesk.AutoCAD.Runtime.Exception es)
        {
            System.Windows.Forms.MessageBox.Show(es.Message);
        }
        finally
        {
            // Close the Block table
            if (!acBlkTblRec.ObjectId.IsNull)
            {
                // Close the Block table record
                acBlkTblRec.Close();
                acBlkTblRec.Dispose();
            }
        }
    }
    catch (Autodesk.AutoCAD.Runtime.Exception es)
    {
        System.Windows.Forms.MessageBox.Show(es.Message);
    }
    finally
    {
        // Close the Block table
        if (!acBlkTbl.ObjectId.IsNull)
        {
            acBlkTbl.Close();
            acBlkTbl.Dispose();
        }
    }
}

Запрос объектов (использование конструкции using)

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

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

[CommandMethod("OpenCloseObjectIdWithUsing")]
public static void OpenCloseObjectIdWithUsing()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Open the Block table for read
    using (BlockTable acBlkTbl = acCurDb.BlockTableId.Open(OpenMode.ForRead) as BlockTable)
    {
        // Open the Block table record Model space for read
        using (BlockTableRecord acBlkTblRec = acBlkTbl[BlockTableRecord.ModelSpace].Open(OpenMode.ForRead)
                                                as BlockTableRecord)
        {
            // Step through the Block table record
            foreach (ObjectId acObjId in acBlkTblRec)
            {
                acDoc.Editor.WriteMessage("\nDXF name: " + acObjId.ObjectClass.DxfName);
                acDoc.Editor.WriteMessage("\nObjectID: " + acObjId.ToString());
                acDoc.Editor.WriteMessage("\nHandle: " + acObjId.Handle.ToString());
                acDoc.Editor.WriteMessage("\n");
            }

        // Close the Block table record
        }

        // Close the Block table
    }
}

Добавление нового объекта в БД чертежа

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

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("AddNewCircleOpenClose")]
public static void AddNewCircleOpenClose()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Open the Block table for read
    using (BlockTable acBlkTbl = acCurDb.BlockTableId.Open(OpenMode.ForRead) as BlockTable)
    {
        // Open the Block table record Model space for write
        using (BlockTableRecord acBlkTblRec = acBlkTbl[BlockTableRecord.ModelSpace].Open(OpenMode.ForWrite)
                                                as BlockTableRecord)
        {
            // Create a circle with a radius of 3 at 5,5
            using (Circle acCirc = new Circle())
            {
                acCirc.Center = new Point3d(5, 5, 0);
                acCirc.Radius = 3;

                // Add the new object to Model space and the transaction
                acBlkTblRec.AppendEntity(acCirc);

                // Close and dispose the circle object
            }

            // Close the Block table record
        }

        // Close the Block table
    }
}

Изменение режима открытия объектов

Режим открытия объекта (OpenMode) может быть изменен с чтения на запись и наоборот. Методы ниже можно использовать для этих целей:

  • Transaction.GetObject : если объект был открыт с использованием транзакции, используйте этот же метод для открытия объекта ещё раз с нужным режимом;
  • Методы UpgradeOpen и DowngradeOpen : если объект был открыт при помощи метода Open или OpenCloseTransaction.GetObject используйте метод UpgradeOpen для изменения режима доступа к объекту с чтения на запись или метод DowngradeOpen для перевода режима с записи на чтение. Вам не потребуется выполнять вручную вызов DowngradeOpen вместе с каждым UpgradeOpen, поскольку закрытие объекта (Close) или удаление объекта транзакции (Dispose) сотрет информацию об измененном режиме доступа к объекту.

Рекомендуется изначально открывать объект сразу в том режиме, в котором требуется, поскольку эффективнее открыть объект для чтения и запросить его свойства, чем открыть объект для записи и также запросить его свойства. Если вы не уверены, что объект потребуется изменить, лучше открыть объект для чтения, а затем обновить его для записи, так как это поможет снизить аппаратные издержки на лишние действия. Пример ниже получает для чтения таблицу слоев и в ней каждый из слоев (LayerTableRecord) также для чтения. Если имя слоя начинается с "Door" и он не является активным, то режим получения объекта изменяется на запись и у него задается флаг заморозки IsFrozen = true.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

[CommandMethod("FreezeDoorLayer")]
public static void FreezeDoorLayer()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Layer table for read
        LayerTable acLyrTbl;
        acLyrTbl = acTrans.GetObject(acCurDb.LayerTableId,
                                        OpenMode.ForRead) as LayerTable;

        // Step through each layer and update those that start with 'Door'
        foreach (ObjectId acObjId in acLyrTbl)
        {
            // Open the Layer table record for read
            LayerTableRecord acLyrTblRec;
            acLyrTblRec = acTrans.GetObject(acObjId,
                                            OpenMode.ForRead) as LayerTableRecord;

            // Check to see if the layer's name starts with 'Door' 
            if (acLyrTblRec.Name.StartsWith("Door",
                                            StringComparison.OrdinalIgnoreCase) == true)
            {
                // Check to see if the layer is current, if so then do not freeze it
                if (acLyrTblRec.ObjectId != acCurDb.Clayer)
                {
                    // Change from read to write mode
                    acTrans.GetObject(acObjId, OpenMode.ForWrite);

                    // Freeze the layer
                    acLyrTblRec.IsFrozen = true;
                }
            }
        }

        // Commit the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Об использовании динамической типизации

В управляемом AutoCAD .NET API возможно использовать динамическую типизацию - Dynamic Language Runtime (DLR), введенную с .NET 4.0 для прямого доступа к объектам без их открытия для чтения. Используя её, необходимо подключать к проекту "Microsoft.CSharp.dll"

Примечание: в nanoCAD .NET API подобная механика не реализовано, через DLR там уместно работать только с библиотеками типов (ActiveX), в отличие от AutoCAD где типизация COM - это object, а не dynamic.

Использование DLR позволяет получать прямой доступ к объектам без необходимости:

  • Открывать объект для чтения или записи, а затем закрывать его после завершения работы;

  • Использовать транзакции для сохранения внесенных изменений;

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

  • Object в VB.NET;
  • dynamic в C#;

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

  • Метод ObjectId.Item для доступа к элементу в коллекции;
  • Создание ссылки на ObjectId целевой таблицы или словаря путем присвоения её временной переменной и затем обращение к этой переменной для получения элемента массива Код ниже показывает оба способа получения доступа к объекту, сохраненному в таблице символов с использованием DLR
// Item method
dynamic acCurDb = HostApplicationServices.WorkingDatabase;
dynamic acMSpace = acCurDb.BlockTableId.Item(BlockTableRecord.ModelSpace);

// Reference an element directly from a collection
dynamic acCurDb = HostApplicationServices.WorkingDatabase;
dynamic acBlkTbl = acCurDb.BlockTableId;
dynamic acMSpace = acBlkTbl[BlockTableRecord.ModelSpace];

Работа с методом GetEnumerator

При использовании метода GetEnumerator в DLR, необходимо будет избавиться от объекта перечисления после завершения работы с ним. Приведенный ниже код содержит это действие.

dynamic acCurDb = HostApplicationServices.WorkingDatabase;
var acLtypeTbl = acCurDb.LinetypeTableId;
var acTblEnum = acLtypeTbl.GetEnumerator();

Использование LINQ-функций

Можно использовать LINQ для получения содержимого таблицы или словаря в чертеже с помощью DLR. Следующий пример демонстрирует использование LINQ-запросов для поиска отключенных и замороженных слоев.

[CommandMethod("LINQ")]
public static void LINQExample()
{
    dynamic db = HostApplicationServices.WorkingDatabase;
    dynamic doc = Application.DocumentManager.MdiActiveDocument;

    var layers = db.LayerTableId;
    for (int i = 0; i < 2; i++)
    {
        var newrec = layers.Add(new LayerTableRecord());
        newrec.Name = "Layer" + i.ToString();
        if (i == 0)
            newrec.IsFrozen = true;
        if (i == 1)
            newrec.IsOff = true;
    }

    var OffLayers = from l in (IEnumerable<dynamic>)layers
                    where l.IsOff
                    select l;

    doc.Editor.WriteMessage("\nLayers Turned Off:");

    foreach (dynamic rec in OffLayers)
        doc.Editor.WriteMessage("\n - " + rec.Name);

    var frozenOrOffNames = from l in (IEnumerable<dynamic>)layers
                            where l.IsFrozen == true || l.IsOff == true
                            select l;

    doc.Editor.WriteMessage("\nLayers Frozen or Turned Off:");

    foreach (dynamic rec in frozenOrOffNames)
        doc.Editor.WriteMessage("\n - " + rec.Name);
}

Прочие примеры

Приведенные ниже примеры используют следующие пространства имён

using Autodesk.AutoCAD.Runtime
using Autodesk.AutoCAD.ApplicationServices
using Autodesk.AutoCAD.DatabaseServices
using Autodesk.AutoCAD.Colors
using Autodesk.AutoCAD.Geometry

Добавление отрезка в текущее пространство без DLR и с его помощью:

[CommandMethod("ADDLINE_WITHOUT_DLR")]
public static void AddLine()
{
    // Get the current database
    Database acCurDb = HostApplicationServices.WorkingDatabase;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                     OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a line that starts at 5,5 and ends at 12,3
        using (Line acLine = new Line(new Point3d(5, 5, 0),
                                      new Point3d(12, 3, 0)))
        {
            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acLine);
            acTrans.AddNewlyCreatedDBObject(acLine, true);
        }

        // Save the new object to the database
        acTrans.Commit();
    }
}
//
[CommandMethod("ADDLINE_WITH_DLR")]
public static void AddLine()
{
    // Get the current database
    dynamic acCurDb = HostApplicationServices.WorkingDatabase;

    // Create a dynamic reference to model or paper space
    dynamic acSpace = acCurDb.CurrentSpaceId;

    // Create a line that starts at 5,5 and ends at 12,3
    dynamic acLine = new Line(new Point3d(5, 5, 0),
                              new Point3d(12, 3, 0));

    // Add the new object to the current space
    acSpace.AppendEntity(acLine);
}

Перебор объектов в текущем пространстве без DLR и с его помощью:

[CommandMethod("LISTOBJECTS_WITHOUT_DLR")]
public static void ListObjects()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = HostApplicationServices.WorkingDatabase;

    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table record Model space for write
        BlockTableRecord acSpace;
        acSpace = acTrans.GetObject(acCurDb.CurrentSpaceId,
                                    OpenMode.ForRead) as BlockTableRecord;

        // Step through the current space
        foreach (ObjectId objId in acSpace)
        {
            // Display the class and current layer of the object
            Entity acEnt = (Entity)acTrans.GetObject(objId, OpenMode.ForRead);
            acDoc.Editor.WriteMessage("\nObject Class: " + acEnt.GetRXClass().Name +
                                      "\nCurrent Layer: " + acEnt.Layer + 
                                       "\n");
        }
        acTrans.Commit();
    }
}

[CommandMethod("LISTOBJECTS_WITH_DLR")]
public static void ListObjects()
{
    // Get the current document and database
    dynamic acDoc = Application.DocumentManager.MdiActiveDocument;
    dynamic acCurDb = HostApplicationServices.WorkingDatabase;

    // Create a dynamic reference to model or paper space
    dynamic acSpace = acCurDb.CurrentSpaceId;

    // Step through the current space
    foreach (dynamic acEnt in acSpace)
    {
        // Display the class and current layer of the object
        acDoc.Editor.WriteMessage("\nObject Class: " + acEnt.GetRXClass().Name +
                                  "\nCurrent Layer: " + acEnt.Layer +
                                  "\n");
    }
}

О создании объектов

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

Примечание: Объекты создаются с помощью ключевого слова new, а затем добавляются к родительскому объекту с помощью методов Add или AppendEntity в зависимости от того, работаете ли вы с контейнером (таблицей символов или словарем) или объектом BlockTableRecord соответственно. При создании новых графических объектов, следующие свойства объектов назначаются по текущим установленным свойствам в базе данных модели:

  • Color
  • Layer
  • Linetype
  • LinetypeScale
  • LineWeight
  • PlotStyleName
  • Visible
  • Transparency

При необходимости установить объекту перечисленные выше свойства по умолчанию (для установленных в БД модели) используйте метод SetDatabaseDefaults.

Определение родительского объекта

Графические объекты добавляются к целевому блоку BlockTableRecord (элементу таблицы BlockTable), например, пространству модели или листа. Если вы хотите работать в текущем активном пространстве, вы можете получить его ObjectId из текущей базы данных с помощью свойства CurrentSpaceId (полученный ObjectId надо будет самостоятельно привести к объекту BlockTableRecord). ObjectId для записей таблицы блоков (BlockTableRecord) пространства модели и листа можно получить из коллекции BlockTable, используя статические свойства класса BlockTableRecord или методы GetBlockModelSpaceId и GetBlockPaperSpaceId класса SymbolUtilityServices из пространства имен DatabaseServices.

Доступ к пространству модели, пространству листа или текущему Блоку

Приведенный ниже код запрашивает у Пользователя, в каком из пространств (Модели, Листа или Текущем) создать отрезок. При выборе соответствующего варианта в выбранном пространстве создается объект. Запрос ObjectId для нужного BlockTableRecord осуществляется двумя способами.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.EditorInput;
 
[CommandMethod("AccessSpace")]
public static void AccessSpace()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record for read
        BlockTableRecord acBlkTblRec;

        // Request which table record to open
        PromptKeywordOptions pKeyOpts = new PromptKeywordOptions("");
        pKeyOpts.Message = "\nEnter which space to create the line in ";
        pKeyOpts.Keywords.Add("Model");
        pKeyOpts.Keywords.Add("Paper");
        pKeyOpts.Keywords.Add("Current");
        pKeyOpts.AllowNone = false;
        pKeyOpts.AppendKeywordsToMessage = true;

        PromptResult pKeyRes = acDoc.Editor.GetKeywords(pKeyOpts);

        if (pKeyRes.StringResult == "Model")
        {
            // Get the ObjectID for Model space from the Block table
            acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                            OpenMode.ForWrite) as BlockTableRecord;
        }
        else if (pKeyRes.StringResult == "Paper")
        {
            // Get the ObjectID for Paper space from the Block table
            acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.PaperSpace],
                                            OpenMode.ForWrite) as BlockTableRecord;
        }
        else
        {
            // Get the ObjectID for the current space from the database
            acBlkTblRec = acTrans.GetObject(acCurDb.CurrentSpaceId,
                                            OpenMode.ForWrite) as BlockTableRecord;
        }

        // Create a line that starts at 2,5 and ends at 10,7
        using (Line acLine = new Line(new Point3d(2, 5, 0),
                                new Point3d(10, 7, 0)))
        {
            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acLine);
            acTrans.AddNewlyCreatedDBObject(acLine, true);
        }

        // Save the new line to the database
        acTrans.Commit();
    }
}

Создание линейных объектов

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

  • Отрезок (Line);
  • Полилиния (Polyline) в плоскости;
  • Мульти-линия (MLine);
  • Плоская полилиния (Polyline2d);
  • Трехмерная полилиния (Polyline3d);

Примечание: тип Polyline2d является устаревшим, существовавшим в версиях AutoCAD до R14 (1997 г.) и далее оставлен только для обратной совместимости данных. Используйте тип Polyline вместо него.

Создание отрезка

Код ниже создает новый отрезок из точки (5,5,0) до точки (12,3,0) в пространстве модели

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("AddLine")]
public static void AddLine()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a line that starts at 5,5 and ends at 12,3
        using (Line acLine = new Line(new Point3d(5, 5, 0),
                                      new Point3d(12, 3, 0)))
        {

            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acLine);
            acTrans.AddNewlyCreatedDBObject(acLine, true);
        }

        // Save the new object to the database
        acTrans.Commit();
    }
}

Создание полилинии

Код ниже создает обычную плоскую полилинию с двумя прямыми сегментами (без дуг), заданными координатами (2,4), (4,2), и (6,4) в пространстве модели

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("AddLightweightPolyline")]
public static void AddLightweightPolyline()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a polyline with two segments (3 points)
        using (Polyline acPoly = new Polyline())
        {
            acPoly.AddVertexAt(0, new Point2d(2, 4), 0, 0, 0);
            acPoly.AddVertexAt(1, new Point2d(4, 2), 0, 0, 0);
            acPoly.AddVertexAt(2, new Point2d(6, 4), 0, 0, 0);

            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acPoly);
            acTrans.AddNewlyCreatedDBObject(acPoly, true);
        }

        // Save the new object to the database
        acTrans.Commit();
    }
}

Создание криволинейных объектов

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

  • Круговая дуга (Arc) - создается для центральной точки, радиуса, величины начального и конечного угла;
  • Окружность (Circle) -создается для заданных центра и величины радиуса;
  • Эллипс и эллиптическая кривая (Ellipse) - создается для центральной точки, точек на большой полуоси, значения отношения малой и большой полуосей, величин углов начала и конца (для эллиптической кривой);
  • Сплайн (Spline) - создается квадратичная или кубическая NURBS:кривая (неоднородный рациональный B-сплайн);

Создание окружности

Ниже приведен код, создающий окружность в пространстве Модели с центром в точке (2,3,0) и радиусом 4.25

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
 
[CommandMethod("AddCircle")]
public static void AddCircle()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a circle that is at 2,3 with a radius of 4.25
        using (Circle acCirc = new Circle())
        {
            acCirc.Center = new Point3d(2, 3, 0);
            acCirc.Radius = 4.25;

            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acCirc);
            acTrans.AddNewlyCreatedDBObject(acCirc, true);
        }

        // Save the new object to the database
        acTrans.Commit();
    }
}

Создание круговой дуги

Ниже приведен код, создающий в пространстве Модели круговую дугу с центром в точке (6.25,9.125,0), радиусом = 6, углом начала = 1.117 (64°), и углом конца = 3.5605 (204°)

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
 
[CommandMethod("AddArc")]
public static void AddArc()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create an arc that is at 6.25,9.125 with a radius of 6, and
        // starts at 64 degrees and ends at 204 degrees
        using (Arc acArc = new Arc(new Point3d(6.25, 9.125, 0),
                            6, 1.117, 3.5605))
        {

            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acArc);
            acTrans.AddNewlyCreatedDBObject(acArc, true);
        }

        // Save the new line to the database
        acTrans.Commit();
    }
}

Создание сплайна

Код ниже создает сплайн по Определяющим точкам в пространстве Модели на основе трёх точек (0, 0, 0), (5, 5, 0), (10, 0, 0). Начальный и конечный векторы касательной сплайна задаются (0.5, 0.5, 0.0).

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("AddSpline")]
public static void AddSpline()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;
    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;
        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;
        // Define the fit points for the spline
        Point3dCollection ptColl = new Point3dCollection();
        ptColl.Add(new Point3d(0, 0, 0));
        ptColl.Add(new Point3d(5, 5, 0));
        ptColl.Add(new Point3d(10, 0, 0));
        // Get a 3D vector from the point (0.5,0.5,0)
        Vector3d vecTan = new Point3d(0.5, 0.5, 0).GetAsVector();
        // Create a spline through (0, 0, 0), (5, 5, 0), and (10, 0, 0) with a
        // start and end tangency of (0.5, 0.5, 0.0)
        using (Spline acSpline = new Spline(ptColl, vecTan, vecTan, 4, 0.0))
        {
            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acSpline);
            acTrans.AddNewlyCreatedDBObject(acSpline, true);
        }
        // Save the new line to the database
        acTrans.Commit();
    }
}

Создание точечных объектов

Точечные объекты могут быть полезны, например, при использовании в качестве опорных точек, к которым можно привязываться при построении иной геометрии. Вы можете задать стиль точки и ее размер в % относительно рабочей области экрана или в абсолютных единицах. Свойства Pdmode и Pdsize объекта Database управляют внешним видом точечных объектов. Значения 0, 2, 3 и 4 для Pdmode задают фигуру (условный знак), которая будет отображаться на месте точки. Значение 1 означает, что точка не будет иметь отображения.

Добавление 32, 64 или 96 к значениям (0..4) с картинки выше приводит к отрисовке определенной фигуры поверх базового обозначения (0..4).

Параметр Pdsize управляет размером точечной фигуры, за исключением случаев, когда Pdmode равен 0 и 1. Значение 0 определяет размер точки, составляющий 5 процентов от высоты текущей графической области. Положительное значение Pdsize задает абсолютный размер точечной фигуры. Отрицательное значение интерпретируется как процент от размера области экрана. После изменения значений Pdmode и Pdsize внешний вид существующих точек изменится при следующем обновлении чертежа (при вызове команд РЕГЕН\ВСЕРЕГЕН)

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

Код ниже создает точечный объект в пространстве Модели в координатах (5, 5, 0). Редактируются отображения точки Pdmode и Pdsize.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("AddPointAndSetPointStyle")]
public static void AddPointAndSetPointStyle()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a point at (4, 3, 0) in Model space
        using (DBPoint acPoint = new DBPoint(new Point3d(4, 3, 0)))
        {
            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acPoint);
            acTrans.AddNewlyCreatedDBObject(acPoint, true);
        }

        // Set the style for all point objects in the drawing
        acCurDb.Pdmode = 34;
        acCurDb.Pdsize = 1;

        // Save the new object to the database
        acTrans.Commit();
    }
}

Создание областей с заполнением (Solid)

Вы можете создавать треугольные и четырёхугольные области, залитые цветом (Solid). Примечание: для оптимизации производительности рекомендуется устанавливать значение переменной FILLMODE в позицию "выкл" на период создания областей. При создании четырёхугольной области, залитой сплошной заливкой, её форма определяется последовательностью третьей и четвёртой точками (см. картинку ниже):

Первые 2 вершины определяют первое ребро полигона. Третья точка определяет диагональ напротив второй точки. Если четвертая точка = третьей точке, то создается треугольное заполнение.

Создание заполнения

Пример ниже формирует четырёхугольное тело (фигуру типа "галстук-бабочка") по опорным координатам (0, 0, 0), (5, 0, 0), (5, 8, 0), (0, 8, 0). Также создается заполнение в прямоугольнике, ограниченном координатами (10, 0, 0), (15, 0, 0), (10, 8, 0), (15, 8, 0).

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("Add2DSolid")]
public static void Add2DSolid()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;
    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;
        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;
        // Create a quadrilateral (bow-tie) solid in Model space
        using (Solid ac2DSolidBow = new Solid(new Point3d(0, 0, 0),
                                        new Point3d(5, 0, 0),
                                        new Point3d(5, 8, 0),
                                        new Point3d(0, 8, 0)))
        {
            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(ac2DSolidBow);
            acTrans.AddNewlyCreatedDBObject(ac2DSolidBow, true);
        }
        // Create a quadrilateral (square) solid in Model space
        using (Solid ac2DSolidSqr = new Solid(new Point3d(10, 0, 0),
                                        new Point3d(15, 0, 0),
                                        new Point3d(10, 8, 0),
                                        new Point3d(15, 8, 0)))
        {
            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(ac2DSolidSqr);
            acTrans.AddNewlyCreatedDBObject(ac2DSolidSqr, true);
        }
        // Save the new object to the database
        acTrans.Commit();
    }
}

Работа с областями (Region)

Области (Regions) — это двумерные плоскости с заполнением, образованные нескольких замкнутых контуров. Контур — это замкнутая граница, состоящая из прямых и/или криволинейных объектов, которые не имеют самопересечений. Контуры могут представлять собой комбинации линий, полилиний, двумерных и трёхмерных полилиний, окружностей, дуг, эллипсов, эллиптических дуг, сплайнов, трёхмерных граней, трасс (Trace) и заполнений (Solid). Объекты, составляющие контур, должны быть либо замкнутыми, либо образовывать замкнутые области, имея общие точки с другими объектами (начало первого = концу второго). Они также должны быть компланарными (находиться в одной плоскости). Контуры, составляющие область, должны быть определены как массив объектов.

Создание областей

Как и остальные объекты, области создаются путем создания экземпляра класса Region и последующего добавления его к объекту BlockTableRecord. Прежде чем добавить область к объекту BlockTableRecord, необходимо сформировать её границу на основе объектов, образующих замкнутый контур. Статический метод Region.CreateFromCurves создает область из каждого замкнутого контура, образованного входным массивом объектов. Метод CreateFromCurves возвращает объект DBObjectCollection (одну или несколько областей, каждую из которых необходимо добавить в целевой BlockTableRecord).

AutoCAD преобразует замкнутые 2D и плоские 3D полилинии в отдельные области, а затем преобразует полилинии, линии и кривые, образующие замкнутые плоские контуры. Если более двух кривых имеют общую конечную точку, результирующий регион может иметь разные варианты: из-за этого с помощью метода CreateFromCurves может быть создано несколько областей.

Создание простой области

Код ниже формирует область из окружности

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
 
[CommandMethod("AddRegion")]
public static void AddRegion()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create an in memory circle
        using (Circle acCirc = new Circle())
        {
            acCirc.Center = new Point3d(2, 2, 0);
            acCirc.Radius = 5;

            // Adds the circle to an object array
            DBObjectCollection acDBObjColl = new DBObjectCollection();
            acDBObjColl.Add(acCirc);

            // Calculate the regions based on each closed loop
            DBObjectCollection myRegionColl = new DBObjectCollection();
            myRegionColl = Region.CreateFromCurves(acDBObjColl);
            Region acRegion = myRegionColl[0] as Region;

            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acRegion);
            acTrans.AddNewlyCreatedDBObject(acRegion, true);

            // Dispose of the in memory circle not appended to the database
        }

        // Save the new object to the database
        acTrans.Commit();
    }
}

Создание составных областей

Вы можете создавать составные области путем вычитания, объединения или нахождения пересечения областей или трехмерных тел. Затем вы можете выдавливать или вращать полученные области для дальнейшего создания сложных трехмерных тел. Для создания составной области используйте метод BooleanOperation.

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

Объединение областей Для объединения областей вызовите метод BooleanOperation и используйте константу BooleanOperationType.BoolUnite вместо BooleanOperationType.BoolSubtract. Вы можете объединять области в любом порядке.

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

Создание составной области

Ниже приведен код, создающий 2 области для двух окружностей, затем область, сформированная для меньшей окружности, вычитается из большей и формируется область с фигурой "колеса".

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("CreateCompositeRegions")]
public static void CreateCompositeRegions()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create two in memory circles
        using (Circle acCirc1 = new Circle())
        {
            acCirc1.Center = new Point3d(4, 4, 0);
            acCirc1.Radius = 2;

            using (Circle acCirc2 = new Circle())
            {
                acCirc2.Center = new Point3d(4, 4, 0);
                acCirc2.Radius = 1;

                // Adds the circle to an object array
                DBObjectCollection acDBObjColl = new DBObjectCollection();
                acDBObjColl.Add(acCirc1);
                acDBObjColl.Add(acCirc2);

                // Calculate the regions based on each closed loop
                DBObjectCollection myRegionColl = new DBObjectCollection();
                myRegionColl = Region.CreateFromCurves(acDBObjColl);
                Region acRegion1 = myRegionColl[0] as Region;
                Region acRegion2 = myRegionColl[1] as Region;

                // Subtract region 1 from region 2
                if (acRegion1.Area > acRegion2.Area)
                {
                    // Subtract the smaller region from the larger one
                    acRegion1.BooleanOperation(BooleanOperationType.BoolSubtract, acRegion2);
                    acRegion2.Dispose();

                    // Add the final region to the database
                    acBlkTblRec.AppendEntity(acRegion1);
                    acTrans.AddNewlyCreatedDBObject(acRegion1, true);
                }
                else
                {
                    // Subtract the smaller region from the larger one
                    acRegion2.BooleanOperation(BooleanOperationType.BoolSubtract, acRegion1);
                    acRegion1.Dispose();

                    // Add the final region to the database
                    acBlkTblRec.AppendEntity(acRegion2);
                    acTrans.AddNewlyCreatedDBObject(acRegion2, true);
                }

                // Dispose of the in memory objects not appended to the database
            }
        }

        // Save the new object to the database
        acTrans.Commit();
    }
}

Создание штриховок

Замкнутые границы могут быть заполнены текстурой по заданному образцу (паттерну), образуя тем самым объект Штриховка. Сперва создается только определение штриховки (объект класса Hatch), для него задаются настройки отрисовки (тип штриховки, имя образца/паттерна, флаг ассоциативности). После этого можно указать контур, который будет являться внешней границей штриховки. Затем можно указать любые внутренние контуры, которые могут быть внутри штриховки.

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

Ассоциативность штриховок

Вы можете создавать ассоциативные или неассоциативные штриховки. Ассоциативные штриховки будут связаны со своими границами и обновлятся при изменении границ. Неассоциативные штриховки соответственно не будут авто-изменены при изменении геометрии границ. Чтобы сделать штриховку ассоциативной, установите свойство Associative созданного объекта штриховки в значение true. Чтобы сделать штриховку неассоциативной, установите свойство Associative в значение false. Ассоциативность штриховки должна быть установлена ​​до добавления в штриховку контуров. Чтобы сделать объект штриховки ассоциативным, установите свойство Associative в значение true, удалите объект из состава контура штриховки (RemoveLoopAt) и добавьте этот объект снова (AppendLoop).

Особенности задания образца штриховки

AutoCAD предоставляет сплошную заливку (SOLID) и десятки иных стандартных образцов штриховок. Разные стили образцов способны облегчить работу с визуальной идентификацией объектов. Возможно использовать образцы штриховок из поставки AutoCAD, либо из внешних подключаемых файлов. Чтобы задать пользовательский образец штриховки, необходимо указать как тип образца (гдеAutoCAD'у его искать), так и наименование самого образца штриховки. Тип образца определяет, где искать имя данного образца штриховки. При вводе типа образца используйте одну из следующих констант:

  • HatchPatternType.PreDefined: имя образца штриховки из файлов acad.pat или acadiso.pat;

  • HatchPatternType.UserDefined: используется шаблон в соответствии с текущим свойством Linetype;

  • HatchPatternType.CustomDefined: имя образца штриховки ищется в подключенных PAT файлах кроме системных acad.pat и acadiso.pat; При вводе имени образца используйте имя, допустимое для файла:определения штриховки в соответствии с её типом, указанным выше.

    Задание границ штриховке

    После создания определения штриховки Hatch можно добавить к ней границы. Границы могут представлять собой любую комбинацию линий, дуг, окружностей, двумерных полилиний, эллипсов, сплайнов и областей (Region). Первая добавленная граница должна быть внешней, определяющей крайние контуры, которые будут заполнены штриховкой. Для добавления внешней границы используйте метод AppendLoop с константой HatchLoopTypes.Outermost, указывающей тип добавляемой границы. После определения внешней границы можно продолжить добавление дополнительных границ. Добавьте внутренние границы, используя метод AppendLoop с константой HatchLoopTypes.Default. Внутренние границы определяют "острова" внутри штриховки. Способ обработки этих "островов" объектом Hatch зависит от значения свойства HatchStyle. Свойство HatchStyle может быть установлено в одно из следующих значений:

    Схема действияПоведениеОписание
    Обычная (HatchStyle.Normal)Задает стандартный стиль -- значение по умолчанию для свойства HatchStyle. Этот параметр заштриховывает область "снаружи -- внутрь". Если AutoCAD встречает внутреннюю границу, он отключает штриховку до тех пор, пока не встретит другую границу
    Только внешние границы (HatchStyle.Outer)Заполняет только самые внешние области. Этот стиль также штрихует по пути "снаружи -- внутрь", но отключает штриховку, если встречает внутреннюю границу, и не включает её снова.
    Пропуск (HatchStyle.Ignore)Игнорирует внутреннюю структуру. Этот параметр заштриховывает все внутренние контуры

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

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("AddHatch")]
public static void AddHatch()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a circle object for the closed boundary to hatch
        using (Circle acCirc = new Circle())
        {
            acCirc.Center = new Point3d(3, 3, 0);
            acCirc.Radius = 1;

            // Add the new circle object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acCirc);
            acTrans.AddNewlyCreatedDBObject(acCirc, true);

            // Adds the circle to an object id array
            ObjectIdCollection acObjIdColl = new ObjectIdCollection();
            acObjIdColl.Add(acCirc.ObjectId);

            // Create the hatch object and append it to the block table record
            using (Hatch acHatch = new Hatch())
            {
                acBlkTblRec.AppendEntity(acHatch);
                acTrans.AddNewlyCreatedDBObject(acHatch, true);

                // Set the properties of the hatch object
                // Associative must be set after the hatch object is appended to the 
                // block table record and before AppendLoop
                acHatch.SetHatchPattern(HatchPatternType.PreDefined, "ANSI31");
                acHatch.Associative = true;
                acHatch.AppendLoop(HatchLoopTypes.Outermost, acObjIdColl);
                acHatch.EvaluateHatch(true);
            }
        }

        // Save the new object to the database
        acTrans.Commit();
    }
}

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

Работа с выборкой объектов

Набор выделенных объектов может одержать один или несколько объектов, в том числе с использованием фильтра (например, объект на заданном слое для заданных типов). Выбор объектов Пользователем обычно предваряет собой начало каких-либо команд.

Наборы выделенных объектов не являются постоянными объектами. Если вам необходимо сохранить набор выделенных объектов для использования между несколькими командами, вам потребуется создать пользовательский словарь (DBDictionary) и записать в него идентификаторы объектов (ObjectId) из набора в виде XRecord с так называемыми soft-pointers (TypedValue с типом SoftPointerId).

В качестве альтернативы хранению идентификаторов объектов в виде soft-pointers, вы можете хранить идентификатор (Handle) каждого объекта в словаре. Затем, для получения из него ObjectId можно использовать метод Database.GetObjectId.

Независимо от того, храните ли вы идентификатор объекта в виде soft-pointers или дескриптора (Handle) в словаре, вам необходимо убедиться, что объект существует, прежде чем обращаться к нему.

Пользовательский ввод и фильтры выбора

Различные типы вводимых данных реализованы через специальные методы класса Editor пространства имён Autodesk.AutoCAD.EditorInput.

Объект PromptSelectionOptions используется для формулировки запроса, который будет предложен Пользователю при начале операции выбора, а класс SelectionFilter может использоваться для фильтрации набора выбора по различным свойствам объекта.

Класс PromptSelectionOptions предоставляет метод SetKeywords для указания ключевых слов запроса, а также свойства MessageForAdding и MessageForRemoval для вывода некоего сообщения вместе с операцией запроса. Класс SelectionFilter принимает параметры фильтра в виде массива объектов TypedValue, как описано в разделе О типе данных ResultBuffer. Каждый объект TypedValue представляет одно условие фильтра. Для выбора может быть указано любое количество условий разных типов (несколько условий для одного TypedValue.Key должно быть записано в единое значение TypedValue.Value).

Когда приложение готово запросить набор выбора, вызывается метод GetSelection объекта Editor. Метод Editor.GetSelection существует в нескольких перегрузках. Для простой выборки без фильтрации с использованием стандартного запроса AutoCAD используется перегрузка без параметров. В случаях, когда требуется предоставить пользовательские сообщения запроса, включая ключевые слова, используется перегрузка, которая принимает объект PromptSelectionOptions. Для указания фильтра используется перегрузка, которая принимает также и объект SelectionFilter.

Существуют и другие методы пользовательского ввода: метод Editor.SelectImplied обеспечивает доступ к набору выбора или набору выбора, который Пользователь сформировал до вызова команды. Метод Editor.SelectPrevious возвращает объекты, выбранные в предыдущем наборе выбора. Такие методы, как SelectCrossingWindow и SelectFence, позволяют выбирать объекты, видимые в текущем окне, пересекаемые заданным многоугольником и т.д.

Получение доступа к выбранным объектам (PickFirst)

Набор выбора PickFirst создается при выборе объектов перед запуском команды. Для получения объектов набора выбора PickFirst необходимо выполнить несколько условий:

  • Системная переменная PICKFIRST должна быть установлена в 1;
  • Флаг команды UsePickSet должен быть определен с помощью команды, которая должна использовать набор выбора PickFirst;
[CommandMethod("ShowObject", CommandFlags.UsePickSet)]
  • Чтобы получить набор выбора PickFirst используйте метод Editor.SelectImplied. Метод SetImpliedSelection используется для очистки текущего набора выбора PickFirst.

Получение текущего набора выбора PickFirst

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

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;

[CommandMethod("CheckForPickfirstSelection", CommandFlags.UsePickSet)]
public static void CheckForPickfirstSelection()
{
    // Get the current document
    Editor acDocEd = Application.DocumentManager.MdiActiveDocument.Editor;

    // Get the PickFirst selection set
    PromptSelectionResult acSSPrompt;
    acSSPrompt = acDocEd.SelectImplied();

    SelectionSet acSSet;

    // If the prompt status is OK, objects were selected before
    // the command was started
    if (acSSPrompt.Status == PromptStatus.OK)
    {
        acSSet = acSSPrompt.Value;

        Application.ShowAlertDialog("Number of objects in Pickfirst selection: " +
                                    acSSet.Count.ToString());
    }
    else
    {
        Application.ShowAlertDialog("Number of objects in Pickfirst selection: 0");
    }

    // Clear the PickFirst selection set
    ObjectId[] idarrayEmpty = new ObjectId[0];
    acDocEd.SetImpliedSelection(idarrayEmpty);

    // Request for objects to be selected in the drawing area
    acSSPrompt = acDocEd.GetSelection();

    // If the prompt status is OK, objects were selected
    if (acSSPrompt.Status == PromptStatus.OK)
    {
        acSSet = acSSPrompt.Value;

        Application.ShowAlertDialog("Number of objects selected: " +
                                    acSSet.Count.ToString());
    }
    else
    {
        Application.ShowAlertDialog("Number of objects selected: 0");
    }
}

Выбор объектов в чертеже

Вы можете выбирать объекты, предоставляя пользователю возможность интерактивного выбора, или имитировать различные варианты выбора объектов с помощью API AutoCAD .NET (без участия Пользователя). Если ваша программа выполняет несколько наборов выбора, вам потребуется либо отслеживать каждый возвращаемый набор выбора, либо создать объект ObjectIdCollection для получения всех выбранных объектов. Следующие функции позволяют выбирать объекты из чертежа:

  • GetSelection : запрос Пользователя указать объекты на чертеже;
  • SelectAll : выбрать все объекты в чертеже (выберутся все объекты в пространстве модели и на листах, а также все объекты на заблокированных и замороженных слоях);
  • SelectCrossingPolygon : выбор объектов, расположенных внутри и/или пересекающих некоторый многоугольник, определенный заданными точками. Многоугольник может иметь любую форму, но не может пересекать или касаться самого себя;
  • SelectCrossingWindow : выбор объектов, расположенных внутри или пересекающих некоторый прямоугольный контур, заданный двумя вершинами (минимальной и максимальной точками);
  • SelectFence : выбор объектов, пересекающих рамку. Выбор с помощью рамки аналогичен SelectCrossingPolygon, за исключением того, что рамка не замкнута, и может пересекать саму себя;
  • SelectLast : выбор объекта в данном пространстве, созданного последним;
  • SelectPrevious : выбор объектов из ранней выборки;
  • SelectWindow : выбор объектов, расположенных строго внутри некоторого прямоугольного контура, заданного двумя вершинами (минимальной и максимальной точками). В отличие от SelectCrossingWindow не допускает пересечения объектами контура;
  • SelectWindowPolygon : выбор объектов, расположенных строго внутри некоторого контура, заданного набором точек. Полигон может быть любой формы, но не может пересекать или касаться самого себя;
  • SelectAtPoint : выбор объектов, проходящих через заданную точку и добавление их в активный набор выделенных объектов.
  • SelectByPolygon : выбор объектов внутри некоторого контура и добавление их в активный набор выделенных объектов.

Запрос отображенных на экране объектов и их перебор

Код ниже содержит запрос к Пользователю на выбор объектов, затем для каждого объекта меняется цвет на зеленый (ColorIndex = 3)

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
 
[CommandMethod("SelectObjectsOnscreen")]
public static void SelectObjectsOnscreen()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Request for objects to be selected in the drawing area
        PromptSelectionResult acSSPrompt = acDoc.Editor.GetSelection();

        // If the prompt status is OK, objects were selected
        if (acSSPrompt.Status == PromptStatus.OK)
        {
            SelectionSet acSSet = acSSPrompt.Value;

            // Step through the objects in the selection set
            foreach (SelectedObject acSSObj in acSSet)
            {
                // Check to make sure a valid SelectedObject object was returned
                if (acSSObj != null)
                {
                    // Open the selected object for write
                    Entity acEnt = acTrans.GetObject(acSSObj.ObjectId,
                                                        OpenMode.ForWrite) as Entity;

                    if (acEnt != null)
                    {
                        // Change the object's color to Green
                        acEnt.ColorIndex = 3;
                    }
                }
            }

            // Save the new object to the database
            acTrans.Commit();
        }

        // Dispose of the transaction
    }
}

Выбор объектов для ограничивающего прямоугольника

Код ниже осуществляет выбор объектов без участия Пользователя для прямоугольной области, заданной двумя крайними точками 2,2,0) и (10,8,0)

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.EditorInput;
 
[CommandMethod("SelectObjectsByCrossingWindow")]
public static void SelectObjectsByCrossingWindow()
{
    // Get the current document editor
    Editor acDocEd = Application.DocumentManager.MdiActiveDocument.Editor;

    // Create a crossing window from (2,2,0) to (10,8,0)
    PromptSelectionResult acSSPrompt;
    acSSPrompt = acDocEd.SelectCrossingWindow(new Point3d(2, 2, 0),
                                                new Point3d(10, 8, 0));

    // If the prompt status is OK, objects were selected
    if (acSSPrompt.Status == PromptStatus.OK)
    {
        SelectionSet acSSet = acSSPrompt.Value;

        Application.ShowAlertDialog("Number of objects selected: " +
                                    acSSet.Count.ToString());
    }
    else
    {
        Application.ShowAlertDialog("Number of objects selected: 0");
    }
}

Ключевые слова

Настройки пользовательского ввода также позволяют указать набор ключевых слов, фактически являющихся аналогов некоторых enum на стороне исходного кода. Задание ключевых слов реализуется через метод SetKeywords объекта класса PromptSelectionOptions. Для передачи Пользователю созданных настроек необходимо передать сформированный объект класса PromptSelectionOptions в метод Editor.GetSelection().

Событие PromptSelectionOptions.KeywordInput срабатывает при выборе Пользователем одного из заданных ключевых слов.

Обработчик KeywordInput имеет аргумент SelectionTextInputEventArgs, который служит как входным, так и выходным параметром. Свойство Input аргумента SelectionTextInputEventArgs указывает выбранное ключевое слово. Обработчик сравнивает это ключевое слово с ключевыми словами в списке заданных ключевых слов и вызывает соответствующий метод выбора. Если метод выбора (GetSelection) возвращает какие-либо элементы модели, приложение добавляет их в аргумент SelectionTextInputEventArgs с помощью метода SelectionTextInputEventArgs.AddObjects.

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

private static void SelectionKeywordInputHandler(object sender, SelectionTextInputEventArgs eSelectionInput)
{
	// Gets the current document editor and define other variables for the current scope
	Editor acDocEd = Application.DocumentManager.MdiActiveDocument.Editor;
    PromptSelectionResult acSSPrompt = null;
    SelectionSet acSSet = null;
    ObjectId[] acObjIds = null;

	   // See if the user choose the myFence keyword
	   switch (eSelectionInput.Input) {
        case "myFence":
			         // Uses the four points to define a fence selection
            Point3dCollection ptsFence = new Point3dCollection();
            ptsFence.Add(new Point3d(5.0, 5.0, 0.0));
            ptsFence.Add(new Point3d(13.0, 15.0, 0.0));
            ptsFence.Add(new Point3d(12.0, 9.0, 0.0));
            ptsFence.Add(new Point3d(5.0, 5.0, 0.0));

            acSSPrompt = acDocEd.SelectFence(ptsFence);
			         break;
        case "myWindow":
			         // Defines a rectangular window selection
            acSSPrompt = acDocEd.SelectWindow(new Point3d(1.0, 1.0, 0.0), new Point3d(30.0, 20.0, 0.0));
			         break;
        case "myWPoly":
			         // Uses the four points to define a polygon window selection
            Point3dCollection ptsPolygon = new Point3dCollection();
            ptsPolygon.Add(new Point3d(5.0, 5.0, 0.0));
            ptsPolygon.Add(new Point3d(13.0, 15.0, 0.0));
            ptsPolygon.Add(new Point3d(12.0, 9.0, 0.0));
            ptsPolygon.Add(new Point3d(5.0, 5.0, 0.0));

            acSSPrompt = acDocEd.SelectWindowPolygon(ptsPolygon);
			         break;
		      case "myLastSel":
			        // Gets the last object created
			        acSSPrompt = acDocEd.SelectLast();
			        break;
		      case "myPrevSel":
			        // Gets the previous object selection set
			        acSSPrompt = acDocEd.SelectPrevious();
			        break;
	   }

    // If the prompt status is OK, objects were selected and return
    if (acSSPrompt != null)
    {
        if (acSSPrompt.Status == PromptStatus.OK)
        {
            // Objects were selected, so add them to the current selection
            acSSet = acSSPrompt.Value;
            acObjIds = acSSet.GetObjectIds();
            eSelectionInput.AddObjects(acObjIds);
        }
    }
}

[CommandMethod("SelectionKeywordInput")]
public static void SelectionKeywordInput()
{
    // Gets the current document editor
    Editor acDocEd = Application.DocumentManager.MdiActiveDocument.Editor;

    // Setups the keyword options
    PromptSelectionOptions acKeywordOpts = new PromptSelectionOptions();
    acKeywordOpts.Keywords.Add("myFence");
    acKeywordOpts.Keywords.Add("myWindow");
    acKeywordOpts.Keywords.Add("myWPoly");
    acKeywordOpts.Keywords.Add("myLastSel");
    acKeywordOpts.Keywords.Add("myPrevSel");

    // Adds the event handler for keyword input
    acKeywordOpts.KeywordInput += new SelectionTextInputEventHandler(SelectionKeywordInputHandler);

    // Prompts the user for a selection set
    PromptSelectionResult acSSPrompt = acDocEd.GetSelection(acKeywordOpts);

    // If the prompt status is OK, objects were selected
    if (acSSPrompt.Status == PromptStatus.OK)
    {
        // Gets the selection set
        SelectionSet acSSet = acSSPrompt.Value;

        // Gets the objects from the selection set
        ObjectId[] acObjIds = acSSet.GetObjectIds();
        Database acCurDb = Application.DocumentManager.MdiActiveDocument.Database;

        // Starts a transaction
        using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
        {
            try
            {
                // Gets information about each object
                foreach (ObjectId acObjId in acObjIds)
                {
                    Entity acEnt = (Entity)acTrans.GetObject(acObjId, OpenMode.ForWrite, true);
                    acDocEd.WriteMessage("\nObject selected: " + acEnt.GetType().FullName);

                }
            }
            finally
            {
                acTrans.Dispose();
            }
        }
    }

    // Removes the event handler for keyword input
    acKeywordOpts.KeywordInput -= new SelectionTextInputEventHandler(SelectionKeywordInputHandler);
}

Объединение нескольких наборов выбора

Вы можете объединить несколько наборов выбора, создав объект ObjectIdCollection, а затем добавив идентификаторы объектов из нескольких наборов выбора в него. Помимо добавления идентификаторов объектов в объект ObjectIdCollection, вы можете удалять идентификаторы объектов. После добавления всех идентификаторов объектов в объект ObjectIdCollection вы можете пройтись по каждому из ObjectId и получить объекты при необходимости.

Код ниже запрашивает у Пользователя 2 разные группы объектов, а потом объединяет объекты групп в единый набор выбора ObjectIdCollection.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;

[CommandMethod("MergeSelectionSets")]
public static void MergeSelectionSets()
{
    // Get the current document editor
    Editor acDocEd = Application.DocumentManager.MdiActiveDocument.Editor;
    // Request for objects to be selected in the drawing area
    PromptSelectionResult acSSPrompt;
    acSSPrompt = acDocEd.GetSelection();
    SelectionSet acSSet1;
    ObjectIdCollection acObjIdColl = new ObjectIdCollection();
    // If the prompt status is OK, objects were selected
    if (acSSPrompt.Status == PromptStatus.OK)
    {
        // Get the selected objects
        acSSet1 = acSSPrompt.Value;
        // Append the selected objects to the ObjectIdCollection
        acObjIdColl = new ObjectIdCollection(acSSet1.GetObjectIds());
    }
    // Request for objects to be selected in the drawing area
    acSSPrompt = acDocEd.GetSelection();
    SelectionSet acSSet2;
    // If the prompt status is OK, objects were selected
    if (acSSPrompt.Status == PromptStatus.OK)
    {
        acSSet2 = acSSPrompt.Value;
        // Check the size of the ObjectIdCollection, if zero, then initialize it
        if (acObjIdColl.Count == 0)
        {
            acObjIdColl = new ObjectIdCollection(acSSet2.GetObjectIds());
        }
        else
        {
            // Step through the second selection set
            foreach (ObjectId acObjId in acSSet2.GetObjectIds())
            {
                // Add each object id to the ObjectIdCollection
                acObjIdColl.Add(acObjId);
            }
        }
    }
    Application.ShowAlertDialog("Number of objects selected: " +
                                acObjIdColl.Count.ToString());
}

Настройки фильтрации объектов

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

Примечание: Фильтрация распознает значения, явно назначенные объектам, а не унаследованные от свойств слоя. Например, если свойство типа линии объекта установлено на ПоСлою, а слою, которому он назначен, установлен тип линии Hidden, то при фильтрации объектов, которым назначен тип линии Hidden, эти объекты не будут выбраны, поскольку их свойство типа линии установлено на ПоСлою.

Выборка объектов из наборов выбора

После создания набора выбора можно работать с идентификаторами выбранных объектов. Наборы выбора не позволяют добавлять или удалять из себя идентификаторы объектов. Вместо этого для объединения, добавления и удаления идентификаторов объектов можно использовать ObjectIdCollection. Для удаления идентификатора объекта из объекта ObjectIdCollection используйте методы Remove или RemoveAt.

Настройка SelectionFilter

Критерии фильтра выбора (SelectionFilter) состоят из пар аргументов (TypeCode и Value) в структуре TypedValue. Первый аргумент, TypeCode, определяет тип фильтра (например, объект), а второй аргумент, Value, указывает значение, по которому выполняется фильтрация (например, тип "Окружность"). Тип фильтра — это код группы DXF, который определяет, какой фильтр следует использовать. Ниже перечислены некоторые из наиболее распространенных типов фильтров.

DXF codeFilter type
DxfCode.Start = 0Тип объекта (строка). Например, отрезок Line, окружность Circle
DxfCode.BlockName =2Наименование определения блока (строка) для выборки Вхождений блоков с данным именем блока
DxfCode.LayerName = 8Наименование слоя (строка)
DxfCode.Visibility = 60Значение видимости объектов (integer). 0 = видимый, 1 = невидимый
DxfCode.Color = 62Числовый индекс, соответствующий цвету - ColorIndex, от 0 до 256. Значение 0 = ПоБлоку. Значение 256 = ПоСлою. Отрицательное значение указывает, что слой выключен.
67Model/paper space indicator (Integer) Use 0 or omitted = model space, 1 = paper space.

Имя типа объекта можно получить с помощью следующего приведения:

System.Type t;
string dxfName =Autodesk.AutoCAD.Runtime.RXObject.GetClass(t).DxfName;

Определение выбора по одному критерию фильтрации

Ниже приведен код, задающий выборку объектов только для типа объектов = Окружность.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
 
[CommandMethod("FilterSelectionSet")]
public static void FilterSelectionSet()
{
    // Get the current document editor
    Editor acDocEd = Application.DocumentManager.MdiActiveDocument.Editor;

    // Create a TypedValue array to define the filter criteria
    TypedValue[] acTypValAr = new TypedValue[1];
    acTypValAr.SetValue(new TypedValue((int)DxfCode.Start, "CIRCLE"), 0);

    // Assign the filter criteria to a SelectionFilter object
    SelectionFilter acSelFtr = new SelectionFilter(acTypValAr);

    // Request for objects to be selected in the drawing area
    PromptSelectionResult acSSPrompt;
    acSSPrompt = acDocEd.GetSelection(acSelFtr);

    // If the prompt status is OK, objects were selected
    if (acSSPrompt.Status == PromptStatus.OK)
    {
        SelectionSet acSSet = acSSPrompt.Value;

        Application.ShowAlertDialog("Number of objects selected: " +
                                    acSSet.Count.ToString());
    }
    else
    {
        Application.ShowAlertDialog("Number of objects selected: 0");
    }
}

Определение выбора по нескольким критериям фильтрации

Ниже приведен код, задающий выборку объектов только для типа объектов = Окружность, расположенных на слое 0 и имеющих цвет = 5 (Синий)

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
 
[CommandMethod("FilterBlueCircleOnLayer0")]
public static void FilterBlueCircleOnLayer0()
{
    // Get the current document editor
    Editor acDocEd = Application.DocumentManager.MdiActiveDocument.Editor;

    // Create a TypedValue array to define the filter criteria
    TypedValue[] acTypValAr = new TypedValue[3];
    acTypValAr.SetValue(new TypedValue((int)DxfCode.Color, 5), 0);
    acTypValAr.SetValue(new TypedValue((int)DxfCode.Start, "CIRCLE"), 1);
    acTypValAr.SetValue(new TypedValue((int)DxfCode.LayerName, "0"), 2);

    // Assign the filter criteria to a SelectionFilter object
    SelectionFilter acSelFtr = new SelectionFilter(acTypValAr);

    // Request for objects to be selected in the drawing area
    PromptSelectionResult acSSPrompt;
    acSSPrompt = acDocEd.GetSelection(acSelFtr);

    // If the prompt status is OK, objects were selected
    if (acSSPrompt.Status == PromptStatus.OK)
    {
        SelectionSet acSSet = acSSPrompt.Value;

        Application.ShowAlertDialog("Number of objects selected: " +
                                    acSSet.Count.ToString());
    }
    else
    {
        Application.ShowAlertDialog("Number of objects selected: 0");
    }
}

Добавление логических операций в фильтры

При указании нескольких критериев фиьтрации выбора AutoCAD предполагает, что выбранный объект должен соответствовать каждому критерию. Вы можете уточнить критерии фильтрации другими способами. Для числовых элементов можно указать операции сравнения (например, радиус круга должен быть больше или равен 5,0). А для всех элементов можно указать логические операции (например, для содержимого Text или MText).

Используйте код DXF -4 или константу DxfCode.Operator для указания оператора сравнения в фильтре выбора. Оператор выражается в виде строки. Перечень допустимых операторов приведен в следующей таблице.

OperatorОписание
"*"Любые значения (всегда true)
"="Точное соответствие
"!="Точное не-соответствие
"/="Точное не-соответствие
"<>"Точное не-соответствие
"<"Менее, чем
"<="Менее или равно, чем
">"Более, чем
">="Более или равно, чем
"&"Побитовое И, только для групп целых чисел
"&="Побитовое И (аналог &&), только для групп целых чисел

Логические операторы в фильтре выбора также обозначаются кодом группы -4 или константой DxfCode.Operator, а оператор представляет собой строку, но операторы должны быть спарены. Открывающий оператор предваряется символом «меньше» (<), а замыкающий оператор сопровождается символом «больше» (>). В следующей таблице перечислены логические операторы, разрешенные для задания критериев фильтрации.

Начальный операторСодержимоеЗамыкающий оператор
"<AND"Один или несколько операндов"AND>"
"<OR"Один или несколько операндов"OR>"
"<XOR"2 операнда"XOR>"
"<NOT"1 операнд"NOT>"

Выбор окружности с радиусом более или равно 5.0

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
 
[CommandMethod("FilterRelational")]
public static void FilterRelational()
{
    // Get the current document editor
    Editor acDocEd = Application.DocumentManager.MdiActiveDocument.Editor;

    // Create a TypedValue array to define the filter criteria
    TypedValue[] acTypValAr = new TypedValue[3];
    acTypValAr.SetValue(new TypedValue((int)DxfCode.Start, "CIRCLE"), 0);
    acTypValAr.SetValue(new TypedValue((int)DxfCode.Operator, ">="), 1);
    acTypValAr.SetValue(new TypedValue(40, 5), 2);

    // Assign the filter criteria to a SelectionFilter object
    SelectionFilter acSelFtr = new SelectionFilter(acTypValAr);

    // Request for objects to be selected in the drawing area
    PromptSelectionResult acSSPrompt;
    acSSPrompt = acDocEd.GetSelection(acSelFtr);

    // If the prompt status is OK, objects were selected
    if (acSSPrompt.Status == PromptStatus.OK)
    {
        SelectionSet acSSet = acSSPrompt.Value;

        Application.ShowAlertDialog("Number of objects selected: " +
                                    acSSet.Count.ToString());
    }
    else
    {
        Application.ShowAlertDialog("Number of objects selected: 0");
    }
}

Выбор объектов Тест и МТекст

В альтернативу использования фильтра типа

TypedValue[] acTypValAr = new TypedValue[1];
acTypValAr[1] = new TypedValue((int)DxfCode.Start, "TEXT, MTEXT");

Можно записать это же условие с помощью операторов:

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;

[CommandMethod("FilterForText")]
public static void FilterForText()
{
    // Get the current document editor
    Editor acDocEd = Application.DocumentManager.MdiActiveDocument.Editor;
    // Create a TypedValue array to define the filter criteria
    TypedValue[] acTypValAr = new TypedValue[4];
    acTypValAr.SetValue(new TypedValue((int)DxfCode.Operator, "\<or"), 0);
    acTypValAr.SetValue(new TypedValue((int)DxfCode.Start, "TEXT"), 1);
    acTypValAr.SetValue(new TypedValue((int)DxfCode.Start, "MTEXT"), 2);
    acTypValAr.SetValue(new TypedValue((int)DxfCode.Operator, "or\>"), 3);
    // Assign the filter criteria to a SelectionFilter object
    SelectionFilter acSelFtr = new SelectionFilter(acTypValAr);
    // Request for objects to be selected in the drawing area
    PromptSelectionResult acSSPrompt;
    acSSPrompt = acDocEd.GetSelection(acSelFtr);
    // If the prompt status is OK, objects were selected
    if (acSSPrompt.Status == PromptStatus.OK)
    {
        SelectionSet acSSet = acSSPrompt.Value;
        Application.ShowAlertDialog("Number of objects selected: " +
                                    acSSet.Count.ToString());
    }
    else
    {
        Application.ShowAlertDialog("Number of objects selected: 0");
    }
}

Использование регулярных выражений

Имена символов и строки в фильтрах выбора могут содержать шаблоны с регулярными выражениями

В следующей таблице приведены регулярные выражения, распознаваемые AutoCAD, и их значение:

СимволОписание
# (pound)Соответствует любой одной цифре
@ (at)Соответствует любому одиночному символу алфавита
. (period)Соответствует любому одиночному не-алфавитному символу
* (asterisk)Соответствует любой последовательности символов, включая пустую строку, и может использоваться в любом месте поисковой строки: в начале, середине или конце.
? (question mark)Соответствует любому одиночному символу
~ (tilde)Если это первый символ в поисковом критерии, он соответствует всему, кроме поискового критерия
[...]Соответствует любому из символов, заключенных в данные [...]
[~...]Не соответствует любому из символов, заключенных в данные [...]
- (hyphen)Используется внутри скобок для указания диапазона значений для одного символа.
, (comma)Разделяет 2 поисковых критерия
` (reverse quote)Исключает специальные символы (читает следующий символ буквально)

Используйте обратную кавычку (`) для случая, если символ должен восприниматься буквально. Например, чтобы указать, что в набор выбора должен быть включен только анонимный блок с именем "*U2", используйте значение "`*U2".

Выборка MText с заданным значением

Ниже приведен код, производящий выборку объектов типа MText, содержащих буквосочетание "The"

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
 
[CommandMethod("FilterMtextWildcard")]
public static void FilterMtextWildcard()
{
    // Get the current document editor
    Editor acDocEd = Application.DocumentManager.MdiActiveDocument.Editor;

    // Create a TypedValue array to define the filter criteria
    TypedValue[] acTypValAr = new TypedValue[2];
    acTypValAr.SetValue(new TypedValue((int)DxfCode.Start, "MTEXT"), 0);
    acTypValAr.SetValue(new TypedValue((int)DxfCode.Text, "*The*"), 1);

    // Assign the filter criteria to a SelectionFilter object
    SelectionFilter acSelFtr = new SelectionFilter(acTypValAr);

    // Request for objects to be selected in the drawing area
    PromptSelectionResult acSSPrompt;
    acSSPrompt = acDocEd.GetSelection(acSelFtr);

    // If the prompt status is OK, objects were selected
    if (acSSPrompt.Status == PromptStatus.OK)
    {
        SelectionSet acSSet = acSSPrompt.Value;

        Application.ShowAlertDialog("Number of objects selected: " +
                                    acSSet.Count.ToString());
    }
    else
    {
        Application.ShowAlertDialog("Number of objects selected: 0");
    }
}

Фильтрация данных в XData

Внешние приложения могут добавлять к объектам AutoCAD такие данные, как текстовые строки, числовые значения, 3D-точки, значения расстояний и имена слоев. Эти данные называются расширенными данными или xdata. Вы можете фильтровать объекты, содержащие расширенные данные для заданного приложения.

Код в примере ниже содержит выборку окружностей, содержащих XData для приложения с именем MY_APP

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
 
[CommandMethod("FilterXdata")]
public static void FilterXdata()
{
    // Get the current document editor
    Editor acDocEd = Application.DocumentManager.MdiActiveDocument.Editor;

    // Create a TypedValue array to define the filter criteria
    TypedValue[] acTypValAr = new TypedValue[2];
    acTypValAr.SetValue(new TypedValue((int)DxfCode.Start, "Circle"), 0);
    acTypValAr.SetValue(new TypedValue((int)DxfCode.ExtendedDataRegAppName, 
                                        "MY_APP"), 1);

    // Assign the filter criteria to a SelectionFilter object
    SelectionFilter acSelFtr = new SelectionFilter(acTypValAr);

    // Request for objects to be selected in the drawing area
    PromptSelectionResult acSSPrompt;
    acSSPrompt = acDocEd.GetSelection(acSelFtr);

    // If the prompt status is OK, objects were selected
    if (acSSPrompt.Status == PromptStatus.OK)
    {
        SelectionSet acSSet = acSSPrompt.Value;

        Application.ShowAlertDialog("Number of objects selected: " +
                                    acSSet.Count.ToString());
    }
    else
    {
        Application.ShowAlertDialog("Number of objects selected: 0");
    }
}

О типе данных ResultBuffer

ResultBuffer — это класс, который отражает структуру resbuf, определенную в ObjectARX. Структура resbuf предоставляет собой контейнер для данных, специфичных для AutoCAD.

Объект класса Autodesk.AutoCAD.DatabaseServices.ResultBuffer используется практически так же, как и нативный resbuf. Вы определяете ResultBuffer и заполняете его последовательностью пар данных. Каждая пара содержит описание типа данных и значение. В управляемом .NET API эти пары данных являются экземплярами класса Autodesk.AutoCAD.DatabaseServices.TypedValue. Этот служебный класс выполняет ту же функцию, что и члены restype и resval структуры resbuf.

ResultBuffer используется в Xrecord и XData.

Свойство TypedValue.TypeCode представляет собой 16-разрядное целое значение, которое указывает на тип данных свойства TypedValue.Value. Допустимые значения TypeCode зависят от конкретного использования экземпляра ResultBuffer. Например, значения TypeCode, подходящие для определения XRecord, не обязательно подходят для xdata. Перечисление Autodesk.AutoCAD.DatabaseServices.DxfCode определяет коды, которые описывают весь диапазон возможных типов данных ResultBuffer.

Свойство TypedValue.Value сопоставляется экземпляру System.Object и теоретически может содержать данные любого типа. Однако данные Value должны соответствовать типу, указанному в TypeCode, чтобы гарантировать получение корректного результата.

Вы можете предварительно заполнить ResultBuffer, передавая массив объектов TypedValue в его конструктор, или создать пустой ResultBuffer и позже вызвать метод ResultBuffer.Add(), чтобы добавить новые объекты TypedValue. В следующем примере показано использование конструктора ResultBuffer:

using (Xrecord rec = new Xrecord())
{
    rec.Data = new ResultBuffer(
        new TypedValue(Convert.ToInt32(DxfCode.Text), "This is a test"),
        new TypedValue(Convert.ToInt32(DxfCode.Int8), 0),
        new TypedValue(Convert.ToInt32(DxfCode.Int16), 1),
        new TypedValue(Convert.ToInt32(DxfCode.Int32), 2),
        new TypedValue(Convert.ToInt32(DxfCode.HardPointerId), db.BlockTableId),
        new TypedValue(Convert.ToInt32(DxfCode.BinaryChunk), new byte[] {0, 1, 2, 3, 4}),
        new TypedValue(Convert.ToInt32(DxfCode.ArbitraryHandle), db.BlockTableId.Handle),
        new TypedValue(Convert.ToInt32(DxfCode.UcsOrg),
        new Point3d(0, 0, 0)));
}

Редактирование объектов. Общие принципы

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

Работа с неграфическими именованными элементами

Помимо графических объектов, используемых AutoCAD, в базе данных чертежа хранятся несколько видов неграфических объектов. Эти объекты имеют специальные обозначения: например, блоки, слои, группы и стили размеров имеют заданные им имена и в большинстве случаев могут быть переименованы. Имена записей таблицы символов (то, чем являются объекты внутри AutoCAD) видны в пользовательском интерфейсе AutoCAD, а их идентификаторы используются для ссылки на данный неграфический объект в большинстве случаев в AutoCAD.NET API. Например, идентификатор объекта (ObjectId) LayerTableRecord (определение Слоя из таблицы Слоёв) присваивается свойству LayerId графического объекта, в то время как его можно получить из таблицы Слоев, используя имя слоя, к которому вы хотите доступ.

Удаление несвязанных элементов

Несвязанные именованные объекты можно удалить из базы данных в любое время. Невозможно удалить именованные объекты, на которые ссылаются другие объекты. Например, на определение Шрифта может ссылаться стиль текста, а на слой могут ссылаться объекты на этом слое. Удаление объектов уменьшает размер файла чертежа при сохранении на диск. Несвязанные объекты удаляются из базы данных чертежа с помощью метода Purge. Метод Purge требует список объектов, которые необходимо удалить, в виде ObjectIdCollection или ObjectIdGraph. Объекты ObjectIdCollection или ObjectIdGraph, переданные в метод Purge, возращают после его отработки те идентификаторы, связанные объекты с которыми, которые можно безопасно удалить самостоятельно (см. пример ниже для удаления пустых слоёв).

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

[CommandMethod("PurgeUnreferencedLayers")]
public static void PurgeUnreferencedLayers()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Layer table for read
        LayerTable acLyrTbl;
        acLyrTbl = acTrans.GetObject(acCurDb.LayerTableId,
                                        OpenMode.ForRead) as LayerTable;

        // Create an ObjectIdCollection to hold the object ids for each table record
        ObjectIdCollection acObjIdColl = new ObjectIdCollection();

        // Step through each layer and add iterator to the ObjectIdCollection
        foreach (ObjectId acObjId in acLyrTbl)
        {
            acObjIdColl.Add(acObjId);
        }

        // Remove the layers that are in use and return the ones that can be erased
        acCurDb.Purge(acObjIdColl);

        // Step through the returned ObjectIdCollection
        // and erase each unreferenced layer
        foreach (ObjectId acObjId in acObjIdColl)
        {
            SymbolTableRecord acSymTblRec;
            acSymTblRec = acTrans.GetObject(acObjId,
                                            OpenMode.ForWrite) as SymbolTableRecord;

            try
            {
                // Erase the unreferenced layer
                acSymTblRec.Erase(true);
            }
            catch (Autodesk.AutoCAD.Runtime.Exception Ex)
            {
                // Layer could not be deleted
                Application.ShowAlertDialog("Error:\n" + Ex.Message);
            }
        }

        // Commit the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Переименовывание объектов

Возможно переименовывать объекты, чтобы сохранить структуру имен или избежать конфликтов с именами объектов из других вставленных или прикрепленных внешними ссылками чертежах. Свойство Name используется для получения текущего имени или изменения имени именованного объекта. Вы можете переименовать любой именованный объект, за исключением тех, которые зарезервированы AutoCAD, например, слой 0 или тип линии CONTINUOUS. Имена могут содержать до 255 символов. Помимо букв и цифр, имена могут содержать пробелы (хотя AutoCAD удаляет пробелы, которые появляются непосредственно перед и после имени) и любые специальные символы, не используемые Microsoft® Windows® или AutoCADдля других целей. Специальные символы, которые нельзя использовать:

  • символы меньше и больше (< >);
  • косые и обратные косые черты (/ \);
  • кавычки (");
  • двоеточие (:);
  • точка с запятой (;);
  • вопросительный знак (?);
  • запятая (,);
  • звездочка (*);
  • вертикальная черта (|);
  • знак равенства (=);
  • одинарные кавычки ('). Также нельзя использовать специальные символы, созданные с помощью шрифтов Unicode (эмоджи и т.д.). В коде ниже создается копия слоя "0" и переименовывается в "MyLayer"
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

[CommandMethod("RenameLayer")]
public static void RenameLayer()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Returns the layer table for the current database
        LayerTable acLyrTbl;
        acLyrTbl = acTrans.GetObject(acCurDb.LayerTableId,
                                        OpenMode.ForWrite) as LayerTable;

        // Clone layer 0 (copy it and its properties) as a new layer
        LayerTableRecord acLyrTblRec;
        acLyrTblRec = acTrans.GetObject(acLyrTbl["0"],
                                        OpenMode.ForRead).Clone() as LayerTableRecord;

        // Change the name of the cloned layer
        acLyrTblRec.Name = "MyLayer";

        // Add the cloned layer to the Layer table and transaction
        acLyrTbl.Add(acLyrTblRec);
        acTrans.AddNewlyCreatedDBObject(acLyrTblRec, true);

        // Save changes and dispose of the transaction
        acTrans.Commit();
    }
}

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

Удаление объектов

С помощью метода Erase можно удалять графические и неграфические объекты.

Внимание: Хотя многие неграфические объекты, такие как таблица слоев (LayerTable) и записи таблицы блоков (BlockTableRecord), имеют метод Erase, его не следует вызывать. Если вызвать Erase для одного из этих объектов, произойдет ошибка. Ниже приведен пример создания и удаления полилинии

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

[CommandMethod("EraseObject")]
public static void EraseObject()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a lightweight polyline
        using (Polyline acPoly = new Polyline())
        {
            acPoly.AddVertexAt(0, new Point2d(2, 4), 0, 0, 0);
            acPoly.AddVertexAt(1, new Point2d(4, 2), 0, 0, 0);
            acPoly.AddVertexAt(2, new Point2d(6, 4), 0, 0, 0);

            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acPoly);
            acTrans.AddNewlyCreatedDBObject(acPoly, true);

            // Update the display and display an alert message
            acDoc.Editor.Regen();
            Application.ShowAlertDialog("Erase the newly added polyline.");

            // Erase the polyline from the drawing
            acPoly.Erase(true);
        }

        // Save the new object to the database
        acTrans.Commit();
    }
}

Копирование объектов

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

Копирование объекта

Чтобы скопировать объект, вызовите функцию Clone у копируемого объекта (внимание: копируемый объектдолжен быть в режиме OpenMode.ForWrite). Этот метод создаст новый объект, который является дубликатом исходного объекта. После создания дубликата объекта вы можете внести в него какие-либо изменения перед добавлением в базу данных модели. Если не производить каких-либо изменений, то скопированный объект будет находиться и иметь те же свойства, что и исходный объект.

Копирование одного объекта

Код ниже содержит пример копирования созданной окружности

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("SingleCopy")]
public static void SingleCopy()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a circle that is at 2,3 with a radius of 4.25
        using (Circle acCirc = new Circle())
        {
            acCirc.Center = new Point3d(2, 3, 0);
            acCirc.Radius = 4.25;

            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acCirc);
            acTrans.AddNewlyCreatedDBObject(acCirc, true);

            // Create a copy of the circle and change its radius
            Circle acCircClone = acCirc.Clone() as Circle;
            acCircClone.Radius = 1;

            // Add the cloned circle
            acBlkTblRec.AppendEntity(acCircClone);
            acTrans.AddNewlyCreatedDBObject(acCircClone, true);
        }

        // Save the new object to the database
        acTrans.Commit();
    }
}

Копирование нескольких объектов

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

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("MultipleCopy")]
public static void MultipleCopy()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a circle that is at (0,0,0) with a radius of 5
        using (Circle acCirc1 = new Circle())
        {
            acCirc1.Center = new Point3d(0, 0, 0);
            acCirc1.Radius = 5;

            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acCirc1);
            acTrans.AddNewlyCreatedDBObject(acCirc1, true);

            // Create a circle that is at (0,0,0) with a radius of 7
            using (Circle acCirc2 = new Circle())
            {
                acCirc2.Center = new Point3d(0, 0, 0);
                acCirc2.Radius = 7;

                // Add the new object to the block table record and the transaction
                acBlkTblRec.AppendEntity(acCirc2);
                acTrans.AddNewlyCreatedDBObject(acCirc2, true);

                // Add all the objects to clone
                DBObjectCollection acDBObjColl = new DBObjectCollection();
                acDBObjColl.Add(acCirc1);
                acDBObjColl.Add(acCirc2);

                foreach (Entity acEnt in acDBObjColl)
                {
                    Entity acEntClone;
                    acEntClone = acEnt.Clone() as Entity;
                    acEntClone.ColorIndex = 1;

                    // Create a matrix and move each copied entity 15 units
                    acEntClone.TransformBy(Matrix3d.Displacement(new Vector3d(15, 0, 0)));

                    // Add the cloned object
                    acBlkTblRec.AppendEntity(acEntClone);
                    acTrans.AddNewlyCreatedDBObject(acEntClone, true);
                }
            }
        }

        // Save the new object to the database
        acTrans.Commit();
    }
}

Копирование объектов между разными Database

Вы можете копировать объекты между двумя различными базами данных (объектами класса Database). Функция Clone используется для копирования объектов в пределах одной базы данных, а метод WblockCloneObjects — для копирования объектов из одной базы данных в другую. Метод WblockCloneObjects является членом объекта Database. Метод WblockCloneObjects требует следующих параметров:

  • ObjectIdCollection : Перечень идентификаторов копируемых объектов;
  • ObjectId : Идентификатор нового родительского объекта, куда объекты будут копироваться;
  • IdMapping : Сопоставление текущих идентификаторов объектов с их новыми идентификаторами (заполнятся после работы метода WblockCloneObjects);
  • DuplicateRecordCloning : Задаёт правило обработки дублирующихся объектов (в виде enum Autodesk.AutoCAD.DatabaseServices.DuplicateRecordCloning);
  • deferTranslation : Флаг, отложить ли заполнение IdMapping идентификаторами для новых объектов (по умолчанию false - т.е. заполнить сразу); Код ниже содержит операцию создания двух новых окружностей, затем с помощью метода WblockCloneObjects они копируются в новый чертёж, который создается автоматически на базе стандартного шаблона
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
 
[CommandMethod("CopyObjectsBetweenDatabases", CommandFlags.Session)]
public static void CopyObjectsBetweenDatabases()
{
    ObjectIdCollection acObjIdColl = new ObjectIdCollection();

    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Lock the current document
    using (DocumentLock acLckDocCur = acDoc.LockDocument())
    {
        // Start a transaction
        using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
        {
            // Open the Block table record for read
            BlockTable acBlkTbl;
            acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                            OpenMode.ForRead) as BlockTable;

            // Open the Block table record Model space for write
            BlockTableRecord acBlkTblRec;
            acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                            OpenMode.ForWrite) as BlockTableRecord;

            // Create a circle that is at (0,0,0) with a radius of 5
            using (Circle acCirc1 = new Circle())
            {
                acCirc1.Center = new Point3d(0, 0, 0);
                acCirc1.Radius = 5;

                // Add the new object to the block table record and the transaction
                acBlkTblRec.AppendEntity(acCirc1);
                acTrans.AddNewlyCreatedDBObject(acCirc1, true);

                // Create a circle that is at (0,0,0) with a radius of 7
                using (Circle acCirc2 = new Circle())
                {
                    acCirc2.Center = new Point3d(0, 0, 0);
                    acCirc2.Radius = 7;

                    // Add the new object to the block table record and the transaction
                    acBlkTblRec.AppendEntity(acCirc2);
                    acTrans.AddNewlyCreatedDBObject(acCirc2, true);

                    // Add all the objects to copy to the new document
                    acObjIdColl = new ObjectIdCollection();
                    acObjIdColl.Add(acCirc1.ObjectId);
                    acObjIdColl.Add(acCirc2.ObjectId);
                }
            }

            // Save the new objects to the database
            acTrans.Commit();
        }

        // Unlock the document
    }

    // Change the file and path to match a drawing template on your workstation
    string sLocalRoot = Application.GetSystemVariable("LOCALROOTPREFIX") as string;
    string sTemplatePath = sLocalRoot + "Template\\acad.dwt";

    // Create a new drawing to copy the objects to
    DocumentCollection acDocMgr = Application.DocumentManager;
    Document acNewDoc = acDocMgr.Add(sTemplatePath);
    Database acDbNewDoc = acNewDoc.Database;

    // Lock the new document
    using (DocumentLock acLckDoc = acNewDoc.LockDocument())
    {
        // Start a transaction in the new database
        using (Transaction acTrans = acDbNewDoc.TransactionManager.StartTransaction())
        {
            // Open the Block table for read
            BlockTable acBlkTblNewDoc;
            acBlkTblNewDoc = acTrans.GetObject(acDbNewDoc.BlockTableId,
                                                OpenMode.ForRead) as BlockTable;

            // Open the Block table record Model space for read
            BlockTableRecord acBlkTblRecNewDoc;
            acBlkTblRecNewDoc = acTrans.GetObject(acBlkTblNewDoc[BlockTableRecord.ModelSpace],
                                                    OpenMode.ForRead) as BlockTableRecord;

            // Clone the objects to the new database
            IdMapping acIdMap = new IdMapping();
            acCurDb.WblockCloneObjects(acObjIdColl, acBlkTblRecNewDoc.ObjectId, acIdMap,
                                        DuplicateRecordCloning.Ignore, false);

            // Save the copied objects to the database
            acTrans.Commit();
        }

        // Unlock the document
    }

    // Set the new document current
    acDocMgr.MdiActiveDocument = acNewDoc;
}

Смещение объектов (подобие)

Операция смещения (подобия), примененная к объекту, создает новый объект на заданном расстоянии от данного. Вы можете применять операцию смещения к дугам, окружностям, эллипсам, отрезкам, плоским полилиниям, сплайнам, прямым.

Для того, чтобы получить смещённый объект, вызовите метод GetOffsetCurves у исходного объекта. Метод ожидает положительное или отрицательное "расстояние смещения". Если значение отрицательное, то сдвиг идёт в сторону образования "меньшей" фигуры с меньшими радиусами кривых (в отношении контура сдвиг внутрь); в противном случае (отрицательная величина) -- в сторону увеличения фигуры (в отношении контура сдвиг наружу).

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

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

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

Создание подобия для полилинии

Код ниже создает плоскую полилинию и выполняет её смещение на заданное расстояние в 0.25 единиц

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
 
[CommandMethod("OffsetObject")]
public static void OffsetObject()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a lightweight polyline
        using (Polyline acPoly = new Polyline())
        {
            acPoly.AddVertexAt(0, new Point2d(1, 1), 0, 0, 0);
            acPoly.AddVertexAt(1, new Point2d(1, 2), 0, 0, 0);
            acPoly.AddVertexAt(2, new Point2d(2, 2), 0, 0, 0);
            acPoly.AddVertexAt(3, new Point2d(3, 2), 0, 0, 0);
            acPoly.AddVertexAt(4, new Point2d(4, 4), 0, 0, 0);
            acPoly.AddVertexAt(5, new Point2d(4, 1), 0, 0, 0);

            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acPoly);
            acTrans.AddNewlyCreatedDBObject(acPoly, true);

            // Offset the polyline a given distance
            DBObjectCollection acDbObjColl = acPoly.GetOffsetCurves(0.25);

            // Step through the new objects created
            foreach (Entity acEnt in acDbObjColl)
            {
                // Add each offset object
                acBlkTblRec.AppendEntity(acEnt);
                acTrans.AddNewlyCreatedDBObject(acEnt, true);
            }
        }

        // Save the new objects to the database
        acTrans.Commit();
    }
}

Трансформация координат

Вы можете применять к объекту различные операции преобразования координат: перемещение, масштабирование, поворот, отзеркаливание с помощью матрицы преобразования 4 х 4, представленной объектом Matrix3d и методом TransformBy. Вы также можете использовать метод GetTransformedCopy для создания копии объекта Matrix3d, а затем применить преобразование координат для данной копии. Объект Matrix3d является частью пространства имен Geometry.

Первые 3 колонки матрицы задают масштаб и поворот. Четвертая колонка матрицы задает вектор перемещения. Ниже приведена табличная запись матрицы, первая буква R (Rotation) - угол поворота, T (Translation) - вектор перемещения.

R00 R01 R02 T0
R10 R11 R12 T1
R20 R21 R22 T2
0   0   0   1

Чтобы применить к объекту трансформацию сперва инициализируйте объект Matrix3d (например, используя массив чисел типа double, единичную матрицу Matrix3d.Identity или с матрицы, которая представляет систему координат World или систему координат пользователя). После инициализации вы можете использовать функции объекта Matrix3d для задания масштабирования, поворота или смещения матрицы.

После завершения создания матрицы преобразования примените её к объекту с помощью метода TransformBy.

Пример матрицы трансформации для простого поворота

Пример ниже описывает матрицу трансформации координат для поворота на 90 градусов вокруг точки (0, 0, 0).

0.0   -1.0    0.0   0.0
1.0   0.0     0.0   0.0
0.0   0.0     1.0   0.0
0.0   0.0     0.0   1.0

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

// Задание матрицы напрямую
double[] dMatrix = new double[16];
dMatrix[0] = 0.0;
dMatrix[1] = -1.0;
dMatrix[2] = 0.0;
dMatrix[3] = 0.0;
dMatrix[4] = 1.0;
dMatrix[5] = 0.0;
dMatrix[6] = 0.0;
dMatrix[7] = 0.0;
dMatrix[8] = 0.0;
dMatrix[9] = 0.0;
dMatrix[10] = 1.0;
dMatrix[11] = 0.0;
dMatrix[12] = 0.0;
dMatrix[13] = 0.0;
dMatrix[14] = 0.0;
dMatrix[15] = 1.0;
Matrix3d acMat3d = new Matrix3d(dMatrix);
// Задание матрицы через специальные методы
Document acDoc;
Matrix3d curUCSMatrix = acDoc.Editor.CurrentUserCoordinateSystem;
CoordinateSystem3d curUCS = curUCSMatrix.CoordinateSystem3d;
Matrix3d acMat3d = new Matrix3d();
acMat3d = Matrix3d.Rotation(Math.PI / 2,
                            curUCS.Zaxis,
                            new Point3d(0, 0, 0));

Примеры матриц трансформации для составных преобразований

Матрица поворота: 45 градусов вокруг точки (5, 5, 0)

0.707107   -0.707107  0.0   5.0
0.707107   0.707107   0.0  -2.071068
0.0        0.0        1.0   0.0
0.0        0.0        0.0   1.0

Матрица сдвига: перемещение объекта на вектор (10, 10, 0)

1.0   0.0   0.0   10.0
0.0   1.0   0.0   10.0
0.0   0.0   1.0   0.0
0.0   0.0   0.0   1.0

Матрица масштабирования: увеличение масштаба в 10 раз по осям X, Y вокруг точки (0, 0, 0)

10.0   0.0    0.0   0.0
0.0    10.0   0.0   0.0
0.0    0.0    1.0   0.0
0.0    0.0    0.0   1.0

Матрица масштабирования: увеличение масштаба в 10 раз по всем осям вокруг точки (2, 2, 0)

10.0   0.0    0.0    -18.0
0.0    10.0   0.0    -18.0
0.0    0.0    10.0   0.0
0.0    0.0    0.0    1.0

Операция сдвига

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

Для перемещения объекта используйте функцию Displacement матрицы преобразования. Эта функция требует в качестве входных данных объект Vector3d. Если он вам не известен, то вы можете определить его, воспользовавшись методом GetVectorTo для известной точки Point3d, в которую необходимо переместить объект. Вектор смещения указывает, на какое расстояние и в каком направлении должен быть перемещен данный объект.

Сдвиг окружности на заданный вектор

Код ниже сдвигает созданную окружность в точке (2, 2, 0) на 2 единицы вдоль оси X.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("MoveObject")]
public static void MoveObject()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a circle that is at 2,2 with a radius of 0.5
        using (Circle acCirc = new Circle())
        {
            acCirc.Center = new Point3d(2, 2, 0);
            acCirc.Radius = 0.5;

            // Create a matrix and move the circle using a vector from (0,0,0) to (2,0,0)
            Point3d acPt3d = new Point3d(0, 0, 0);
            Vector3d acVec3d = acPt3d.GetVectorTo(new Point3d(2, 0, 0));

            acCirc.TransformBy(Matrix3d.Displacement(acVec3d));

            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acCirc);
            acTrans.AddNewlyCreatedDBObject(acCirc, true);
        }

        // Save the new objects to the database
        acTrans.Commit();
    }
}

Операция поворота

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

Чтобы повернуть объект, используйте функцию Rotation матрицы преобразования. Эта функция требует угла поворота, выраженного в радианах, оси поворота и базовой точки. Ось поворота должна быть выражена как объект Vector3d, а базовая точка — как объект Point3d. Этот угол определяет, насколько объект поворачивается вокруг базовой точки относительно своего текущего местоположения. Положительные значения угла соответствуют повороту против часовой стрелки.

Поворот полилинии вокруг базовой точки

Код ниже поворачивает плоскую полилинию на угол 45 градусов вокруг точки (4, 4.25, 0).

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("RotateObject")]
public static void RotateObject()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;
    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                     OpenMode.ForRead) as BlockTable;
        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;
        // Create a lightweight polyline
        using (Polyline acPoly = new Polyline())
        {
            acPoly.AddVertexAt(0, new Point2d(1, 2), 0, 0, 0);
            acPoly.AddVertexAt(1, new Point2d(1, 3), 0, 0, 0);
            acPoly.AddVertexAt(2, new Point2d(2, 3), 0, 0, 0);
            acPoly.AddVertexAt(3, new Point2d(3, 3), 0, 0, 0);
            acPoly.AddVertexAt(4, new Point2d(4, 4), 0, 0, 0);
            acPoly.AddVertexAt(5, new Point2d(4, 2), 0, 0, 0);
            // Close the polyline
            acPoly.Closed = true;
            Matrix3d curUCSMatrix = acDoc.Editor.CurrentUserCoordinateSystem;
            CoordinateSystem3d curUCS = curUCSMatrix.CoordinateSystem3d;
            // Rotate the polyline 45 degrees, around the Z-axis of the current UCS
            // using a base point of (4,4.25,0)
            acPoly.TransformBy(Matrix3d.Rotation(0.7854,
                                                 curUCS.Zaxis,
                                                 new Point3d(4, 4.25, 0)));
            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acPoly);
            acTrans.AddNewlyCreatedDBObject(acPoly, true);
        }
        // Save the new objects to the database
        acTrans.Commit();
    }
}

Операция отзеркаливания

Зеркальное отражение переворачивает объект по оси или линии зеркального отражения.

Зеркальное отражение можно применять ко всем объектам чертежа. Для зеркального отражения объекта используйте функцию Mirroring матрицы преобразования. Для этой функции требуется объект Point3d, Plane (плоскость, для случая трехмерного отзеркаливания) или Line3d для задания линии зеркального отражения. Поскольку зеркальное отражение выполняется с помощью матрицы преобразования, оно приводит к изменению текущего объекта. Если вы хотите сохранить исходный объект, вам необходимо сначала создать копию объекта, а затем выполнить его зеркальное отражение.

Для управления свойствами отражения текстовых объектов используйте системную переменную MIRRTEXT. По умолчанию для MIRRTEXT установлено значение Вкл. (1), что приводит к зеркальному отражению текстовых объектов, как и любых других объектов. Когда для MIRRTEXT установлено значение Выкл. (0), текст не отражается. Для получения и задания параметра MIRRTEXT используйте методы GetSystemVariable и SetSystemVariable соответственно у статического класса Application.

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

Отзеркаливание полилинии вдоль оси

Код ниже создает полилинию, делает её копию и производит отражение вдоль отрезка, заданной точками (0, 4.25, 0) и (4, 4.25, 0). Отзеркаленная копия полилинии окрашивается в голубой цвет.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
 
[CommandMethod("MirrorObject")]
public static void MirrorObject()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a lightweight polyline
        using (Polyline acPoly = new Polyline())
        {
            acPoly.AddVertexAt(0, new Point2d(1, 1), 0, 0, 0);
            acPoly.AddVertexAt(1, new Point2d(1, 2), 0, 0, 0);
            acPoly.AddVertexAt(2, new Point2d(2, 2), 0, 0, 0);
            acPoly.AddVertexAt(3, new Point2d(3, 2), 0, 0, 0);
            acPoly.AddVertexAt(4, new Point2d(4, 4), 0, 0, 0);
            acPoly.AddVertexAt(5, new Point2d(4, 1), 0, 0, 0);

            // Create a bulge of -2 at vertex 1
            acPoly.SetBulgeAt(1, -2);

            // Close the polyline
            acPoly.Closed = true;

            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acPoly);
            acTrans.AddNewlyCreatedDBObject(acPoly, true);

            // Create a copy of the original polyline
            Polyline acPolyMirCopy = acPoly.Clone() as Polyline;
            acPolyMirCopy.ColorIndex = 5;

            // Define the mirror line
            Point3d acPtFrom = new Point3d(0, 4.25, 0);
            Point3d acPtTo = new Point3d(4, 4.25, 0);
            Line3d acLine3d = new Line3d(acPtFrom, acPtTo);

            // Mirror the polyline across the X axis
            acPolyMirCopy.TransformBy(Matrix3d.Mirroring(acLine3d));

            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acPolyMirCopy);
            acTrans.AddNewlyCreatedDBObject(acPolyMirCopy, true);
        }

        // Save the new objects to the database
        acTrans.Commit();
    }
}

Операция масштабирования

Масштабирование объекта осуществляется путем указания базовой точки и коэффициента масштабирования в текущих единицах чертежа.

Можно применять масштабирование ко всем объектам чертежа, а также к определениям атрибутов. Для масштабирования объекта используйте функцию Scaling матрицы преобразования. Эта функция требует числового значения коэффициента масштабирования объекта и объекта Point3d для задания базовой точки. Функция Scaling масштабирует объект одинаково по направлениям X, Y и Z. Размеры объекта умножаются на коэффициент масштабирования. Коэффициент масштабирования больше 1 увеличивает объект. Коэффициент масштабирования от 0 до 1 уменьшает объект.

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

Масштабирование полилинии

Код ниже уменьшает созданную полилинию в 4 раза (коэффициент масштабирования = 0.25) относительно базовой точки (4,4.25,0).

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("ScaleObject")]
public static void ScaleObject()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                     OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a lightweight polyline
        using (Polyline acPoly = new Polyline())
        {
            acPoly.AddVertexAt(0, new Point2d(1, 2), 0, 0, 0);
            acPoly.AddVertexAt(1, new Point2d(1, 3), 0, 0, 0);
            acPoly.AddVertexAt(2, new Point2d(2, 3), 0, 0, 0);
            acPoly.AddVertexAt(3, new Point2d(3, 3), 0, 0, 0);
            acPoly.AddVertexAt(4, new Point2d(4, 4), 0, 0, 0);
            acPoly.AddVertexAt(5, new Point2d(4, 2), 0, 0, 0);

            // Close the polyline
            acPoly.Closed = true;

            // Reduce the object by a factor of 0.5 
            // using a base point of (4,4.25,0)
            acPoly.TransformBy(Matrix3d.Scaling(0.5, new Point3d(4, 4.25, 0)));

            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acPoly);
            acTrans.AddNewlyCreatedDBObject(acPoly, true);
        }

        // Save the new objects to the database
        acTrans.Commit();
    }
}

Массивы объектов

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

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

Ниже приведена основная логика для каждого из типов массивов.

Полярный массив

Скопируйте объект, который необходимо расположить в массиве, и переместите в позицию, соответствующую текущему полярному углу. Код ниже формирует полярный массив на основе окружности, в массиве 4 элемента, заполнение на 180 градусов относительно базовой точки (4, 4, 0)

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
 
static Point2d PolarPoints(Point2d pPt, double dAng, double dDist)
{
  return new Point2d(pPt.X + dDist * Math.Cos(dAng),
                     pPt.Y + dDist * Math.Sin(dAng));
}
 
[CommandMethod("PolarArrayObject")]
public static void PolarArrayObject()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table record for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a circle that is at 2,2 with a radius of 1
        using (Circle acCirc = new Circle())
        {
            acCirc.Center = new Point3d(2, 2, 0);
            acCirc.Radius = 1;

            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acCirc);
            acTrans.AddNewlyCreatedDBObject(acCirc, true);

            // Create a 4 object polar array that goes a 180
            int nCount = 1;

            // Set a value in radians for 60 degrees
            double dAng = 1.0472;

            // Use (4,4,0) as the base point for the array
            Point2d acPt2dArrayBase = new Point2d(4, 4);

            while (nCount < 4)
            {
                Entity acEntClone = acCirc.Clone() as Entity;

                Extents3d acExts;
                Point2d acPtObjBase;

                // Typically the upper-left corner of an object's extents is used
                // for the point on the object to be arrayed unless it is
                // an object like a circle.
                Circle acCircArrObj = acEntClone as Circle;

                if (acCircArrObj != null)
                {
                    acPtObjBase = new Point2d(acCircArrObj.Center.X,
                                                acCircArrObj.Center.Y);
                }
                else
                {
                    acExts = acEntClone.Bounds.GetValueOrDefault();
                    acPtObjBase = new Point2d(acExts.MinPoint.X,
                                                acExts.MaxPoint.Y);
                }

                double dDist = acPt2dArrayBase.GetDistanceTo(acPtObjBase);
                double dAngFromX = acPt2dArrayBase.GetVectorTo(acPtObjBase).Angle;

                Point2d acPt2dTo = PolarPoints(acPt2dArrayBase,
                                                (nCount * dAng) + dAngFromX,
                                                dDist);

                Vector2d acVec2d = acPtObjBase.GetVectorTo(acPt2dTo);
                Vector3d acVec3d = new Vector3d(acVec2d.X, acVec2d.Y, 0);
                acEntClone.TransformBy(Matrix3d.Displacement(acVec3d));

                /*
                // The following code demonstrates how to rotate each object like
                // the ARRAY command does.
                acExts = acEntClone.Bounds.GetValueOrDefault();
                acPtObjBase = new Point2d(acExts.MinPoint.X,
                                            acExts.MaxPoint.Y);
                
                // Rotate the cloned entity around its upper-left extents point
                Matrix3d curUCSMatrix = acDoc.Editor.CurrentUserCoordinateSystem;
                CoordinateSystem3d curUCS = curUCSMatrix.CoordinateSystem3d;
                acEntClone.TransformBy(Matrix3d.Rotation(nCount * dAng,
                                                            curUCS.Zaxis,
                                                            new Point3d(acPtObjBase.X,
                                                                        acPtObjBase.Y, 0)));
                */

                acBlkTblRec.AppendEntity(acEntClone);
                acTrans.AddNewlyCreatedDBObject(acEntClone, true);

                nCount = nCount + 1;
            }
        }

        // Save the new objects to the database
        acTrans.Commit();
    }
}

Прямоугольный массив

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

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
 
static Point2d PolarPoints(Point2d pPt, double dAng, double dDist)
{
  return new Point2d(pPt.X + dDist * Math.Cos(dAng),
                     pPt.Y + dDist * Math.Sin(dAng));
}
 
[CommandMethod("RectangularArrayObject")]
public static void RectangularArrayObject()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table record for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a circle that is at 2,2 with a radius of 0.5
        using (Circle acCirc = new Circle())
        {
            acCirc.Center = new Point3d(2, 2, 0);
            acCirc.Radius = 0.5;

            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acCirc);
            acTrans.AddNewlyCreatedDBObject(acCirc, true);

            // Create a rectangular array with 5 rows and 5 columns
            int nRows = 5;
            int nColumns = 5;

            // Set the row and column offsets along with the base array angle
            double dRowOffset = 1;
            double dColumnOffset = 1;
            double dArrayAng = 0;

            // Get the angle from X for the current UCS 
            Matrix3d curUCSMatrix = acDoc.Editor.CurrentUserCoordinateSystem;
            CoordinateSystem3d curUCS = curUCSMatrix.CoordinateSystem3d;
            Vector2d acVec2dAng = new Vector2d(curUCS.Xaxis.X,
                                                curUCS.Xaxis.Y);

            // If the UCS is rotated, adjust the array angle accordingly
            dArrayAng = dArrayAng + acVec2dAng.Angle;

            // Use the upper-left corner of the objects extents for the array base point
            Extents3d acExts = acCirc.Bounds.GetValueOrDefault();
            Point2d acPt2dArrayBase = new Point2d(acExts.MinPoint.X,
                                                    acExts.MaxPoint.Y);

            // Track the objects created for each column
            DBObjectCollection acDBObjCollCols = new DBObjectCollection();
            acDBObjCollCols.Add(acCirc);

            // Create the number of objects for the first column
            int nColumnsCount = 1;
            while (nColumns > nColumnsCount)
            {
                Entity acEntClone = acCirc.Clone() as Entity;
                acDBObjCollCols.Add(acEntClone);

                // Caclucate the new point for the copied object (move)
                Point2d acPt2dTo = PolarPoints(acPt2dArrayBase,
                                                dArrayAng,
                                                dColumnOffset * nColumnsCount);

                Vector2d acVec2d = acPt2dArrayBase.GetVectorTo(acPt2dTo);
                Vector3d acVec3d = new Vector3d(acVec2d.X, acVec2d.Y, 0);
                acEntClone.TransformBy(Matrix3d.Displacement(acVec3d));

                acBlkTblRec.AppendEntity(acEntClone);
                acTrans.AddNewlyCreatedDBObject(acEntClone, true);

                nColumnsCount = nColumnsCount + 1;
            }

            // Set a value in radians for 90 degrees
            double dAng = Math.PI / 2;

            // Track the objects created for each row and column
            DBObjectCollection acDBObjCollLvls = new DBObjectCollection();

            foreach (DBObject acObj in acDBObjCollCols)
            {
                acDBObjCollLvls.Add(acObj);
            }

            // Create the number of objects for each row
            foreach (Entity acEnt in acDBObjCollCols)
            {
                int nRowsCount = 1;

                while (nRows > nRowsCount)
                {
                    Entity acEntClone = acEnt.Clone() as Entity;
                    acDBObjCollLvls.Add(acEntClone);

                    // Caclucate the new point for the copied object (move)
                    Point2d acPt2dTo = PolarPoints(acPt2dArrayBase,
                                                    dArrayAng + dAng,
                                                    dRowOffset * nRowsCount);

                    Vector2d acVec2d = acPt2dArrayBase.GetVectorTo(acPt2dTo);
                    Vector3d acVec3d = new Vector3d(acVec2d.X, acVec2d.Y, 0);
                    acEntClone.TransformBy(Matrix3d.Displacement(acVec3d));

                    acBlkTblRec.AppendEntity(acEntClone);
                    acTrans.AddNewlyCreatedDBObject(acEntClone, true);

                    nRowsCount = nRowsCount + 1;
                }
            }
        }

        // Save the new objects to the database
        acTrans.Commit();
    }
}

Обрезка и удлинение объектов

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

Для этого используйте соответствующие свойства для классов, описывающих объект, например, чтобы удлинить линию, просто измените свойство StartPoint или EndPoint. Чтобы изменить угол дуги, измените её свойство StartAngle или EndAngle. После изменения свойств объекта может потребоваться перерисовка окна чертежа, чтобы увидеть изменения.

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

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
 
[CommandMethod("ExtendObject")]
public static void ExtendObject()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a line that starts at (4,4,0) and ends at (7,7,0)
        using (Line acLine = new Line(new Point3d(4, 4, 0),
                                new Point3d(7, 7, 0)))
        {

            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acLine);
            acTrans.AddNewlyCreatedDBObject(acLine, true);

            // Update the display and diaplay a message box
            acDoc.Editor.Regen();
            Application.ShowAlertDialog("Before extend");

            // Double the length of the line
            acLine.EndPoint = acLine.EndPoint + acLine.Delta;
        }

        // Save the new object to the database
        acTrans.Commit();
    }
}

Разбивка объектов

Операция разбивки (расчленения) объекта с помощью функции Explode преобразует его в составные части при том условии, если в классе объекта реализована данная процедура (например, разбить Вхождение блока можно, а отдельную 3D-грань -- нельзя). Функция возвращает объект DBObjectCollection, в котором содержатся все полученные составные объекты. Например, разбивка полилинии может вернуть несколько отрезков и дуг.

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

Разбивка полилинии

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

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
 
[CommandMethod("ExplodeObject")]
public static void ExplodeObject()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a lightweight polyline
        using (Polyline acPoly = new Polyline())
        {
            acPoly.AddVertexAt(0, new Point2d(1, 1), 0, 0, 0);
            acPoly.AddVertexAt(1, new Point2d(1, 2), 0, 0, 0);
            acPoly.AddVertexAt(2, new Point2d(2, 2), 0, 0, 0);
            acPoly.AddVertexAt(3, new Point2d(3, 2), 0, 0, 0);
            acPoly.AddVertexAt(4, new Point2d(4, 4), 0, 0, 0);
            acPoly.AddVertexAt(5, new Point2d(4, 1), 0, 0, 0);

            // Sets the bulge at index 3
            acPoly.SetBulgeAt(3, -0.5);

            // Explodes the polyline
            DBObjectCollection acDBObjColl = new DBObjectCollection();
            acPoly.Explode(acDBObjColl);

            foreach (Entity acEnt in acDBObjColl)
            {
                // Add the new object to the block table record and the transaction
                acBlkTblRec.AppendEntity(acEnt);
                acTrans.AddNewlyCreatedDBObject(acEnt, true);
            }

            // Dispose of the in memory polyline
        }

        // Save the new objects to the database
        acTrans.Commit();
    }
}

Редактирование полилиний

2D- и 3D-полилинии, прямоугольники, многоугольники, кольца являются разновидностями полилиний и редактируются с использованием сходных подходов.

Примечание: в оригинальном тексте сказано также и про "3D polygon meshes", я не понял, как PolufaceMesh может редактироваться аналогично полилинии и не стал это включать.

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

Для редактирования полилинии используйте свойства и методы объекта Polyline, Polyline2d или Polyline3d в зависимости от того, каким классом описывается данный объект в чертеже. Используйте следующие свойства и методы, чтобы замкнуть\разомкнуть полилинию, изменить координаты отдельной вершины или добавить новую вершину:

  • Свойство Closed : возвращает или задает замкнутость полилинии;
  • Свойство ConstantWidth : возвращает или задает постоянную ширины плоской полилинии;
  • Метод AppendVertex : добавляет новую вершину для плоской или пространственной полилинии;
  • Метод AddVertexAt : задает значение вершины в заданной точке кривой;
  • Метод ReverseCurve : обращает направление кривой;
  • Метод SetBulgeAt : задает величину выпуклости для данного сегмента кривой;
  • Метод SetStartWidthAt : задет ширину в начале сегмента кривой;
  • Метод Straighten : удаляет из полилинии все вершины сплайна и аппроксимирующей кривой и определяет все оставшиеся вершины в качестве простых вершин. Эта операция эквивалентна команде PEDIT с опцией "Decurve"

В примере ниже создается плоская полилиния. Затем третьему сегменту полилинии задается выпуклость = :0.5, к полилинии добавляется вершина, изменяется ширина последнего сегмента и, наконец, полилиния замыкается.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("EditPolyline")]
public static void EditPolyline()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a lightweight polyline
        using (Polyline acPoly = new Polyline())
        {
            acPoly.AddVertexAt(0, new Point2d(1, 1), 0, 0, 0);
            acPoly.AddVertexAt(1, new Point2d(1, 2), 0, 0, 0);
            acPoly.AddVertexAt(2, new Point2d(2, 2), 0, 0, 0);
            acPoly.AddVertexAt(3, new Point2d(3, 2), 0, 0, 0);
            acPoly.AddVertexAt(4, new Point2d(4, 4), 0, 0, 0);

            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acPoly);
            acTrans.AddNewlyCreatedDBObject(acPoly, true);

            // Sets the bulge at index 3
            acPoly.SetBulgeAt(3, -0.5);

            // Add a new vertex
            acPoly.AddVertexAt(5, new Point2d(4, 1), 0, 0, 0);

            // Sets the start and end width at index 4
            acPoly.SetStartWidthAt(4, 0.1);
            acPoly.SetEndWidthAt(4, 0.5);

            // Close the polyline
            acPoly.Closed = true;
        }

        // Save the new objects to the database
        acTrans.Commit();
    }
}

Редактирование сплайнов

Вы можете редактировать свойства замкнутых или незамкнутых сплайнов и даже преобразовать их в полилинии. Используйте следующие свойства и методы для редактирования сплайнов:

  • Degree : возвращает порядок (степень кривизны) сплайна;

  • EndFitTangent (StartFitTangent) : возвращает вектор касательной сплайна в последней (начальной) вершине;

  • FitTolerance : возвращает или задает точность аппроксимации кривой;

  • NumControlPoints : возвращает количество управляющих вершин у данного сплайна;

  • NumFitPoints : возвращает количество определяющих вершин у данного сплайна;

Методы для редактирования геометрии сплайна:

  • InsertFitPointAt : задает определяющую вершину по данному индексу;

  • ElevateDegree : задает новый порядок (степень кривизны) сплайна;

  • GetControlPointAt : возвращает координаты управляющей вершины для заданного индекса (число управляющих точек хранится в значении свойства NumControlPoints);

  • GetFitPointAt : возвращает координаты определяющей вершины для заданного индекса (число определяющих точек хранится в значении свойства NumFitPoints). Чтобы получить все определяющие вершины вызовите свойство FitData, а у него :: метод GetFitPoints;

  • RemoveFitPointAt : удаляет определяющую вершину в заданной точке;

  • ReverseCurve : обращает направление сплайна;

  • SetControlPointAt : задает координаты управляющей вершины в заданной точке;

  • SetFitPointAt : задает координаты определяющей вершины в заданной точке;

  • SetWeightAt : задает вес определяющей точки сплайна (как каждая точка влияет на его форму: =1 не влияет, > 1 сплайн "притягивается" к точке, < 1 сплайн "отталкивается" от точки);

Свойства "только для чтения":

  • Area : площадь области, образуемой, если бы сплайн был бы замкнут;

  • Closed : возвращает флаг, замкнут ли сплайн в контур;

  • IsPeriodic : возвращает признак, является ли данный сплайн периодическим (замкнутая сплайн:кривая, в которой кривая и ее производные являются непрерывными в начальной/конечной точках);

  • IsPlanar : возвращает признак, является ли данный сплайн плоским;

  • IsRational : возвращает признак, является ли данный сплайн рациональным (используются такие веса в определяющих точках сплайна, что позволяет ему представлять точные конические сечения (окружности, эллипсы, параболы, гиперболы) и другие сложные кривые, которые не могут быть представлены обычными нерациональными сплайнами);

    Редактирование управляющей вершины сплайна

В примере далее создается новый сплайн и затем изменяются координаты его первой определяющей точки

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("EditSpline")]
public static void EditSpline()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a Point3d Collection
        Point3dCollection acPt3dColl = new Point3dCollection();
        acPt3dColl.Add(new Point3d(1, 1, 0));
        acPt3dColl.Add(new Point3d(5, 5, 0));
        acPt3dColl.Add(new Point3d(10, 0, 0));

        // Set the start and end tangency
        Vector3d acStartTan = new Vector3d(0.5, 0.5, 0);
        Vector3d acEndTan = new Vector3d(0.5, 0.5, 0);

        // Create a spline
        using (Spline acSpline = new Spline(acPt3dColl,
                                        acStartTan,
                                        acEndTan, 4, 0))
        {

            // Set a control point
            acSpline.SetControlPointAt(0, new Point3d(0, 3, 0));

            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acSpline);
            acTrans.AddNewlyCreatedDBObject(acSpline, true);
        }

        // Save the new objects to the database
        acTrans.Commit();
    }
}

Редактирование штриховок

Вы можете редактировать как контуры штриховки, так и её стиль (шаблон). Если вы редактируете границы ассоциативной штриховки, то она перестроится при условии, если в результате редактирования получится замкнутый контур. Ассоциативные штриховки обновляются, даже если они находятся на выключенных слоях. Вы можете изменить стиль штриховки (шаблон) у существующей штриховки, но ассоциативность можно установить только при добавлении контуров в определение штриховки. Вы можете проверить, является ли данный объект Hatch ассоциативным, с помощью свойства Associative.

Чтобы увидеть все внесенные изменения необходимо вызвать метод EvaluateHatch.

Редактирование границ штриховки

Вы можете добавляет, переопределять, удалять контуры у штриховки. Ассоциативные штриховки обновятся при любых изменениях, внесенных в из границы; неассоциативные штриховки не обновятся. Чтобы отредактировать границу штриховки или получить свойства о них, используйте следующие методы и свойства:

  • AppendLoop - добавляет новый контур к определению штриховки. Тип добавляемой петли определяется первым аргументом метода AppendLoop (константами, определенными перечислением Autodesk.AutoCAD.DatabaseServices.HatchLoopTypes);
  • GetLoopAt - возвращает контур по заданному индексу;
  • InsertLoopAt - переопределяет контур по заданному индексу;
  • RemoveLoopAt - удаляет контур по заданному индексу;
  • LoopTypeAt - вовзвращает тип контура по заданному индексу;
  • NumberOfLoops - возвращает число контуров;

Добавление внутреннего контура в штриховку

Пример ниже создает ассоциативную штриховку, добавляет в неё окружность в качестве внешнего контура и другую окружность в качестве внутреннего контура

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("EditHatchAppendLoop")]
public static void EditHatchAppendLoop()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create an arc object for the closed boundary to hatch
        using (Arc acArc = new Arc(new Point3d(5, 3, 0), 3, 0, 3.141592))
        {

            acBlkTblRec.AppendEntity(acArc);
            acTrans.AddNewlyCreatedDBObject(acArc, true);

            // Create an line object for the closed boundary to hatch
            using (Line acLine = new Line(acArc.StartPoint, acArc.EndPoint))
            {
                acBlkTblRec.AppendEntity(acLine);
                acTrans.AddNewlyCreatedDBObject(acLine, true);

                // Adds the arc and line to an object id collection
                ObjectIdCollection acObjIdColl = new ObjectIdCollection();
                acObjIdColl.Add(acArc.ObjectId);
                acObjIdColl.Add(acLine.ObjectId);

                // Create the hatch object and append it to the block table record
                using (Hatch acHatch = new Hatch())
                {
                    acBlkTblRec.AppendEntity(acHatch);
                    acTrans.AddNewlyCreatedDBObject(acHatch, true);

                    // Set the properties of the hatch object
                    // Associative must be set after the hatch object is appended to the 
                    // block table record and before AppendLoop
                    acHatch.SetHatchPattern(HatchPatternType.PreDefined, "ANSI31");
                    acHatch.Associative = true;
                    acHatch.AppendLoop(HatchLoopTypes.Outermost, acObjIdColl);

                    // Create a circle object for the inner boundary of the hatch
                    using (Circle acCirc = new Circle())
                    {
                        acCirc.Center = new Point3d(5, 4.5, 0);
                        acCirc.Radius = 1;

                        acBlkTblRec.AppendEntity(acCirc);
                        acTrans.AddNewlyCreatedDBObject(acCirc, true);

                        // Adds the circle to an object id collection
                        acObjIdColl.Clear();
                        acObjIdColl.Add(acCirc.ObjectId);

                        // Append the circle as the inner loop of the hatch and evaluate it
                        acHatch.AppendLoop(HatchLoopTypes.Default, acObjIdColl);
                        acHatch.EvaluateHatch(true);
                    }
                }
            }
        }

        // Save the new object to the database
        acTrans.Commit();
    }
}

Редактирование определения штриховки

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

  • GradientAngle : возвращает или задает угол градиента;
  • GradientName : возвращает наименование градиентной заливки (если использовался преднастроенный стиль из поставки);
  • GradientShift : возвращает или задает сдвиг градиента (определяет позицию, где будет центр смены цветов);
  • GradientType : возвращает тип градиентной заливки;
  • PatternAngle : возвращает или задает угол штриховки;
  • PatternDouble : возвращает признак, является ли штриховка наложенной;
  • PatternType : возвращает тип образца штриховки;
  • SetHatchPattern : задает тип образца штриховки;
  • PatternName : возвращает имя образца штриховки;
  • SetHatchPattern : задает имя образца штриховки;
  • PatternScale : возвращает или задает масштаб штриховки;
  • PatternSpace : возвращает или задает интервал штриховки;
  • SetGradient : задает тип и имя образца градиента;

Изменение имени образца штриховки

В примере ниже создается определение штриховки, контуром которого выступает окружность. После добавления контура редактируются значения PatternScale и HatchPattern.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
 
[CommandMethod("EditHatchPatternScale")]
public static void EditHatchPatternScale()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a circle object for the boundary of the hatch
        using (Circle acCirc = new Circle())
        {
            acCirc.Center = new Point3d(5, 3, 0);
            acCirc.Radius = 3;

            acBlkTblRec.AppendEntity(acCirc);
            acTrans.AddNewlyCreatedDBObject(acCirc, true);

            // Adds the arc and line to an object id collection
            ObjectIdCollection acObjIdColl = new ObjectIdCollection();
            acObjIdColl.Add(acCirc.ObjectId);

            // Create the hatch object and append it to the block table record
            using (Hatch acHatch = new Hatch())
            {
                acBlkTblRec.AppendEntity(acHatch);
                acTrans.AddNewlyCreatedDBObject(acHatch, true);

                // Set the properties of the hatch object
                // Associative must be set after the hatch object is appended to the 
                // block table record and before AppendLoop
                acHatch.SetHatchPattern(HatchPatternType.PreDefined, "ANSI31");
                acHatch.Associative = true;
                acHatch.AppendLoop(HatchLoopTypes.Outermost, acObjIdColl);

                // Evaluate the hatch
                acHatch.EvaluateHatch(true);

                // Increase the pattern scale by 2 and re-evaluate the hatch
                acHatch.PatternScale = acHatch.PatternScale + 2;
                acHatch.SetHatchPattern(acHatch.PatternType, acHatch.PatternName);
                acHatch.EvaluateHatch(true);
            }
        }

        // Save the new object to the database
        acTrans.Commit();
    }
}

Работа со слоями, цветами и типами линий

Создаваемые объекты чертежа имеют некоторые общие свойства, наследуемые от класса Entity: слои, цвета, типы линий и некоторые другие характеристики.

Работа со слоями

Все графические объекты располагаются на некотором слое. Каждый из слоев имеет ряд общих характеристик: цвет, тип линии, вес линии и пр. Слои принято использовать для отрисовки разных по типу\смыслу объектов. Все слои и типы линий хранятся в различных таблицах символов. Слои хранятся в таблице LayerTable, а типы линий — в таблице LinetypeTable

Перебор слоев

Получить информацию о слоях документа вы можете путем итеративного перебора таблицы слоев (LayerTable). Пример ниже собирает информацию об именах слоев и выводит её в диалоговое окно.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

[CommandMethod("DisplayLayerNames")]
public static void DisplayLayerNames()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;
    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Layer table for read
        LayerTable acLyrTbl;
        acLyrTbl = acTrans.GetObject(acCurDb.LayerTableId,
                                        OpenMode.ForRead) as LayerTable;
        string sLayerNames = "";
        foreach (ObjectId acObjId in acLyrTbl)
        {
            LayerTableRecord acLyrTblRec;
            acLyrTblRec = acTrans.GetObject(acObjId,
                                            OpenMode.ForRead) as LayerTableRecord;
            sLayerNames = sLayerNames + "\\n" + acLyrTblRec.Name;
        }
        Application.ShowAlertDialog("The layers in this drawing are: " +
                                    sLayerNames);
        // Dispose of the transaction
    }
}

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

Вы можете создавать новые слои и назначать им цвет, тип линии и прочие свойства. Каждый из слоев отображается в диалоговое окне "Слои". Имя слоя подчиняется правилам для неграфических элементов (см. статью). Пример ниже содержит код, создающий новый слой зеленого цвета и задающий его для окружности

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Colors;

[CommandMethod("CreateAndAssignALayer")]
public static void CreateAndAssignALayer()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Layer table for read
        LayerTable acLyrTbl;
        acLyrTbl = acTrans.GetObject(acCurDb.LayerTableId,
                                        OpenMode.ForRead) as LayerTable;

        string sLayerName = "Center";

        if (acLyrTbl.Has(sLayerName) == false)
        {
            using (LayerTableRecord acLyrTblRec = new LayerTableRecord())
            {
                // Assign the layer the ACI color 3 and a name
                acLyrTblRec.Color = Color.FromColorIndex(ColorMethod.ByAci, 3);
                acLyrTblRec.Name = sLayerName;

                // Upgrade the Layer table for write
                acTrans.GetObject(acCurDb.LayerTableId, OpenMode.ForWrite);

                // Append the new layer to the Layer table and the transaction
                acLyrTbl.Add(acLyrTblRec);
                acTrans.AddNewlyCreatedDBObject(acLyrTblRec, true);
            }
        }

        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a circle object
        using (Circle acCirc = new Circle())
        {
            acCirc.Center = new Point3d(2, 2, 0);
            acCirc.Radius = 1;
            acCirc.Layer = sLayerName;

            acBlkTblRec.AppendEntity(acCirc);
            acTrans.AddNewlyCreatedDBObject(acCirc, true);
        }

        // Save the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Задание слоя активным

Создание каких-либо элементов в чертеже всегда происходит на активном слое. Если вы сделаете активным другой слой, то все новые объекты, будут создаваться на нём, наследуя также настройки слоя -- цвет, тип линии и пр. Вы не можете сделать слой активным, если он заморожен.

Задание активного слоя через свойство Database.Clayer

Пример ниже содержит код, задающий слой с именем "Center" активным, если слой с таким именем имеется в чертеже

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

[CommandMethod("SetLayerCurrent")]
public static void SetLayerCurrent()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Layer table for read
        LayerTable acLyrTbl;
        acLyrTbl = acTrans.GetObject(acCurDb.LayerTableId,
                                        OpenMode.ForRead) as LayerTable;

        string sLayerName = "Center";

        if (acLyrTbl.Has(sLayerName) == true)
        {
            // Set the layer Center current
            acCurDb.Clayer = acLyrTbl[sLayerName];

            // Save the changes
            acTrans.Commit();
        }

        // Dispose of the transaction
    }
}

Задание активного слоя через переменную CLAYER

using Autodesk.AutoCAD.ApplicationServices;

Application.SetSystemVariable("CLAYER", "Center");

Управление видимостью слоя

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

При включении слоя, AutoCAD запускает отрисовку объектов на данном слое. Для управления видимостью слоя (описываемого классом LayerTableRecord) используйте свойство IsOff. Если ввести значение true, то слой будет выключен; если false - то включен.

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

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("TurnLayerOff")]
public static void TurnLayerOff()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;
    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Layer table for read
        LayerTable acLyrTbl;
        acLyrTbl = acTrans.GetObject(acCurDb.LayerTableId,
                                        OpenMode.ForRead) as LayerTable;
        string sLayerName = "ABC";
        if (acLyrTbl.Has(sLayerName) == false)
        {
            using (LayerTableRecord acLyrTblRec = new LayerTableRecord())
            {
                // Assign the layer a name
                acLyrTblRec.Name = sLayerName;
                // Turn the layer off
                acLyrTblRec.IsOff = true;
                // Upgrade the Layer table for write
                acTrans.GetObject(acCurDb.LayerTableId, OpenMode.ForWrite);
                // Append the new layer to the Layer table and the transaction
                acLyrTbl.Add(acLyrTblRec);
                acTrans.AddNewlyCreatedDBObject(acLyrTblRec, true);
            }
        }
        else
        {
            LayerTableRecord acLyrTblRec = acTrans.GetObject(acLyrTbl[sLayerName],
                                            OpenMode.ForWrite) as LayerTableRecord;
            // Turn the layer off
            acLyrTblRec.IsOff = true;
        }
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;
        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;
        // Create a circle object
        using (Circle acCirc = new Circle())
        {
            acCirc.Center = new Point3d(2, 2, 0);
            acCirc.Radius = 1;
            acCirc.Layer = sLayerName;
            acBlkTblRec.AppendEntity(acCirc);
            acTrans.AddNewlyCreatedDBObject(acCirc, true);
        }
        // Save the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Заморозка и размораживание слоя

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

Используйте свойство IsFrozen для заморозки или размораживания слоя. Если ввести значение true, слой будет заморожен; если false, то слой будет разморожен.

В примере ниже создается новый слой "ABC" при его отсутствии, после чего он замораживается.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
 
[CommandMethod("FreezeLayer")]
public static void FreezeLayer()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Layer table for read
        LayerTable acLyrTbl;
        acLyrTbl = acTrans.GetObject(acCurDb.LayerTableId,
                                        OpenMode.ForRead) as LayerTable;

        string sLayerName = "ABC";

        if (acLyrTbl.Has(sLayerName) == false)
        {
            using (LayerTableRecord acLyrTblRec = new LayerTableRecord())
            {
                // Assign the layer a name
                acLyrTblRec.Name = sLayerName;

                // Freeze the layer
                acLyrTblRec.IsFrozen = true;

                // Upgrade the Layer table for write
                acTrans.GetObject(acCurDb.LayerTableId, OpenMode.ForWrite);

                // Append the new layer to the Layer table and the transaction
                acLyrTbl.Add(acLyrTblRec);
                acTrans.AddNewlyCreatedDBObject(acLyrTblRec, true);
            }
        }
        else
        {
            LayerTableRecord acLyrTblRec = acTrans.GetObject(acLyrTbl[sLayerName],
                                            OpenMode.ForWrite) as LayerTableRecord;

            // Freeze the layer
            acLyrTblRec.IsFrozen = true;
        }

        // Save the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Блокировка и разблокировка слоя

Объекты на заблокированном слое нельзя изменить (они видимы и заморожены).

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

Используйте свойство IsLocked для управления блокировкой слоя. Если ввести значение true, то слой будет заблокирован; если false, то слой будет разблокирован. В примере ниже создается новый слой "ABC" при его отсутствии и ему задается блокировка

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
 
[CommandMethod("LockLayer")]
public static void LockLayer()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Layer table for read
        LayerTable acLyrTbl;
        acLyrTbl = acTrans.GetObject(acCurDb.LayerTableId,
                                        OpenMode.ForRead) as LayerTable;

        string sLayerName = "ABC";

        if (acLyrTbl.Has(sLayerName) == false)
        {
            using (LayerTableRecord acLyrTblRec = new LayerTableRecord())
            {
                // Assign the layer a name
                acLyrTblRec.Name = sLayerName;

                // Lock the layer
                acLyrTblRec.IsLocked = true;

                // Upgrade the Layer table for write
                acTrans.GetObject(acCurDb.LayerTableId, OpenMode.ForWrite);

                // Append the new layer to the Layer table and the transaction
                acLyrTbl.Add(acLyrTblRec);
                acTrans.AddNewlyCreatedDBObject(acLyrTblRec, true);
            }
        }
        else
        {
            LayerTableRecord acLyrTblRec = acTrans.GetObject(acLyrTbl[sLayerName],
                                            OpenMode.ForWrite) as LayerTableRecord;

            // Lock the layer
            acLyrTblRec.IsLocked = true;
        }

        // Save the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Установка цвета слою

Каждому слою может быть назначен определенный цвет. Цвет слоя описывается объектом Color, который является частью пространства имен Colors. Этот объект может содержать значение RGB или номер ACI (целое число от 1 до 255). Вариант задания цвета из альбома цветов, существовавший в AutoCAD .NET API не реализован.

Чтобы назначить цвет слою или получить информацию о нём, используйте свойство Color. Примечание: некоторые объекты, такие как линии и окружности и пр., имеют два разных свойства для управления их текущим цветом. Свойство Color используется для назначения значения RGB, номера ACI или цвета из альбома цветов, а свойство ColorIndex поддерживает только номера ACI.

Если вы используете цвет ACI=0 или ByBlock, AutoCAD рисует новые объекты в цвете по умолчанию (белом или черном, в зависимости от настроек приложения), пока они не будут добавлены в блок. При размещении объекты в конкретном блоке (объекте таблицы записи блоков), объекты наследуют его текущие настройки свойств.

Если вы используете цвет ACI=256 или ByLayer, новые объекты наследуют цвет слоя, на котором они находятся. В примере ниже создаются 2 слоя, каждому из которых задаются цвета двумя разными перечисленными выше методами

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Colors;
 
[CommandMethod("SetLayerColor")]
public static void SetLayerColor()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Layer table for read
        LayerTable acLyrTbl;
        acLyrTbl = acTrans.GetObject(acCurDb.LayerTableId,
                                        OpenMode.ForRead) as LayerTable;

        // Define an array of layer names
        string[] sLayerNames = new string[3];
        sLayerNames[0] = "ACIRed";
        sLayerNames[1] = "TrueBlue";
        sLayerNames[2] = "ColorBookYellow";

        // Define an array of colors for the layers
        Color[] acColors = new Color[3];
        acColors[0] = Color.FromColorIndex(ColorMethod.ByAci, 1);
        acColors[1] = Color.FromRgb(23, 54, 232);
        acColors[2] = Color.FromNames("PANTONE Yellow 0131 C",
                                      "PANTONE+ Pastels & Neons Coated");

        int nCnt = 0;

        // Add or change each layer in the drawing
        foreach (string sLayerName in sLayerNames)
        {
            if (acLyrTbl.Has(sLayerName) == false)
            {
                using (LayerTableRecord acLyrTblRec = new LayerTableRecord())
                {
                    // Assign the layer a name
                    acLyrTblRec.Name = sLayerName;

                    // Set the color of the layer
                    acLyrTblRec.Color = acColors[nCnt];

                    // Upgrade the Layer table for write
                    if (acLyrTbl.IsWriteEnabled == false) acTrans.GetObject(acCurDb.LayerTableId, OpenMode.ForWrite);

                    // Append the new layer to the Layer table and the transaction
                    acLyrTbl.Add(acLyrTblRec);
                    acTrans.AddNewlyCreatedDBObject(acLyrTblRec, true);
                }
            }
            else
            {
                // Open the layer if it already exists for write
                LayerTableRecord acLyrTblRec = acTrans.GetObject(acLyrTbl[sLayerName],
                                                                 OpenMode.ForWrite) as LayerTableRecord;

                // Set the color of the layer
                acLyrTblRec.Color = acColors[nCnt];
            }

            nCnt = nCnt + 1;
        }

        // Save the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Установка типа линии

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

Используйте свойство Linetype, чтобы назначить тип линии слою. Это свойство принимает в качестве входных данных имя типа линии.

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

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

[CommandMethod("SetLayerLinetype")]
public static void SetLayerLinetype()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Layer table for read
        LayerTable acLyrTbl;
        acLyrTbl = acTrans.GetObject(acCurDb.LayerTableId,
                                        OpenMode.ForRead) as LayerTable;

        string sLayerName = "ABC";

        if (acLyrTbl.Has(sLayerName) == false)
        {
            using (LayerTableRecord acLyrTblRec = new LayerTableRecord())
            {
                // Assign the layer a name
                acLyrTblRec.Name = sLayerName;

                // Open the Layer table for read
                LinetypeTable acLinTbl;
                acLinTbl = acTrans.GetObject(acCurDb.LinetypeTableId,
                                                OpenMode.ForRead) as LinetypeTable;

                if (acLinTbl.Has("Center") == true)
                {
                    // Set the linetype for the layer
                    acLyrTblRec.LinetypeObjectId = acLinTbl["Center"];
                }

                // Upgrade the Layer table for write
                acTrans.GetObject(acCurDb.LayerTableId, OpenMode.ForWrite);

                // Append the new layer to the Layer table and the transaction
                acLyrTbl.Add(acLyrTblRec);
                acTrans.AddNewlyCreatedDBObject(acLyrTblRec, true);
            }
        }
        else
        {
            LayerTableRecord acLyrTblRec = acTrans.GetObject(acLyrTbl[sLayerName],
                                            OpenMode.ForRead) as LayerTableRecord;

            // Open the Layer table for read
            LinetypeTable acLinTbl;
            acLinTbl = acTrans.GetObject(acCurDb.LinetypeTableId,
                                            OpenMode.ForRead) as LinetypeTable;

            if (acLinTbl.Has("Center") == true)
            {
                // Upgrade the Layer Table Record for write
                acTrans.GetObject(acLyrTbl[sLayerName], OpenMode.ForWrite);

                // Set the linetype for the layer
                acLyrTblRec.LinetypeObjectId = acLinTbl["Center"];
            }
        }

        // Save the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Удаление слоя

Слой возможно удалить, если он не задан в качестве текущего, не содержит объектов, не является системным ("0", "DEFPOINTS"), не пришёл из внешних ссылок, не является одним из слоёв, где лежат определения блоков.

Чтобы удалить слой, используйте метод Erase. Рекомендуется использовать функцию Purge, чтобы убедиться, что слой можно удалить, а также проверить, что это не системный или текущий слой.

В примере ниже удаляется слой "ABC", если он имеется в чертеже. Транзакция закрывается в теле функции, поскольку ожидается удаление только одного объекта.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

[CommandMethod("EraseLayer")]
public static void EraseLayer()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;
    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Layer table for read
        LayerTable acLyrTbl;
        acLyrTbl = acTrans.GetObject(acCurDb.LayerTableId,
                                        OpenMode.ForRead) as LayerTable;
        string sLayerName = "ABC";
        if (acLyrTbl.Has(sLayerName) == true)
        {
            // Check to see if it is safe to erase layer
            ObjectIdCollection acObjIdColl = new ObjectIdCollection();
            acObjIdColl.Add(acLyrTbl[sLayerName]);
            acCurDb.Purge(acObjIdColl);
            if (acObjIdColl.Count \> 0)
            {
                LayerTableRecord acLyrTblRec;
                acLyrTblRec = acTrans.GetObject(acObjIdColl[0],
                                                OpenMode.ForWrite) as LayerTableRecord;
                try
                {
                    // Erase the unreferenced layer
                    acLyrTblRec.Erase(true);
                    // Save the changes and dispose of the transaction
                    acTrans.Commit();
                }
                catch (Autodesk.AutoCAD.Runtime.Exception Ex)
                {
                    // Layer could not be deleted
                    Application.ShowAlertDialog("Error:\\n" + Ex.Message);
                }
            }
        }
    }
}

Работа с цветами

Вы можете назначить цвет отдельному объекту в чертеже, используя его свойство Color, ColorIndex или цвета из альбома цветов. Свойство ColorIndex принимает значение т.н. AutoCAD Color Index (ACI) в виде числового значения от 0 до 256. Свойство Color используется для назначения объекту номера ACI, цвета RGB.

Примечание: Вариант задания цвета из альбома цветов, существовавший в AutoCAD .NET API не реализован.

Объект Color имеет метод SetRGB, который позволяет определить цвет, состоящий из заданного числа красного, зеленого и синего оттенков. Вы также можете назначать цвет конкретному слою. Если вы хотите, чтобы объект унаследовал цвет слоя, на котором он находится, установите цвет объекта на ByLayer, задав значение цвета слоя ACI = 256. Один и тот же цвет может иметь неограниченное количество объектов и слоев.

Задание цвета объекты несколькими способами

В примере ниже окружности задается цвет 3 разными способами: через индекс цвета ACI, RGB-код и альбом цветов.

Примечание: наименование альбома цвета и сам цвет скорректирован по шаблону acadiso.dwt из AutoCAD 2022, для оригинального примера такие значения отсутствуют.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Colors;

[CommandMethod("SetObjectColor")]
public static void SetObjectColor()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Define an array of colors for the layers
        Color[] acColors = new Color[3];
        acColors[0] = Color.FromColorIndex(ColorMethod.ByAci, 1);
        acColors[1] = Color.FromRgb(23, 54, 232);
        acColors[2] = Co"PANTONE Yellow C", "PANTONE+ Solid Coated"astel coated");

        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a circle object and assign it the ACI value of 4
        Point3d acPt = new Point3d(0, 3, 0);
        using (Circle acCirc = new Circle())
        {
            acCirc.Center = acPt;
            acCirc.Radius = 1;
            acCirc.ColorIndex = 4;

            acBlkTblRec.AppendEntity(acCirc);
            acTrans.AddNewlyCreatedDBObject(acCirc, true);

            int nCnt = 0;

            while (nCnt < 3)
            {
                // Create a copy of the circle
                Circle acCircCopy;
                acCircCopy = acCirc.Clone() as Circle;

                // Shift the copy along the Y-axis
                acPt = new Point3d(acPt.X, acPt.Y + 3, acPt.Z);
                acCircCopy.Center = acPt;

                // Assign the new color to the circle
                acCircCopy.Color = acColors[nCnt];

                acBlkTblRec.AppendEntity(acCircCopy);
                acTrans.AddNewlyCreatedDBObject(acCircCopy, true);

                nCnt = nCnt + 1;
            }
        }

        // Save the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Задание активного цвета через свойство Database.Cecolor

Пример ниже содержит код, задающий активным цвет "По слою"

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Colors;

[CommandMethod("SetColorCurrent")]
public static void SetColorCurrent()
{
    // Get the current document
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    // Set the current color
    acDoc.Database.Cecolor = Color.FromColorIndex(ColorMethod.ByLayer, 256);
}

Задание активного цвета через переменную CECOLOR

Пример ниже задает активный слой = Красный с помощью задания системной переменной CECOLOR

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;

[CommandMethod("SetColorCurrent")]
public static void SetColorCurrent()
{
    Application.SetSystemVariable("CECOLOR", "1");
}

Работа с типами линий

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

Следует помнить также и о масштабе типа линии, он задается в отдельном свойстве (LinetypeScale у графических объектов и Celtscale в свойствах БД чертежа), подробнее см. статью Редактирование типа линии, раздел "Масштаб типа линии"

Загрузка и установка активного типа линии

Чтобы вновь создаваемые объекты имели заданный тип линии, его необходимо задать в качестве значения по умолчанию. Существует два различных способа установки типа линии к объекту: напрямую (перезаписать тип линии слоя) или унаследованный от слоя (объект наследует тип линии слоя, на котором он находится, путем установки свойства Linetype или LinetypeId, для типа линии ByLayer).

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

В каждом чертеже обязательно существует как минимум три типа линий: BYBLOCK, BYLAYER и CONTINUOUS. Доступ к каждому из этих типов линий можно получить из таблицы типов линий LinetypeTable или с помощью статических методов вспомогательного SymbolUtilityServices из пространства имён Autodesk.AutoCAD.DatabaseServices. Следующие методы позволяют получить идентификатор объекта для этих типов линий по умолчанию:

  • GetLinetypeByBlockId — возвращает идентификатор объекта для типа линии BYBLOCK;
  • GetLinetypeByLayerId — возвращает идентификатор объекта для типа линии BYLAYER;
  • GetLinetypeContinuousId — возвращает идентификатор объекта для типа линии CONTINUOUS;

Пример ниже содержит код, задающий тип линии "Осевая" новому объекту - окружности. Если такого типа линий в таблице типов линий нет, то его можно загрузить из файла определения типов линий.

string sLineTypName = "Center";
Database acCurDb;
acCurDb.LoadLineTypeFile(sLineTypName, "acad.lin");

Примечание: в nanoCAD .NET API и вероятно в прочих .NET API, основанных на ODA, в реализации метода LoadLineTypeFile допущена ошибка, для корректной работы ему необходимо подавать полный путь к файлу с линиями, иначе выбросится ошибка eFileAccessErr или иная.

Для установки объекту типа линии желательно использовать свойство LinetypeId, вместо свойства Linetype.

Примечание: в nanoCAD .NET API при попытке задания типа линии через Linetype может выброситься ошибка eNoDatabase.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("SetObjectLinetype")]
public static void SetObjectLinetype()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Linetype table for read
        LinetypeTable acLineTypTbl;
        acLineTypTbl = acTrans.GetObject(acCurDb.LinetypeTableId,
                                            OpenMode.ForRead) as LinetypeTable;

        string sLineTypName = "Center";

        if (acLineTypTbl.Has(sLineTypName) == false)
        {
            acCurDb.LoadLineTypeFile(sLineTypName, "acad.lin");
        }

        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a circle object
        using (Circle acCirc = new Circle())
        {
            acCirc.Center = new Point3d(2, 2, 0);
            acCirc.Radius = 1;
            acCirc.Linetype = sLineTypName;

            acBlkTblRec.AppendEntity(acCirc);
            acTrans.AddNewlyCreatedDBObject(acCirc, true);
        }

        // Save the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Задание активного типа линии через свойство Database.Celtype

В примере ниже тип линии "Осевая" задается активным, если он присутсвует в таблице типов линии чертежа. Имейте в виду также о свойстве масштаба типа линии, который задается через свойство Celtscale.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

[CommandMethod("SetLinetypeCurrent")]
public static void SetLinetypeCurrent()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Linetype table for read
        LinetypeTable acLineTypTbl;
        acLineTypTbl = acTrans.GetObject(acCurDb.LinetypeTableId,
                                            OpenMode.ForRead) as LinetypeTable;

        string sLineTypName = "Center";

        if (acLineTypTbl.Has(sLineTypName) == true)
        {
            // Set the linetype Center current
            acCurDb.Celtype = acLineTypTbl[sLineTypName];

            // Save the changes
            acTrans.Commit();
        }

        // Dispose of the transaction
    }
}

Задание активного типа линии через переменную CELTYPE

Пример ниже задает активный тип линии = Осевая с помощью задания системной переменной CELTYPE. Если типа линий с таким названием не существует, то будет выброшено исключение eInvalidInput.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;

[CommandMethod("SetLineTypeCurrent")]
public static void SetLineTypeCurrent()
{
    Application.SetSystemVariable("CELTYPE", "1");
}

Редактирование типа линии

Переименование типа линии

Чтобы переименовать тип линии, используйте свойство Name. При переименовании типа линии вы переименовываете только определение типа линии в чертеже. Имя типа линия в файле LIN останется прежним.

Удаление типа линии

Чтобы удалить тип линии, используйте метод Erase. Нельзя удалить тип линии, если:

  • это системный тип линии BYLAYER, BYBLOCK, CONTINUOUS;
  • это текущий тип линии;
  • это используемый каким:либо объектом или слоем тип линии;
  • типы линий зависят от внешних ссылок;

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

Изменение описания типа линии

Типы линий могут иметь связанное с ними описание. Описание представляет собой строку в ASCII-кодировке. Вы можете задать или изменить описание типа линии с помощью свойства AsciiDescription. Описание должно содержать до 47 символов (больше символов возможно, но при сохранении в lin-файл и последующем считывании описание урежется до 47 символов). Описание может содержать символы табуляции (например, для имитации рисунка типа линии). В примере ниже редактируется описание для активного типа линии, информация о котором возвращена через свойство Database.Celtype

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

[CommandMethod("ChangeLinetypeDescription")]
public static void ChangeLinetypeDescription()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Linetype table record of the current linetype for write
        LinetypeTableRecord acLineTypTblRec;
        acLineTypTblRec = acTrans.GetObject(acCurDb.Celtype,
                                            OpenMode.ForWrite) as LinetypeTableRecord;

        // Change the description of the current linetype
        acLineTypTblRec.AsciiDescription = "Exterior Wall";

        // Save the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Изменение масштаба типа линии

Вы можете указать масштаб типа линии для новых создаваемых объектов. Чем меньше масштаб, тем больше повторений "узора линии" на единицу чертежа. По умолчанию AutoCAD использует глобальный масштаб типа линии 1.0, который равен одной единице чертежа. Вы можете изменить масштаб типа линии для всех объектов чертежа и определений атрибутов. Системная переменная CELTSCALE устанавливает масштаб типа линии для вновь создаваемых объектов. Системная переменная LTSCALE изменяет глобальный масштаб линий существующих объектов, а также новых объектов.

Свойство LinetypeScale объекта используется для изменения масштаба типа линий объекта. Масштаб типа линий, в котором отображается объект, рассчитается как масштаб типа линий отдельного объекта, умноженный на глобальный масштаб типа линий (LTSCALE). В примере ниже задается глобальный масштаб типа линии и у одной из окружностей редактируется значение масштаба типа линии на 0.5

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("SetObjectLinetypeScale")]
public static void SetObjectLinetypeScale()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Save the current linetype
        ObjectId acObjId = acCurDb.Celtype;

        // Set the global linetype scale
        acCurDb.Ltscale = 3;

        // Open the Linetype table for read
        LinetypeTable acLineTypTbl;
        acLineTypTbl = acTrans.GetObject(acCurDb.LinetypeTableId,
                                            OpenMode.ForRead) as LinetypeTable;

        string sLineTypName = "Border";

        if (acLineTypTbl.Has(sLineTypName) == false)
        {
            acCurDb.LoadLineTypeFile(sLineTypName, "acad.lin");
        }

        // Set the Border linetype current
        acCurDb.Celtype = acLineTypTbl[sLineTypName];

        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a circle object and set its linetype
        // scale to half of full size
        using (Circle acCirc1 = new Circle())
        {
            acCirc1.Center = new Point3d(2, 2, 0);
            acCirc1.Radius = 4;
            acCirc1.LinetypeScale = 0.5;

            acBlkTblRec.AppendEntity(acCirc1);
            acTrans.AddNewlyCreatedDBObject(acCirc1, true);

            // Create a second circle object
            using (Circle acCirc2 = new Circle())
            {
                acCirc2.Center = new Point3d(12, 2, 0);
                acCirc2.Radius = 4;

                acBlkTblRec.AppendEntity(acCirc2);
                acTrans.AddNewlyCreatedDBObject(acCirc2, true);
            }
        }

        // Restore the original active linetype
        acCurDb.Celtype = acObjId;

        // Save the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Состояния слоев

Со слоем также связано понятие "Состояние слоя" (в AutoCAD он также называется "Фильтр" в окне СЛОЙ). Оно включают в себя информацию о том, включен ли слой, заморожен ли он, заблокирован ли, выведен ли на печать и автоматически замораживается ли в видовых экранах, а также хранит информацию о цвете слоя, связанных типа линии, толщины линии и стиля печати. Данные о состоянии слоя можно сохранить и обратиться к ним позднее; этот инструмент облегчает хранение различных конфигураций настроек слоев.

Доступ к состояниям слоев осуществляется через вспомогательный класс LayerStateManager (состоит из набора словарей), возвращаемый через одноименное свойство у объекта Database.

Подробнее о механике сохранения состояний слоя

AutoCAD сохраняет информацию о настройках слоев в словаре расширений объекта LayerTable. При первом сохранении состояния слоя AutoCAD выполняет следующие действия:

  • Создает словарь расширений в таблице слоев;
  • Создает объект Dictionary с именем ACAD_LAYERSTATE в словаре расширений;
  • Сохраняет свойства каждого слоя чертежа в объекте XRecord в словаре ACAD_LAYERSTATE. AutoCAD сохраняет все настройки слоев в XRecord, но в дальнейшем при восстановлении будет использоваться только те, которые вы выбрали для сохранения;

При сохранении других настроек слоев в чертеже, AutoCAD создает другой объект XRecord, описывающий сохраненные настройки, и сохраняет XRecord в словаре ACAD_LAYERSTATE.

Сохранение и чтение состояний слоев

Пример ниже сохраняет информацию о состоянии слоев (заморозка, видимость, цвет) в набор "Config1":

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

[CommandMethod("SaveLayerStates")]
public static void SaveLayerStates()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;
    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        LayerStateManager acLyrStMan;
        acLyrStMan = acCurDb.LayerStateManager;
        acLyrStMan.SaveLayerState("Config1", LayerStateMasks.Frozen | LayerStateMasks.On |
LayerStateMasks.Color, ObjectId.Null);
        acTrans.Commit();
    }
}

Пример ниже выводит информации в модальное окно о конфигурациях слоев, имеющихся в данном чертеже

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

[CommandMethod("ListLayerStates")]
public static void ListLayerStates()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;
    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        LayerStateManager acLyrStMan;
        acLyrStMan = acCurDb.LayerStateManager;
        DBDictionary acDbDict;
        acDbDict = acTrans.GetObject(acLyrStMan.LayerStatesDictionaryId(true),
                                        OpenMode.ForRead) as DBDictionary;
        string sLayerStateNames = "";
        foreach (DBDictionaryEntry acDbDictEnt in acDbDict)
        {
            sLayerStateNames = sLayerStateNames + "\\n" + acDbDictEnt.Key;
        }
        Application.ShowAlertDialog("The saved layer settings in this drawing are:" +
                                    sLayerStateNames);
        // Dispose of the transaction
    }
}

Работа с состоянием слоев

Объект LayerStateManager предоставляет различный набор функций для сохранения и управления состояниями слоев:

  • DeleteLayerState - удаляет именованный набор настроек состояний;
  • ExportLayerState (ImportLayerState) - сохраняет (считывает) информацию о состоянии в (из) текстовый файл (формат DXF-like);
  • ImportLayerStateFromDb - импортирует именованный набор состояний из другой базы данных чертежа;
  • RenameLayerState - переименовывает набор состояний;
  • RestoreLayerState - задает настройки слоев из данного набора состояний;
  • SaveLayerState - сохраняет информацию о наборе настроек состояний для данного чертежа с данным именем и данными сохраняемыми значениями;

Сохранение состояний слоев

Используйте метод SaveLayerState для сохранения набора состояний слоев чертежа. Метод SaveLayerState требует указания трех аргументов. Первый аргумент — это строковое название вашего набор настроек. Второй аргумент определяет свойства слоя, которые вы хотите сохранить в виде констант перечисления LayerStateMasks. В следующей таблице перечислены константы LayerStateMasks и их значения

Имя константыРасшифровка
ColorЦвет слоя
CurrentViewportНастройки заморозки слоев в существующих видовых экранах
FrozenИнформация о заморозке слоев
LastRestoredИнформация о последнем восстановленном слое (после удаления)
LineTypeТип линий
LineWeightВес линий
LockedИнформация о блокировке слоев
NewViewportНастройки заморозки слоев для новых создаваемых видовых экранах
NoneНикакие настройки слоев не будут сохранены
OnВидимость слоев
PlotВывод слоев на печать
PlotStyleСтиль печати

Для указания нескольких свойств используйте оператор "|". Третий аргумент — это идентификатор видового экрана, настройки слоев для которого вы хотите сохранить. Используйте ObjectId.Null, чтобы не указывать ВЭ (то есть настройки будут справедливые для всех видовых экранов). Если вы попытаетесь сохранить состояние слоев под уже существующим именем, будет возвращена ошибка. Перед повторным использованием имени необходимо переименовать или удалить существующий набор состояний слоев. Проверить, имеется ли набор с таким именем, можно с помощью метода HasLayerState. В примере ниже сохраняется набор состояний слоев с именем "ColorLinetype" и настройками цвета и типов линий слоев чертежа.


Переименование набора состояний

Метод RenameLayerState предназначен для изменения имени набора состояний слоев. Пример ниже содержит код для для переименовывания сохраненного набор состояний "ColorLinetype", созданного в примере раннее, в "OldColorLinetype".

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

[CommandMethod("RenameLayerState")]
public static void RenameLayerState()
{
    // Get the current document
    Document acDoc = Application.DocumentManager.MdiActiveDocument;

    LayerStateManager acLyrStMan;
    acLyrStMan = acDoc.Database.LayerStateManager;

    string sLyrStName = "ColorLinetype";
    string sLyrStNewName = "OldColorLinetype";

    if (acLyrStMan.HasLayerState(sLyrStName) == true &&
        acLyrStMan.HasLayerState(sLyrStNewName) == false)
    {
        acLyrStMan.RenameLayerState(sLyrStName, sLyrStNewName);
    }
}

Удаление набора состояний

Метод DeleteLayerState предназначен для удаления набора состояний слоев из чертежа. Приведенный ниже код удаляет набор состояний с именем "ColorLinetype", если он существует в чертеже:

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
 
[CommandMethod("RemoveLayerState")]
public static void RemoveLayerState()
{
    // Get the current document
    Document acDoc = Application.DocumentManager.MdiActiveDocument;

    LayerStateManager acLyrStMan;
    acLyrStMan = acDoc.Database.LayerStateManager;

    string sLyrStName = "ColorLinetype";

    if (acLyrStMan.HasLayerState(sLyrStName) == true)
    {
        acLyrStMan.DeleteLayerState(sLyrStName);
    }
}

Восстановление состояний слоев

Метод RestoreLayerState предназначен для загрузки состояний слоев из данного набора, он требует указания 4 аргументов: первое значение — это имя восстанавливаемого набора состояний слоев, второе — идентификатор объекта видового экрана, настройки слоев для которого необходимо восстановить (ObjectId.Null, если для всех). Третье значение — целое число, определяющее способ обработки слоев, информация о которых отсутствует в сохраненном наборе. Четвертое значение определяет, какие настройки слоев будут восстановлены. Следующие значения определяют способ обработки слоев, информация о которых отсутствует в сохраненном наборе (третий аргумент):

  • 0 — Слои, не находящиеся в сохраненном наборе, остаются без изменений;
  • 1 — Слои, не находящиеся в сохраненном наборе, отключаются;
  • 2 — Слои, не находящиеся в сохраненном наборе, замораживаются в текущем видовом экране;
  • 4 — Настройки слоёв задаются, переопределяя настройки для текущего видового экрана;

Примечание: Вы можете использовать несколько значений для задания поведения слоев, не находящихся в сохраненном наборе. Например, вы можете отключить и заморозить слои, которые отсутствуют в нём Например, если вы сохраните настройки цвета и типа линии под именем "ColorLinetype" и впоследствии измените эти настройки, восстановление "ColorLinetype" вернет слоям цвета и типы линий, которые они имели при сохранении "ColorLinetype". Если вы добавили новые слои в чертеж после сохранения "ColorLinetype", эти новые слои не будут изменены при восстановлении "ColorLinetype".

Восстановление настроек цвета и типа линий слоев чертежа.

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

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

[CommandMethod("RestoreLayerState")]
public static void RestoreLayerState()
{
    // Get the current document
    Document acDoc = Application.DocumentManager.MdiActiveDocument;

    LayerStateManager acLyrStMan;
    acLyrStMan = acDoc.Database.LayerStateManager;

    string sLyrStName = "ColorLinetype";

    if (acLyrStMan.HasLayerState(sLyrStName) == true)
    {
        acLyrStMan.RestoreLayerState(sLyrStName,
                                     ObjectId.Null,
                                     1,
                                     LayerStateMasks.Color |
                                     LayerStateMasks.LineType);
    }
}

Экспорт и загрузка набора состояний

Вы можете экспортировать и импортировать сохраненные наборы состояний слоев, чтобы использовать те же настройки слоев в других чертежах (помимо метода ImportLayerStateFromDb). Используйте метод ExportLayerState для экспорта сохраненного состояния слоев в обменный файл LAS; используйте метод ImportLayerState для импорта файла LAS в чертеж.

Примечание: Импорт набора состояний слоев не восстанавливает их; для восстановления состояния слоев после импорта необходимо использовать метод RestoreLayerState.

Метод ExportLayerState принимает два аргумента. Первый аргумент — это наименование экспортируемого набора состояний слоев. Второй аргумент — это имя файла, в который вы сохраняете эти настройки. Если вы не укажете путь к файлу, он будет сохранен в том же каталоге, из которого был открыт чертеж. Если указанное вами имя файла уже существует, существующий файл будет перезаписан. Используйте расширение .las при именовании файлов; это расширение распознается AutoCAD для файлов состояний слоев. Метод ImportLayerState принимает один параметр: путь к файлу с набором состояний. Если набор состояний слоев, который вы хотите импортировать, отсутствует в файле LAS, но существует в файле чертежа, вы можете открыть базу данных целевого чертежа, а затем использовать метод ImportLayerStateFromDb для импорта данных из неё.

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

Если импортируемый файл определяет настройки для слоев, которые не существуют в текущем чертеже, эти слои создаются в текущем чертеже с заданными настройками. При использовании метода RestoreLayerState новым слоям присваиваются свойства, указанные при сохранении настроек; всем остальным свойствам новых слоев присваиваются значения по умолчанию.

Экспорт настроек состояний

Пример ниже сохраняет набор состояний "ColorLinetype", созданный в примерах раннее, в файл на ПК.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

[CommandMethod("ExportLayerState")]
public static void ExportLayerState()
{
    // Get the current document
    Document acDoc = Application.DocumentManager.MdiActiveDocument;

    LayerStateManager acLyrStMan;
    acLyrStMan = acDoc.Database.LayerStateManager;

    string sLyrStName = "ColorLinetype";

    if (acLyrStMan.HasLayerState(sLyrStName) == true)
    {
        acLyrStMan.ExportLayerState(sLyrStName, "c:\\my documents\\" +
                                                sLyrStName + ".las");
    }
}

Импорт настроек состояний

Пример ниже загружает из файла настройки состояний слоев (но не делает их активными, для этого используйте RestoreLayerState).

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

[CommandMethod("ImportLayerState")]
public static void ImportLayerState()
{
    // Get the current document
    Document acDoc = Application.DocumentManager.MdiActiveDocument;

    LayerStateManager acLyrStMan;
    acLyrStMan = acDoc.Database.LayerStateManager;

    string sLyrStFileName = "c:\\my documents\\ColorLinetype.las";

    if (System.IO.File.Exists(sLyrStFileName))
    {
        try
        {
            acLyrStMan.ImportLayerState(sLyrStFileName);
        }
        catch (Autodesk.AutoCAD.Runtime.Exception ex)
        {
            Application.ShowAlertDialog(ex.Message);
        }
    }
}

Работа с текстом

Существует 2 варианта текстовых объектов, используемых в основном, для аннотации: однострочный текст (описывается классом DBText) и многострочный текст (описывается классом MText). У текста можно задавать стиль.

Многострочный текст

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

Создание многострочного текста

Для создания многострочного текста сперва необходимо создать объект класса MText (конструктор MText не принимает никаких параметров), далее с помощью полей и методов класса задать текстовую строку, точку вставки и иные параметры. В конце созданный объект необходимо добавить в целевой блок по аналогии с прочими графическими объектами. В примере ниже создается экземпляр многострочного текста в пространстве модели в точке (2, 2, 0) и содержимым "This is a text string for the MText object."

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
 
[CommandMethod("CreateMText")]
public static void CreateMText()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a multiline text object
        using (MText acMText = new MText())
        {
            acMText.Location = new Point3d(2, 2, 0);
            acMText.Width = 4;
            acMText.Contents = "This is a text string for the MText object.";

            acBlkTblRec.AppendEntity(acMText);
            acTrans.AddNewlyCreatedDBObject(acMText, true);
        }

        // Save the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Форматирование многострочного текста

Создаваемый многострочный текст автоматически принимает настройки активного стиля текста (по умолчанию - ГОСТ 2.304). Вы можете переопределить стиль текста целиком или задать отдельные свойства у объекту MText, также вы можете применить форматирование к отдельным символам или словосочетаниям.

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

Свойство TextStyleId задает шрифт и настройки форматирования для многострочного текстового объекта. При изменении стиля многострочного текстового объекта, к которому применено форматирование символов, стиль применяется ко всему объекту, и некоторое форматирование символов может быть утрачено. Например, при смене стиля TrueType на стиль, использующий шрифт SHX, или на другой шрифт TrueType, многострочный текст станет использовать новый шрифт для всего объекта, и любое пользовательское форматирование символов будет утрачено.

Параметры форматирования, такие как подчеркивание, внрхний\нижний регистр и пр. можно применять к отдельным словам или символам в абзаце текста. Вы также можете изменять цвет, шрифт и высоту отдельных символов. Можно изменять интервалы между символами текста или увеличивать ширину символов.

Используйте фигурные скобки ({ }) для задания форматирования только к тексту внутри скобок. Скобки могут быть вложенными друг в друга до восьми уровней.

Вы также можете использовать ASCII управляющие коды символов, чтобы указать настройки форматирования или специальные символы, например, греческого алфавита или операторов сравнения.

Создание МТекста с форматированием

В примере ниже создается многострочный текст в точке (10, 5, 0), содержащий несколько операций форматирования: жирное выделение, текст в верхнем и нижнем регистре.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
 
[CommandMethod("FormatMText")]
public static void FormatMText()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a multiline text object
        using (MText acMText = new MText())
        {
            acMText.Location = new Point3d(2, 2, 0);
            acMText.Width = 4.5;
            acMText.Contents = "{{\\H1.5x; Big text}\\A2; over text\\A1;/\\A0;under text}";

            acBlkTblRec.AppendEntity(acMText);
            acTrans.AddNewlyCreatedDBObject(acMText, true);
        }

        // Save the changes and dispose of the transaction
        acTrans.Commit();
    }
}

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

Однострочный текст

Однострочный тект, описываемый классом DBText, удобно использовать для коротких текстовых строк, не требующих специальных настроек форматирования.

Создание однострочного текста

При использовании однострочного текста каждая отдельная строка текста будет являться отдельным объектом. Чтобы создать однострочный текст, необходимо создать экземпляр класса DBText, а затем добавить его в запись таблицы блоков, представляющую пространство модели или листа. При создании нового экземпляра объекта DBText конструктору не передаются никакие параметры, информация о текстовой строке, её стиле, положении задается с помощью свойств класса. В примере ниже создается однострочный текст в пространстве модели в точке (2, 2, 0), с высотой 0.5 и текстовой строкой "Hello, AutoCAD!"

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
 
[CommandMethod("CreateText")]
public static void CreateText()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a single-line text object
        using (DBText acText = new DBText())
        {
            acText.Position = new Point3d(2, 2, 0);
            acText.Height = 0.5;
            acText.TextString = "Hello, AutoCAD!";

            acBlkTblRec.AppendEntity(acText);
            acTrans.AddNewlyCreatedDBObject(acText, true);
        }

        // Save the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Редактирование однострочного текста

Как и любые другие графические объекты, однострочный текст можно перемещать, поворачивать, удалять и копировать. Также можно зеркально отражать текст. Если вы не хотите, чтобы текст при зеркальном отражении переворачивался, установите системную переменную MIRRTEXT в значение 0. Перемещать, поворачивать и копировать объекты можно с помощью методов TransformBy и Clone.

Редактирование высоты текста

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

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

В дополнение к высоте заглавной буквы и дополнительной области сверху, которые в сумме составляют высоту символа, указанную пользователем, шрифты TrueType также имеют область внизу символа для частей символов, которые выходят за пределы строки вставки текста. Примеры таких символов: y, j, p, g и q.

Высоту текста можно задать с помощью свойства TextSize стиля текста или свойства Height текстового объекта. Это свойство принимает только положительные числа.

Форматирование однострочного текста

Создаваемый однострочный текст автоматически принимает настройки активного стиля текста (по умолчанию - Standard). Вы можете изменить отображение однострочного текста, изменив связанный с ним стиль текста или непосредственно отредактировав свойства однострочного текстового объекта. К отдельным словам и символам в однострочных текстовых объектах нельзя применять форматирование, как в случае многострочного текста.

Чтобы изменить стиль текста, связанный с данным однострочным текстовым объектом, установите для свойства TextStyleId идентификатор требуемого стиля текста. После изменения стиля текста необходимо обновить чертеж или обновить объект, чтобы увидеть визуальные изменения в чертеже.

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

  • HorizontalMode : горизонтальное выравнивание текста;
  • VerticalMode : вертикальное выравнивание текста;
  • Position : точка вставки текста;
  • Oblique : угол наклона текста;
  • Rotation : поворот текстовой строки (не самого объекта);
  • WidthFactor : масштабный коэффициент для ширины текста;
  • AlignmentPoint : точка выравнивания текста;
  • IsMirroredInX : признак того, что текст отображен задом наперед;
  • IsMirroredInY : признак того, что текст отображен снизу вверх;
  • TextString : текстовая строка; После изменения свойства необходимо вызвать обновление модели для отображения изменений.

Угол наклона текста

Угол наклона определяет, будет ли текст наклонен вперед или назад. Угол представляет собой отклонение от вертикальной оси (90 градусов). Чтобы задать угол наклона, используйте свойство ObliquingAngle для изменения стиля текста или свойство Oblique текстового объекта. Угол наклона должен быть указан в радианах. Положительный угол обозначает наклон вправо, к отрицательному значению (означает наклон влево) будет добавлено 2*PI для преобразования в положительное.

В примере ниже задается угол наклона текста 45 градусов.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
 
[CommandMethod("ObliqueText")]
public static void ObliqueText()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a single-line text object
        using (DBText acText = new DBText())
        {
            acText.Position = new Point3d(3, 3, 0);
            acText.Height = 0.5;
            acText.TextString = "Hello, World.";

            // Change the oblique angle of the text object to 45 degrees(0.707 in radians)
            acText.Oblique = 0.707;

            acBlkTblRec.AppendEntity(acText);
            acTrans.AddNewlyCreatedDBObject(acText, true);
        }

        // Save the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Выравнивание текста

Вы можете выравнивить однострочный текст по горизонтали и вертикали. По умолчанию используется выравнивание по левому краю. Для настройки параметров выравнивания по горизонтали и вертикали используйте свойства HorizontalMode и VerticalMode соответственно.

Обычно при завершении работы с текстовым объектом его положение и границы выравнивания пересчитываются согласно настройкам стиля текста. Для визуального изменения настроек выравнивания вызовите метод AdjustAlignment у текстового объекта.

В примере ниже для каждого из выравниваний текста по горизонталей отрисовывается точка, относительно которой происходит выравнивание, в виде объекта класса DBPoint красного цвета в виде креста.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
 
[CommandMethod("TextAlignment")]
public static void TextAlignment()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        string[] textString = new string[3];
        textString[0] = "Left";
        textString[1] = "Center";
        textString[2] = "Right";

        int[] textAlign = new int[3];
        textAlign[0] = (int)TextHorizontalMode.TextLeft;
        textAlign[1] = (int)TextHorizontalMode.TextCenter;
        textAlign[2] = (int)TextHorizontalMode.TextRight;

        Point3d acPtIns = new Point3d(3, 3, 0);
        Point3d acPtAlign = new Point3d(3, 3, 0);

        int nCnt = 0;

        foreach (string strVal in textString)
        {
            // Create a single-line text object
            using (DBText acText = new DBText())
            {
                acText.Position = acPtIns;
                acText.Height = 0.5;
                acText.TextString = strVal;

                // Set the alignment for the text
                acText.HorizontalMode = (TextHorizontalMode)textAlign[nCnt];

                if (acText.HorizontalMode != TextHorizontalMode.TextLeft)
                {
                    acText.AlignmentPoint = acPtAlign;
                }

                acBlkTblRec.AppendEntity(acText);
                acTrans.AddNewlyCreatedDBObject(acText, true);
            }

            // Create a point over the alignment point of the text
            using (DBPoint acPoint = new DBPoint(acPtAlign))
            {
                acPoint.ColorIndex = 1;

                acBlkTblRec.AppendEntity(acPoint);
                acTrans.AddNewlyCreatedDBObject(acPoint, true);

                // Adjust the insertion and alignment points
                acPtIns = new Point3d(acPtIns.X, acPtIns.Y + 3, 0);
                acPtAlign = acPtIns;
            }

            nCnt = nCnt + 1;
        }

        // Set the point style to crosshair
        Application.SetSystemVariable("PDMODE", 2);

        // Save the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Направление текста

Для задания направления текста имеются 2 специальных метода -- IsMirroredInX и IsMirroredInY. Если задать IsMirroredInX = true, то текст будет отображен в обратном направлении (справа налево); если задать IsMirroredInY = true, то текст будет перевернутым (снизу вверх). На уровне стиля текста регулируются с помощью флагов (FlagBits).

В примере ниже методу IsMirroredInX задается значение true, чтобы текст отобразился справа-налево

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
 
[CommandMethod("BackwardsText")]
public static void BackwardsText()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a single-line text object
        using (DBText acText = new DBText())
        {
            acText.Position = new Point3d(3, 3, 0);
            acText.Height = 0.5;
            acText.TextString = "Hello, World.";

            // Display the text backwards
            acText.IsMirroredInX = true;

            acBlkTblRec.AppendEntity(acText);
            acTrans.AddNewlyCreatedDBObject(acText, true);
        }

        // Save the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Стили текста

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

Создание и редактирование стилей

Чтобы создать новый текстовый стиль, создайте новый экземпляр класса TextStyleTableRecord. Присвойте новому стилю текста имя с помощью свойства Name, задайте иные требуемые свойства. Затем откройте объект TextStyleTable для записи и используйте метод Add для добавления нового стиля текста.

Имена стилей, как и слои, а также иные служебные неграфические объекты подчиняются единым правилам (см. статью). Вы можете изменить существующий стиль, изменив свойства объекта TextStyleTableRecord, полученного для записи. Если вы хотите работать с активным стилем текста, получите его TextStyleTableRecord, воспользовавшись идентификатором (ObjectId) активного стиля по свойству TextStyle объекта Database.

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

  • BigFontFileName : задает имя файла с определением шрифта для заглавных букв для не0ASCII символов;
  • FileName : задает имя файла с определением шрифта;
  • FlagBits : задает признаки направления текста в виде флагов (у текстовых объектов -- IsMirroredInX и IsMirroredInY);
  • Font : задает форматирование текста (жирный, курсив и пр.) в виде структуры FontDescriptor;
  • IsVertical : признак того, направлен ли текст по вертикали;
  • ObliquingAngle : угол наклона символов текста от вертикали;
  • TextSize : размер текста;
  • XScale : сжатие символов текста;

Если вы измените настройки форматирования текста или ориентацию для существующего стиля, то весь текст, использующий этот стиль, будет перерисован с новыми настройками при вызове процедуры обновления чертежа. Изменение высоты текста (TextSize), коэффициента сжатия ширины (XScale) и угла наклона (ObliquingAngle) не влияет на существующие текстовые объекты, только на вновь создаваемые текстовые объекты.

Форматирование шрифта текста

Шрифты определяют форму текстовых символов, составляющих каждый набор символов. Один шрифт может использоваться в нескольких стилях. Свойство FileName используется для задания файла шрифта для текстового стиля. Текстовому стилю можно назначить шрифты TrueType или SHX.

В следующем примере получается активный стиль текста по свойству Textstyle базы данных чертежа, у него изменяется шрифта на "Calibri" редактированием свойства Font. Чтобы увидеть эффект изменения шрифта, добавьте в текущий чертеж несколько новых текстовых объектов.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

[CommandMethod("UpdateTextFont")]
public static void UpdateTextFont()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;
    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the current text style for write
        TextStyleTableRecord acTextStyleTblRec;
        acTextStyleTblRec = acTrans.GetObject(acCurDb.Textstyle,
                                              OpenMode.ForWrite) as TextStyleTableRecord;
        // Get the current font settings
        Autodesk.AutoCAD.GraphicsInterface.FontDescriptor acFont;
        acFont = acTextStyleTblRec.Font;
        // Update the text style's typeface with "PlayBill"
        Autodesk.AutoCAD.GraphicsInterface.FontDescriptor acNewFont;
        acNewFont = new
          Autodesk.AutoCAD.GraphicsInterface.FontDescriptor("Calibri",
                                                            acFont.Bold,
                                                            acFont.Italic,
                                                            acFont.CharacterSet,
                                                            acFont.PitchAndFamily);
        acTextStyleTblRec.Font = acNewFont;
        acDoc.Editor.Regen();
        // Save the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Использоание TrueType-шрифтов (TTF)

Шрифты TrueType всегда отображаются в чертеже с внутренним заполнением. При выводе на печать заполнение можно отключить, установив значение переменной TEXTFILL в 0.

Использование Unicode

AutoCAD поддерживает стандарт кодирования символов Unicode. Шрифт Unicode может содержать 65 535 символов для различных языков мира. Все шрифты SHX, поставляемые с продуктом, поддерживают символы Unicode.

Текстовые файлы для некоторых алфавитов содержат тысячи символов, не входящих в ASCII. Для работы с таким текстом AutoCAD поддерживает специальный тип определения символов, известный как файл Big Font. Можно установить текстовый стиль для использования как обычных шрифтов, так и специальных файлов Big Font. Обычные шрифты указываются с помощью свойства FileName. Шрифты Big Font указываются с помощью свойства BigFontFileName.

Примечание: Имена файлов шрифтов не могут содержать запятые. AutoCADпозволяет указать шрифт по умолчанию, который будет использоваться, если указанный файл шрифта не может быть найден. Для его установки используйте системную переменную FONTALT или метод SetSystemVariable приложения Application. В следующем примере кода изменяются свойства FileName и BigFontFileName для bigfont.shx. Вам необходимо отредактировать пути к файлам на вашем ПК, в примере ниже они приведены для AutoCAD 2022.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

[CommandMethod("ChangeFontFiles")]
public static void ChangeFontFiles()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the current text style for write
        TextStyleTableRecord acTextStyleTblRec;
        acTextStyleTblRec = acTrans.GetObject(acCurDb.Textstyle,
                                                OpenMode.ForWrite) as TextStyleTableRecord;

        // Change the font files used for both Big and Regular fonts
        acTextStyleTblRec.BigFontFileName = @"C:\Program Files\Autodesk\AutoCAD 2022\Fonts\bigfont.shx";
        acTextStyleTblRec.FileName = @"C:\Program Files\Autodesk\AutoCAD 2022\Fonts\italic.shx";

        // Save the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Замена шрифтов

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

Примечание: таблицы сопоставления шрифтов (по системной переменной FONTMAP) в nanoCAD не реализованы.

Указание альтернативного шрифта

Если указанного в стиле или многострочном тексте шрифта нет в системе среди указанных папок с данными, он будет заменен на шрифт, указанный в качестве альтернативного по умолчанию. По умолчанию, в AutoCAD используется шрифт simplex.shx. Вы можете переопределить это назначение, отредактировав системную переменную FONTALT. Если в стиле текста используется т.н. "большой шрифт" (расширенные символы юникода),сопоставление должно выполняться парами файлов шрифтов: txt.shx, bigfont.shx.

Работа с размерами

С помощью объектов-размеров можно добавлять в чертеж различные анотативные подписи и результаты измерений между объектами. Допуски (Tolerance) определяют, насколько может варьироваться размер. У размеров можно изменять визуальное отображение с помощью стилей или прямых свойств.

Принцип работы размеров

Размеры показывают геометрические характеристики объектов, величины расстояния или углов между объектами, либо координаты X и Y элемента. Имеется три основных типа размеров: линейные, радиальные и угловые.

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

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

Составляющие размера

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

  • Dimension line : отрезок, определяющий направление и область охвата размера. Для угловых размеров этот отрезок превращается в дугу;
  • Extension line : линия продолжения опорной измерительной плоскости размера (между какими элементами объекта размер создан);
  • Arrowhead : символы:указатели стрелок размерной линии;
  • Dimension text : текстовая строка, содержащая результат измерения;
  • Leader : плоская линия с полкой, связывающая размер с объектом, в случае когда размер для читаемости удален от объекта;

  • Center mark : крестик, обозначающий центр окружности или круговой дуги;
  • Centerline : набор прерывистых линий, обозначающих центр окружности или круговой дуги;

Системные переменные для размеров

Существует ряд системных переменных, контролирующих вид размеров: DIMAUNIT, DIMUPT, DIMFIT, DIMTOFL, DIMTIH, DIMTOH, DIMJUST и DIMTAD. Эти переменные можно установить с помощью метода SetSystemVariable, доступного из статического класса Application. Например, следующая строка кода устанавливает системную переменную DIMAUNIT (формат единиц для угловых размеров) в радианы (3):

Autodesk.AutoCAD.ApplicationServices.Application.SetSystemVariable("DIMAUNIT", 3);

Примечание: переменная DIMFIT в nanoCAD не реализована.

Стиль размерного текста

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

О линиях выносок размеров

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

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

Ассоциативные размеры

Ассоциативные размеры автоматически изменяют свое расположение, ориентацию и измеренные значения вслед за изменением связанных с ними геометрических объектов. Системная переменная DIMASSOC управляет ассоциативностью размера. Установите системную переменную DIMASSOC в значение 2, чтобы включить ассоциативность для размеров.

Создание размеров

Вы можете создавать линейные, радиальные, угловые и ординатные размеры.

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

Создание линейных размеров

Линейные размеры могут быть параллельными (описываются классом AlignedDimension) или нет (описываются классом RotatedDimension); в первом случае размерная линия параллельна измеряемой линии у объекта, во втором случае она лежит под некоторым углом по отношению к объекту.

После создания экземпляра класса линейного размера (AlignedDimension или RotatedDimension) ему можно задать текст, угол наклона текста или угол наклона размерной линии. На следующих иллюстрациях показано, как тип линейного размера и расположение начальных точек удлинительной линии влияют на угол наклона размерной линии и текста.

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

Размерная линия на линейных размерах добавляется не через набор свойств, а через расширенные данные (Xdata). Имя приложения, отвечающего за размерную линию — ACAD_DSTYLE_DIMJAG_POSITION. Ниже приведен пример структуры Xdata, которую необходимо добавить к линейному размеру.

В примере ниже создается простой повернутый размер в пространстве модели с размерными линиями.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("CreateRotatedDimension")]
public static void CreateRotatedDimension()
{
    // Get the current database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;
    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;
        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;
        // Open the Registered Application table for read
        RegAppTable acRegAppTbl;
        acRegAppTbl = acTrans.GetObject(acCurDb.RegAppTableId,
                                              OpenMode.ForRead) as RegAppTable;
        // Check to see if the app "ACAD_DSTYLE_DIMJAG_POSITION" is
        // registered and if not add it to the RegApp table
        if (acRegAppTbl.Has("ACAD_DSTYLE_DIMJAG_POSITION") == false)
        {
            using (RegAppTableRecord acRegAppTblRec = new RegAppTableRecord())
            {
                acRegAppTblRec.Name = "ACAD_DSTYLE_DIMJAG_POSITION";
                acTrans.GetObject(acCurDb.RegAppTableId, OpenMode.ForWrite);
                acRegAppTbl.Add(acRegAppTblRec);
                acTrans.AddNewlyCreatedDBObject(acRegAppTblRec, true);
            }
        }
        // Create the rotated dimension
        using (RotatedDimension acRotDim = new RotatedDimension())
        {
            acRotDim.XLine1Point = new Point3d(0, 0, 0);
            acRotDim.XLine2Point = new Point3d(6, 3, 0);
            acRotDim.Rotation = 0.707;
            acRotDim.DimLinePoint = new Point3d(0, 5, 0);
            acRotDim.DimensionStyle = acCurDb.Dimstyle;
            // Create a result buffer to define the Xdata
            ResultBuffer acResBuf = new ResultBuffer();
            acResBuf.Add(new TypedValue((int)DxfCode.ExtendedDataRegAppName,
                                                     "ACAD_DSTYLE_DIMJAG_POSITION"));
            acResBuf.Add(new TypedValue((int)DxfCode.ExtendedDataInteger16, 387));
            acResBuf.Add(new TypedValue((int)DxfCode.ExtendedDataInteger16, 3));
            acResBuf.Add(new TypedValue((int)DxfCode.ExtendedDataInteger16, 389));
            acResBuf.Add(new TypedValue((int)DxfCode.ExtendedDataXCoordinate,
                                                     new Point3d(-1.26985, 3.91514, 0)));
            // Attach the Xdata to the dimension
            acRotDim.XData = acResBuf;
            // Add the new object to Model space and the transaction
            acBlkTblRec.AppendEntity(acRotDim);
            acTrans.AddNewlyCreatedDBObject(acRotDim, true);
        }
        // Commit the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Создание радиальных размеров

Радиальные размеры предназначены для измерения радиусов и диаметров дуг и окружностей. Радиальные и диаметральные размеры создаются путем создания экземпляров объектов RadialDimension и DiametricDimension соответственно. Существует несколько вариантов отрисовки размеров в зависимости от габаритов измеряемых окружности или дуги, положения текста размера, значений системных переменных размеров DIMUPT, DIMTOFL, DIMTIH, DIMTOH, DIMJUST и DIMTAD (системные переменные можно получить или задать с помощью методов GetSystemVariable и SetSystemVariable соответственно у статического класса Application).

Примечание: переменная DIMFIT в nanoCAD не реализована.

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

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

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("CreateRadialDimension")]
public static void CreateRadialDimension()
{
    // Get the current database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create the radial dimension
        using (RadialDimension acRadDim = new RadialDimension())
        {
            acRadDim.Center = new Point3d(0, 0, 0);
            acRadDim.ChordPoint = new Point3d(5, 5, 0);
            acRadDim.LeaderLength = 5;
            acRadDim.DimensionStyle = acCurDb.Dimstyle;

            // Add the new object to Model space and the transaction
            acBlkTblRec.AppendEntity(acRadDim);
            acTrans.AddNewlyCreatedDBObject(acRadDim, true);
        }

        // Commit the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Создание угловых размеров

Угловые размеры измеряют угол между двумя линиями или тремя точками. Например, их можно использовать для измерения угла между двумя радиусами окружности. Линия измерения угла образует дугу. Угловые размеры создаются путем создания экземпляров классов LineAngularDimension2 или Point3AngularDimension.

  • LineAngularDimension2. Представляет угловой размер, задаваемый двумя линиями;
  • Point3AngularDimension. Представляет угловой размер, задаваемый тремя точками;

При создании экземпляра классов LineAngularDimension2 или Point3AngularDimension их конструкторы могут принимать ряд необязательных параметров (с предопределенными значениями). При создании углового размера между двумя линиями (LineAngularDimension2) можно указать следующие параметры:

  • Линия продолжения 1 от начала размерной линии к измеряемому объекту (свойство XLine1Start);
  • Линия продолжения 1 от конца размерной линии к измеряемому объекту (свойство XLine1End);
  • Линия продолжения 2 от начала размерной линии к измеряемому объекту (свойство XLine2Start);
  • Линия продолжения 2 от конца размерной линии к измеряемому объекту (свойство XLine2End);

При создании углового размера, заданного тремя точками (Point3AngularDimension) можно указать следующие параметры:

  • Точка центра (пересечения измеряемых отрезков), свойство CenterPoint;
  • Линия продолжения от первой точке к измеряемому объекту (свойство XLine1Point);
  • Линия продолжения от второй точке к измеряемому объекту (свойство XLine2Point); Общими для обоих подвидов угловых размеров являются:
  • Точка на дуге, где будет размещен текст размера (свойство ArcPoint);
  • Текст размера (свойство DimensionText);
  • Стиль размера (свойство DimensionStyleName или DimensionStyle);

В примере ниже создается угловой размер в пространстве модели

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("CreateAngularDimension")]
public static void CreateAngularDimension()
{
    // Get the current database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;
    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;
        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;
        // Create an angular dimension
        using (LineAngularDimension2 acLinAngDim = new LineAngularDimension2())
        {
            acLinAngDim.XLine1Start = new Point3d(0, 5, 0);
            acLinAngDim.XLine1End = new Point3d(1, 7, 0);
            acLinAngDim.XLine2Start = new Point3d(0, 5, 0);
            acLinAngDim.XLine2End = new Point3d(1, 3, 0);
            acLinAngDim.ArcPoint = new Point3d(3, 5, 0);
            acLinAngDim.DimensionStyle = acCurDb.Dimstyle;
            // Add the new object to Model space and the transaction
            acBlkTblRec.AppendEntity(acLinAngDim);
            acTrans.AddNewlyCreatedDBObject(acLinAngDim, true);
        }
        // Commit the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Создание размеров с изломом

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

  • Центр объекта расположен за пределами листа или находится над областью модели, в которой недостаточно места для размещения радиального размера;
  • Объект имеет большой радиус;

Размер с изломом создается путем создания экземпляра класса RadialDimensionLarge. При создании экземпляра класса RadialDimensionLarge его конструкторы могут дополнительно принимать некоторый набор параметров, которые также можно задать с помощью свойств класса:

  • Center : точка центра;
  • ChordPoint : точка на окружности;
  • OverrideCenter : переопределение точки центра;
  • JogPoint : точка, где рисуется излом;
  • JogAngle : угол линии излома;
  • Текст размера (свойство DimensionText);
  • Стиль размера (свойство DimensionStyleName или DimensionStyle);

В примере ниже создается размер с изломом

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("CreateJoggedDimension")]
public static void CreateJoggedDimension()
{
    // Get the current database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a large radius dimension
        using (RadialDimensionLarge acRadDimLrg = new RadialDimensionLarge())
        {
            acRadDimLrg.Center = new Point3d(-3, -4, 0);
            acRadDimLrg.ChordPoint = new Point3d(2, 7, 0);
            acRadDimLrg.OverrideCenter = new Point3d(0, 2, 0);
            acRadDimLrg.JogPoint = new Point3d(1, 4.5, 0);
            acRadDimLrg.JogAngle = 0.707;
            acRadDimLrg.DimensionStyle = acCurDb.Dimstyle;

            // Add the new object to Model space and the transaction
            acBlkTblRec.AppendEntity(acRadDimLrg);
            acTrans.AddNewlyCreatedDBObject(acRadDimLrg, true);
        }

        // Commit the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Создание дуговых размеров

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

  • Center : точка центра;
  • Линия продолжения от первой точке к измеряемому объекту (свойство XLine1Point);
  • Линия продолжения от второй точке к измеряемому объекту (свойство XLine2Point);
  • Точка на дуге, где будет создан текст (свойство ArcPoint);
  • Текст размера (свойство DimensionText);
  • Стиль размера (свойство DimensionStyleName или DimensionStyle);

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

В примере ниже создается размер для длины дуги в пространстве модели

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
 
[CommandMethod("CreateArcLengthDimension")]
public static void CreateArcLengthDimension()
{
    // Get the current database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create an arc length dimension
        using (ArcDimension acArcDim = new ArcDimension(new Point3d(4.5, 1.5, 0),
                                                        new Point3d(8, 4.25, 0),
                                                        new Point3d(0, 2, 0),
                                                        new Point3d(5, 7, 0),
                                                        "<>",
                                                        acCurDb.Dimstyle))
        {

            // Add the new object to Model space and the transaction
            acBlkTblRec.AppendEntity(acArcDim);
            acTrans.AddNewlyCreatedDBObject(acArcDim, true);
        }

        // Commit the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Создание ординатных размеров

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

Ординатные размеры состоят из координат X или Y на выносках. Размеры по ординатам X измеряют расстояние элемента от базовой точки вдоль оси X. Размеры по ординатам Y измеряют такое же расстояние вдоль оси Y.

Координаты измеряются относительно текущей пользовательской системы координат (ПСК). Используются абсолютные значения координат. Текст размера выравнивается по выноске ординаты независимо от ориентации, заданной текущим размерным стилем.

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

  • UsingXAxis : признак, создавать ли размер вдоль оси X (аналогично свойство UsingYAxis);
  • DefiningPoint : базовая точка, для которой считается размер;
  • LeaderEndPoint : точка начала полки выноски;
  • DimensionText : текст размера;
  • Стиль размера (свойство DimensionStyleName или DimensionStyle);

В примере ниже создается ординатный размер в пространстве модели

[CommandMethod("CreateOrdinateDimension")]
public static void CreateOrdinateDimension()
{
    // Get the current database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;
    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;
        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;
        // Create an ordinate dimension
        using (OrdinateDimension acOrdDim = new OrdinateDimension())
        {
            acOrdDim.UsingXAxis = true;
            acOrdDim.DefiningPoint = new Point3d(5, 5, 0);
            acOrdDim.LeaderEndPoint = new Point3d(10, 5, 0);
            acOrdDim.DimensionStyle = acCurDb.Dimstyle;
            // Add the new object to Model space and the transaction
            acBlkTblRec.AppendEntity(acOrdDim);
            acTrans.AddNewlyCreatedDBObject(acOrdDim, true);
        }
        // Commit the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Редактирование размеров

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

  • DimensionStyle : идентификатор (ObjectId) размерного стиля;
  • DimensionStyleName : наименование размерного стиля (предпочтительнее использовать DimensionStyle, т.к. при попытке задания данного свойства могут быть ошибки);
  • DimensionText : позволяет задать пользовательский текст для размера (вместо авто:рассчитываемого значения);
  • HorizontalRotation : угол поворота размера в радианах;
  • Measurement : значение размера в виде числа double;
  • TextPosition : точка, где расположен элемент анотации (текст размера);
  • TextRotation : угол поворота текста размера;

Отображаемое значение размера можно заменить или изменить с помощью свойства DimensionText. Для использования измеренного значения в тексте DimensionText используйте символьную строку "<>" в тексте нового выражения. В примере ниже к значению размера добавляется префикс "The value is "

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
 
[CommandMethod("OverrideDimensionText")]
public static void OverrideDimensionText()
{
    // Get the current database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create the aligned dimension
        using (AlignedDimension acAliDim = new AlignedDimension())
        {
            acAliDim.XLine1Point = new Point3d(5, 3, 0);
            acAliDim.XLine2Point = new Point3d(10, 3, 0);
            acAliDim.DimLinePoint = new Point3d(7.5, 5, 0);
            acAliDim.DimensionStyle = acCurDb.Dimstyle;

            // Override the dimension text
            acAliDim.DimensionText = "The value is <>";

            // Add the new object to Model space and the transaction
            acBlkTblRec.AppendEntity(acAliDim);
            acTrans.AddNewlyCreatedDBObject(acAliDim, true);
        }

        // Commit the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Работа с размерными стилями

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

Новые размеры наследуют настройки активного стиля размеров; в AutoCAD активным стилем считается "Standard". Для задания активного стиля (его идентификатора ObjectId) используйте свойство Dimstyle текущей базы данных.

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

Создание, редактирование и копирование размерных стилей

Для создания нового размерного стиля создайте новый экземпляр классса DimStyleTableRecord и добавьте его в коллекцию размерных стилей DimStyleTable с помощью метода Add. Перед добавлением стиля размеров в таблицу стилей необходимо задать ему имя с помощью свойства Name, требования к имени стандартные (см. статью).

Вы также можете скопировать существующий или переопределённый стиль. Используйте метод CopyFrom для копирования в текущий стиль настроек из целевого размерного стиля (он указывается в аргументе метода). Исходным объектом может быть другой объект DimStyleTableRecord, объект Dimension (иной размер), Tolerance (допуск) или Leader (выноска), или даже объект Database (в этом случае задаются активные настройки размеров, заданные через системные переменные).

Копирование размерных стилей с последующим их переопределением

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

Перед выполнением кода проделайте следующие шаги с чертежом:

  1. Создайте новый чертеж и сделайте его активным;

  2. Создайте произвольный линейный размер. Этот размер должен быть единственным объектом на чертеже;

  3. Измените цвет размерной линии на желтый;

  4. Измените системную переменную DIMCLRD на 5 (синий);

  5. Запустите следующий пример:

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
 
[CommandMethod("CopyDimStyles")]
public static void CopyDimStyles()
{
    // Get the current database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for read
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForRead) as BlockTableRecord;

        object acObj = null;
        foreach (ObjectId acObjId in acBlkTblRec)
        {
            // Get the first object in Model space
            acObj = acTrans.GetObject(acObjId,
                                        OpenMode.ForRead);

            break;
        }

        // Open the DimStyle table for read
        DimStyleTable acDimStyleTbl;
        acDimStyleTbl = acTrans.GetObject(acCurDb.DimStyleTableId,
                                            OpenMode.ForRead) as DimStyleTable;

        string[] strDimStyleNames = new string[3];
        strDimStyleNames[0] = "Style 1 copied from a dim";
        strDimStyleNames[1] = "Style 2 copied from Style 1";
        strDimStyleNames[2] = "Style 3 copied from the running drawing values";

        int nCnt = 0;

        // Keep a reference of the first dimension style for later
        DimStyleTableRecord acDimStyleTblRec1 = null;

        // Iterate the array of dimension style names
        foreach (string strDimStyleName in strDimStyleNames)
        {
            DimStyleTableRecord acDimStyleTblRec;
            DimStyleTableRecord acDimStyleTblRecCopy = null;

            // Check to see if the dimension style exists or not
            if (acDimStyleTbl.Has(strDimStyleName) == false)
            {
                if (acDimStyleTbl.IsWriteEnabled == false) acTrans.GetObject(acCurDb.DimStyleTableId, OpenMode.ForWrite);

                acDimStyleTblRec = new DimStyleTableRecord();
                acDimStyleTblRec.Name = strDimStyleName;

                acDimStyleTbl.Add(acDimStyleTblRec);
                acTrans.AddNewlyCreatedDBObject(acDimStyleTblRec, true);
            }
            else
            {
                acDimStyleTblRec = acTrans.GetObject(acDimStyleTbl[strDimStyleName],
                                                        OpenMode.ForWrite) as DimStyleTableRecord;
            }

            // Determine how the new dimension style is populated
            switch ((int)nCnt)
            {
                // Assign the values of the dimension object to the new dimension style
                case 0:
                    try
                    {
                        // Cast the object to a Dimension
                        Dimension acDim = acObj as Dimension;

                        // Copy the dimension style data from the dimension and
                        // set the name of the dimension style as the copied settings
                        // are unnamed.
                        acDimStyleTblRecCopy = acDim.GetDimstyleData();
                        acDimStyleTblRec1 = acDimStyleTblRec;
                    }
                    catch
                    {
                        // Object was not a dimension
                    }

                    break;

                // Assign the values of the dimension style to the new dimension style
                case 1:
                    acDimStyleTblRecCopy = acDimStyleTblRec1;
                    break;
                // Assign the values of the current drawing to the dimension style
                case 2:
                    acDimStyleTblRecCopy = acCurDb.GetDimstyleData();
                    break;
            }

            // Copy the dimension settings and set the name of the dimension style
            acDimStyleTblRec.CopyFrom(acDimStyleTblRecCopy);
            acDimStyleTblRec.Name = strDimStyleName;

            // Dispose of the copied dimension style
            acDimStyleTblRecCopy.Dispose();

            nCnt = nCnt + 1;
        }

        // Commit the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Откройте диспетчер размерных стилей (команда DIMSTYLE), и вы увидите 3 новых стиля. "Style 1" будет иметь желтую размерную линию, как и "Style 2", а вот "Style 3" будет иметь синюю линию. Это показывает принцип наследования свойств размерных стилей в зависимости от источника копирования.

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

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

  • Dimatfit : тип размещения стрелок размерных линий и текста размера, когда на размерной линии не хватает места;
  • Dimaltrnd : округление альтернативных единиц в размерах;
  • Dimasz : Размер стрелок в размерных линиях и выносках;
  • Dimaunit : формата единиц измерения в угловых размерах;
  • Dimblk1, Dimblk2 : Указывает блоки, используемые для концов стрелок размерной линии;
  • Dimcen : настройка меток и линий центра дуг и окружностей при нанесении на них размера;
  • Dimclrd : цвет размерных линий, стрелок и размерных линий:выносок (радиус, диаметр);
  • Dimclre : цвет выносных линий, маркеров центра и центровых/осевых линий;
  • Dimclrt : цвет текста, относящегося к размерам и допускам;
  • Dimdec : количество знаков после запятой в цифрах линейных размеров;
  • Dimdsep : настройку разделителя в десятичных дробях измерений;
  • Dimexe : величина удлинения выносных линий за размерные линии;
  • Dimexo : величину отступа выносных линий от объекта;
  • Dimfrac : формат дроби, когда для линейных размеров выбран формат "Дюймовые дробные" или "Дробные";
  • Dimltex1, Dimltex2 : задает тип линии для выносных линий размера;
  • Dimlwd : вес размерных линий;
  • Dimlwe : вес выносных линий;
  • Dimjust : выравнивание по горизонтали размерного текста;
  • Dimrnd : настройка округления измерений в размерах;
  • Dimsd1 (Dimsd2) : контроль подавления первой (второй) размерной линии и окончания левой (правой) стрелки;
  • Dimse1 (Dimse2): контроля подавления первой (второй) выносной линии;
  • Dimtad : выравнивание по вертикали размерного текста;
  • Dimtdec : настройки количества десятичных знаков в значениях допуска основных единиц измерения;
  • Dimtfac : настройку высоты текста допуска и дробей;
  • Dimlunit : формат единиц измерения в неугловых размерах;
  • Dimtih : ориентация размерного текста для всех типов размеров, кроме ординатных, если текст вписывается внутри выносных линий;
  • Dimtm : минимальне (нижнее) значение предела допуска;
  • Dimtmove : настройка перемещения текста с позиции по умолчанию;
  • Dimtofl : настройка отрисовки размерных линий между выносными, когда размерный текст находится за пределами выносных линий;
  • Dimtoh : положение текста размеров за пределами выносных линий для всех типов размеров, кроме ординатных;
  • Dimtol : признак отображения текста допуска в измерениях;
  • Dimtolj : выравнивание текста допуска по вертикали по отношению к тексту размера;
  • Dimtp : максимальное (верхнее) значение предела допуска;
  • Dimtxt : высота (величина) размерного текста;
  • Dimzin : настройки подавления нулей в значениях допуска линейных размеров;
  • Prefix : префикс (приставка) к значениям размеров;
  • Suffix : суффикс к значениям размеров;

Свойства ниже характерны только для объектов размеров, не размерных стилей:

  • TextPrecision : точность отображения текстового значения для угловых размеров;

  • TextPosition : точка расположения текста размера;

  • TextRotation : поворот текста размера в радианах;

Примечание: в nanoCAD .NET API свойства Dimtoh, TextPrecision не реализованы;

Добавление суффикса для линейного размера

В примере ниже редактируется формат отображения линейных размеров, добавляется суффикс, равный значению, которое пользователь введёт из строки (обработка с помощью Editor.GetString)

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.EditorInput;

[CommandMethod("AddDimensionTextSuffix")]
public static void AddDimensionTextSuffix()
{
    // Get the current database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;
    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;
        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;
        // Create the aligned dimension
        using (AlignedDimension acAliDim = new AlignedDimension())
        {
            acAliDim.XLine1Point = new Point3d(0, 5, 0);
            acAliDim.XLine2Point = new Point3d(5, 5, 0);
            acAliDim.DimLinePoint = new Point3d(5, 7, 0);
            acAliDim.DimensionStyle = acCurDb.Dimstyle;
            // Add the new object to Model space and the transaction
            acBlkTblRec.AppendEntity(acAliDim);
            acTrans.AddNewlyCreatedDBObject(acAliDim, true);
            // Append a suffix to the dimension text
            PromptStringOptions pStrOpts = new PromptStringOptions("");
            pStrOpts.Message = "\\nEnter a new text suffix for the dimension: ";
            pStrOpts.AllowSpaces = true;
            PromptResult pStrRes = acDoc.Editor.GetString(pStrOpts);
            if (pStrRes.Status == PromptStatus.OK)
            {
                acAliDim.Suffix = pStrRes.StringResult;
            }
        }
        // Commit the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Размеры в модели и на листах

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

Если вы создаете размер в пространстве листа, для объекта расположенного в пространстве модели, размер в пространстве листа не изменяется при использовании команд редактирования или изменении масштаба отображения в видовом окне пространства модели. Расположение размеров в пространстве листа также остается неизменным при переключении вида из пространства листа в пространство модели.

Создание выносок

Выноска — это линия, соединяющая некоторый анонативный элемент с объектом на чертеже. Выноски и их аннотации ассоциативны, это значит, что если вы изменяете аннотацию, выноска обновляется соответствующим образом. Не путайте объект «Выноска» с линией-выноской, которая создается как часть размерной линии.

Создание линий выносок

Вы можете создать выноску из любой точки или для любого элемента чертежа и в дальнейшем отредактировать её вид. Линия выноски может быть как простым отрезком, так и сплайновой кривой. Цвет выноски определяется свойством цвета линии. Масштаб выноски определяется параметром размерного масштаба, заданным в активном размерном стиле. Тип и размер стрелки, если она присутствует на выноске, также определяется активным размерным стилем.

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

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

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

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("CreateLeader")]
public static void CreateLeader()
{
    // Get the current database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create the leader
        using (Leader acLdr = new Leader())
        {
            acLdr.AppendVertex(new Point3d(0, 0, 0));
            acLdr.AppendVertex(new Point3d(4, 4, 0));
            acLdr.AppendVertex(new Point3d(4, 5, 0));
            acLdr.HasArrowHead = true;

            // Add the new object to Model space and the transaction
            acBlkTblRec.AppendEntity(acLdr);
            acTrans.AddNewlyCreatedDBObject(acLdr, true);
        }

        // Commit the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Добавление анотации к выноске

В качестве анотации для выноски могут выступать объекты типа допуск (класс Tolerance), многострочный текст (класс MText), вхождения блоков (класс BlockReference). Вы можете создать новый объект аннотации или добавить копию существующего объекта аннотации. Аннотация добавляется к выноске путем присвоения идентификатора объекта (ObjectId) аннотации свойству Annotation.

Ассоциативность выносок

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

Удаление любого из составляющих выноску объектов с чертежа с помощью методов Erase, Add (добавление в состав Блока) или WBlock нарушит ассоциативность. Если выноска и ее аннотация копируются вместе в рамках одной операции, новая копия будет ассоциативной. Если они копируются отдельно, они будут неассоциативными. Если ассоциативность нарушена по какой-либо причине, например, при копировании только объекта WBlock или при удалении аннотации, полка будет также удалена с выноски.

Связывание анотации с выноской

В примере ниже создается выноска с многострочным текстом в качестве анотации

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
 
[CommandMethod("AddLeaderAnnotation")]
public static void AddLeaderAnnotation()
{
    // Get the current database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create the MText annotation
        using (MText acMText = new MText())
        {
            acMText.Contents = "Hello, World.";
            acMText.Location = new Point3d(5, 5, 0);
            acMText.Width = 2;

            // Add the new object to Model space and the transaction
            acBlkTblRec.AppendEntity(acMText);
            acTrans.AddNewlyCreatedDBObject(acMText, true);

            // Create the leader with annotation
            using (Leader acLdr = new Leader())
            {
                acLdr.AppendVertex(new Point3d(0, 0, 0));
                acLdr.AppendVertex(new Point3d(4, 4, 0));
                acLdr.AppendVertex(new Point3d(4, 5, 0));
                acLdr.HasArrowHead = true;

                // Add the new object to Model space and the transaction
                acBlkTblRec.AppendEntity(acLdr);
                acTrans.AddNewlyCreatedDBObject(acLdr, true);

                // Attach the annotation after the leader object is added
                acLdr.Annotation = acMText.ObjectId;
                acLdr.EvaluateLeader();
            }
        }

        // Commit the changes and dispose of the transaction
        acTrans.Commit();
    }
}

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

Редактирование асоциативности выносок

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

Хотя текстовая аннотация создаётся с использованием системных переменных DIMCLRT, DIMTXT и DIMTXSTY для задания её цвета, высоты и стиля текста, эти системные переменные не могут её изменить, поскольку она не является разновидностью размера. Текстовую аннотацию необходимо редактировать так же, как и любой другой объект MText.

Используйте метод EvaluateLeader для обновления связи выноски с связанной с ней аннотацией.

Редактирование выносок

Любые изменения анотации, влияющие на её положение, затрагивают положение конечной точки связанного с ней выноски. Кроме того, вращение аннотации приводит к вращению линии-полки выноски (если таковая имеется).

При масштабировании выноски аннотация останется в том же положении относительно конечной точки выноски, и её масштабирование не произойдет. Помимо масштабирования, вы также можете перемещать, зеркально отображать и вращать выноску. Используйте метод TransformBy для редактирования положения выноски. Связанную аннотацию редактируйте, используя её свойства и методы для соответствующего класса.

Использование допусков

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

Создание допусков

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

  • Text - Текстовая строка, содержащая символ допуска;

  • Location - Точка вставки;

  • Normal - Вектор нормали;

  • Direction - Вектор направления;

В примере ниже создается допуск в пространстве модели

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("CreateGeometricTolerance")]
public static void CreateGeometricTolerance()
{
    // Get the current database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create the Geometric Tolerance (Feature Control Frame)
        using (FeatureControlFrame acFcf = new FeatureControlFrame())
        {
            acFcf.Text = "{\\Fgdt;j}%%v{\\Fgdt;n}0.001%%v%%v%%v%%v";
            acFcf.Location = new Point3d(5, 5, 0);

            // Add the new object to Model space and the transaction
            acBlkTblRec.AppendEntity(acFcf);
            acTrans.AddNewlyCreatedDBObject(acFcf, true);
        }

        // Commit the changes and dispose of the transaction
        acTrans.Commit();
    }
}

Редактирование допусков

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

  • DIMCLRD : цвет рамки допуска;
  • DIMCLRT : цвет текста допуска;
  • DIMGAP : размер отступа между рамкой допуска и текстом внутри;
  • DIMTXT : размер текста в допуске;
  • DIMTXTSTY : стиль текста допуска;

Примечание: в nanoCAD отсутствует системная переменная DIMTXTSTY, задающая стиль текста допуска.

Работа в трехмерном пространстве

В данном разделе будет рассмотрено программное взаимодействие с трехмерными координатами и трехмерными объектами (солидами)

Использование пространственных координат

Ввод трехмерных координат мировой системы координат (WCS) аналогичен вводу двумерных координат WCS. Помимо значений X и Y, вы также указываете значение Z. Двумерные координаты представляются структурой Point2d, а для представления трехмерных координат используется структура Point3d. Большинство свойств и методов объектов в AutoCAD .NET API используют трехмерные координаты.

В примере ниже создаются 2 трехмерные полилинии, первая полилиния -- классическая плоская полилиния на фиксированной отметке (описывается классом Polyline), вторая -- трехмерная полилиния (описывается классом Polyline3d). После создания объектов показывается механизм чтения координат созданных полилиний, информация о количество точек выводится в диалоговые окна.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("Polyline_2D_3D")]
public static void Polyline_2D_3D()
{
    // Get the current document and database, and start a transaction
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table record for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;
        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;
        // Create a polyline with two segments (3 points)
        using (Polyline acPoly = new Polyline())
        {
            acPoly.AddVertexAt(0, new Point2d(1, 1), 0, 0, 0);
            acPoly.AddVertexAt(1, new Point2d(1, 2), 0, 0, 0);
            acPoly.AddVertexAt(2, new Point2d(2, 2), 0, 0, 0);
            acPoly.ColorIndex = 1;
            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acPoly);
            acTrans.AddNewlyCreatedDBObject(acPoly, true);
            // Create a 3D polyline with two segments (3 points)
            using (Polyline3d acPoly3d = new Polyline3d())
            {
                acPoly3d.ColorIndex = 5;
                // Add the new object to the block table record and the transaction
                acBlkTblRec.AppendEntity(acPoly3d);
                acTrans.AddNewlyCreatedDBObject(acPoly3d, true);
                // Before adding vertexes, the polyline must be in the drawing
                Point3dCollection acPts3dPoly = new Point3dCollection();
                acPts3dPoly.Add(new Point3d(1, 1, 0));
                acPts3dPoly.Add(new Point3d(2, 1, 0));
                acPts3dPoly.Add(new Point3d(2, 2, 0));
                foreach (Point3d acPt3d in acPts3dPoly)
                {
                    using (PolylineVertex3d acPolVer3d = new PolylineVertex3d(acPt3d))
                    {
                        acPoly3d.AppendVertex(acPolVer3d);
                        acTrans.AddNewlyCreatedDBObject(acPolVer3d, true);
                    }
                }
                // Get the coordinates of the lightweight polyline
                Point2dCollection acPts2d = new Point2dCollection();
                for (int nCnt = 0; nCnt \< acPoly.NumberOfVertices; nCnt++)
                {
                    acPts2d.Add(acPoly.GetPoint2dAt(nCnt));
                }
                // Get the coordinates of the 3D polyline
                Point3dCollection acPts3d = new Point3dCollection();
                foreach (ObjectId acObjIdVert in acPoly3d)
                {
                    PolylineVertex3d acPolVer3d;
                    acPolVer3d = acTrans.GetObject(acObjIdVert,
                                                    OpenMode.ForRead) as PolylineVertex3d;
                    acPts3d.Add(acPolVer3d.Position);
                }
                // Display the Coordinates
                Application.ShowAlertDialog("2D polyline (red): \\n" +
                                            acPts2d[0].ToString() + "\\n" +
                                            acPts2d[1].ToString() + "\\n" +
                                            acPts2d[2].ToString());
                Application.ShowAlertDialog("3D polyline (blue): \\n" +
                                            acPts3d[0].ToString() + "\\n" +
                                            acPts3d[1].ToString() + "\\n" +
                                            acPts3d[2].ToString());
            }
        }
        // Save the new object to the database
        acTrans.Commit();
    }
}

Задание пользовательских систем координат

Вы можете определить пользовательскую систему координат (ПСК), чтобы переопределить положение начальной точки (0, 0, 0) и ориентацию плоскости XY и оси Z. Вы можете разместить ПСК в любом месте трехмерного пространства чертежа, а также создать их столько, сколько вам нужно. Ввод координат и их отображение будут относительными по отношению к текущей ПСК.

Чтобы включить показ начала координат и ориентацию ПСК, вы можете отобразить значок ПСК в её начальной точке, используя свойство IconAtOrigin для текущего видового экрана Viewport или через системную переменную UCSICON. Если значок ПСК включен (свойство IconVisible) и не отображается в начале координат, он отображается в координатах МСК (мировой системы координат), определенных системной переменной UCSORG.

Вы можете создать новую пользовательскую систему координат, используя метод Add коллекции UCSTable. Этот метод принимает на вход четыре значения: координаты начала координат, координаты по осям X и Y (задающие векторы направления), а также имя пользовательской системы координат. Имя подчиняется единым правилам для неграфических элементов (см. статью).

Чтобы сделать пользовательскую систему координат активной, используйте свойство ActiveUCS у текущего документа AutoCAD (описывается классом Document). Если в определение ПСК были внесены изменения, то необходимо повторить процедуру задания свойства ActiveUCS. В примере ниже создается новая ПСК, делается активной с выводом модального информационного окна, затем у Пользователя запрашивается точка в пространстве чертежа и другом модальном окне выводятся её координаты в значениях текущей ПСК и МСК (мировой системы координат)

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
 
[CommandMethod("NewUCS")]
public static void NewUCS()
{
    // Get the current document and database, and start a transaction
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the UCS table for read
        UcsTable acUCSTbl;
        acUCSTbl = acTrans.GetObject(acCurDb.UcsTableId,
                                        OpenMode.ForRead) as UcsTable;

        UcsTableRecord acUCSTblRec;

        // Check to see if the "New_UCS" UCS table record exists
        if (acUCSTbl.Has("New_UCS") == false)
        {
            acUCSTblRec = new UcsTableRecord();
            acUCSTblRec.Name = "New_UCS";

            // Open the UCSTable for write
            acTrans.GetObject(acCurDb.UcsTableId, OpenMode.ForWrite);

            // Add the new UCS table record
            acUCSTbl.Add(acUCSTblRec);
            acTrans.AddNewlyCreatedDBObject(acUCSTblRec, true);
        }
        else
        {
            acUCSTblRec = acTrans.GetObject(acUCSTbl["New_UCS"],
                                            OpenMode.ForWrite) as UcsTableRecord;
        }

        acUCSTblRec.Origin = new Point3d(4, 5, 3);
        acUCSTblRec.XAxis = new Vector3d(1, 0, 0);
        acUCSTblRec.YAxis = new Vector3d(0, 1, 0);

        // Open the active viewport
        ViewportTableRecord acVportTblRec;
        acVportTblRec = acTrans.GetObject(acDoc.Editor.ActiveViewportId,
                                            OpenMode.ForWrite) as ViewportTableRecord;

        // Display the UCS Icon at the origin of the current viewport
        acVportTblRec.IconAtOrigin = true;
        acVportTblRec.IconEnabled = true;

        // Set the UCS current
        acVportTblRec.SetUcs(acUCSTblRec.ObjectId);
        acDoc.Editor.UpdateTiledViewportsFromDatabase();

        // Display the name of the current UCS
        UcsTableRecord acUCSTblRecActive;
        acUCSTblRecActive = acTrans.GetObject(acVportTblRec.UcsName,
                                                OpenMode.ForRead) as UcsTableRecord;

        Application.ShowAlertDialog("The current UCS is: " +
                                    acUCSTblRecActive.Name);

        PromptPointResult pPtRes;
        PromptPointOptions pPtOpts = new PromptPointOptions("");

        // Prompt for a point
        pPtOpts.Message = "\nEnter a point: ";
        pPtRes = acDoc.Editor.GetPoint(pPtOpts);

        Point3d pPt3dWCS;
        Point3d pPt3dUCS;

        // If a point was entered, then translate it to the current UCS
        if (pPtRes.Status == PromptStatus.OK)
        {
            pPt3dWCS = pPtRes.Value;
            pPt3dUCS = pPtRes.Value;

            // Translate the point from the current UCS to the WCS
            Matrix3d newMatrix = new Matrix3d();
            newMatrix = Matrix3d.AlignCoordinateSystem(Point3d.Origin,
                                                        Vector3d.XAxis,
                                                        Vector3d.YAxis,
                                                        Vector3d.ZAxis,
                                                        acVportTblRec.Ucs.Origin,
                                                        acVportTblRec.Ucs.Xaxis,
                                                        acVportTblRec.Ucs.Yaxis,
                                                        acVportTblRec.Ucs.Zaxis);

            pPt3dWCS = pPt3dWCS.TransformBy(newMatrix);

            Application.ShowAlertDialog("The WCS coordinates are: \n" +
                                        pPt3dWCS.ToString() + "\n" +
                                        "The UCS coordinates are: \n" +
                                        pPt3dUCS.ToString());
        }

        // Save the new objects to the database
        acTrans.Commit();
    }
}

Преобразования координат

Метод TransformBy также может выполнять преобразования координат из одной ПСК в другую, для этого у структуры матрицы трансформации Matrix3d имеется метод AlignCoordinateSystem, требующий указания нескольких аргументов:

  • Координаты точки начала исходной системы координат;
  • Три вектора в пространстве, задающие оси ПСК по компонентам X, Y, Z исходной системы координат;
  • Координаты точки начала целевой системы координат;
  • Три вектора в пространстве, задающие оси ПСК по компонентам X, Y, Z целевой системы координат;

В случае плоской матрицы (Matrix2d), векторов будет по два, а точка начала будет представлена плоской точкой, а не трехмерной. Необходимо также ввести некоторые термины по системам координат:

  • WCS (World coordinate system, МСК) : Мировая система координат. Все остальные системы координат определяются относительно WCS, она которая никогда не изменяется. Значения, измеренные относительно WCS, остаются постоянными при изменении других систем координат. Все точки, передаваемые в методы и свойства .NET API, выражаются в WCS, если не указано иное;
  • UCS (User coordinate system , ПСК) : Пользовательская система координат (UCS). UCS используются для упрощения задач разработки чертежей. Все точки, передаваемые командам AutoCAD, включая точки, возвращаемые различными функциями, являются точками в текущей ПСК (если пользователь не поставил перед ними * в командной строке). Если вы хотите, чтобы ваше приложение отправляло координаты в WCS, OCS или DCS командам AutoCAD, вы должны сначала преобразовать их в UCS (ПСК), вызвав метод преобразования, а затем преобразовать объект Point3d или Point2d с помощью метода TransformBy, представляющего значение компонентов координат;
  • OCS (Object coordinate system) :также известная, как система координат элемента или ECS: значения точек, заданные определенными методами и свойствами для объектов Polyline2d и Polyline, выражаются в этой системе координат относительно объекта. Эти точки обычно преобразуются в WCS, текущую UCS или текущую DCS в зависимости от предполагаемого использования объекта. И наоборот, точки в WCS, UCS или DCS должны быть преобразованы в OCS перед записью в базу данных с помощью тех же свойств. При преобразовании координат в OCS или из OCS необходимо учитывать нормаль OCS;
  • DCS (Display coordinate system): система координат, в которую объекты преобразуются при отрисовке на экране. Начало DCS находится в точке, хранимой в системной переменной TARGET, а направление оси Z соответствует направлению данного видового экрана (ВЭ). Другими словами, ВЭ всегда является плоским видом для данной DCS.

Для получения матрицы преобразования между мировыми координатами и DCS можно использовать следующий код:

Document acDoc;
Database acCurDb;
using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
{
    // Get the current view
    using (ViewTableRecord acView = acDoc.Editor.GetCurrentView())
    {
        Extents3d eExtents;
        // Translate WCS coordinates to DCS
        Matrix3d matWCS2DCS;
        matWCS2DCS = Matrix3d.PlaneToWorld(acView.ViewDirection);
        matWCS2DCS = Matrix3d.Displacement(acView.Target : Point3d.Origin) * matWCS2DCS;
        matWCS2DCS = Matrix3d.Rotation(:acView.ViewTwist,
                                        acView.ViewDirection,
                                        acView.Target) * matWCS2DCS;
    }
}
  • PSDCS (Paper space DCS) : Система координат листа. По сути, это двумерное преобразование, где координаты X и Y отмасштабированы относительно целевой СК (здесь, DCS). Поэтому ее можно использовать для определения коэффициента масштабирования между двумя системами координат. Координаты в данной СК могут быть преобразованы только в DCS модели и аналогично обратно;

Преобразование из OCS в WCS

В примере ниже создается полилиния в пространстве модели. Выводится информация, какие координаты у первой вершины полилинии в системе координат полилинии и в мировой системе координат

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
 
[CommandMethod("TranslateCoordinates")]
public static void TranslateCoordinates()
{
    // Get the current document and database, and start a transaction
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table record for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                     OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a 2D polyline with two segments (3 points)
        using (Polyline2d acPoly2d = new Polyline2d())
        {
            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acPoly2d);
            acTrans.AddNewlyCreatedDBObject(acPoly2d, true);

            // Before adding vertexes, the polyline must be in the drawing
            Point3dCollection acPts2dPoly = new Point3dCollection();
            acPts2dPoly.Add(new Point3d(1, 1, 0));
            acPts2dPoly.Add(new Point3d(1, 2, 0));
            acPts2dPoly.Add(new Point3d(2, 2, 0));
            acPts2dPoly.Add(new Point3d(3, 2, 0));
            acPts2dPoly.Add(new Point3d(4, 4, 0));

            foreach (Point3d acPt3d in acPts2dPoly)
            {
                Vertex2d acVer2d = new Vertex2d(acPt3d, 0, 0, 0, 0);
                acPoly2d.AppendVertex(acVer2d);
                acTrans.AddNewlyCreatedDBObject(acVer2d, true);
            }

            // Set the normal of the 2D polyline
            acPoly2d.Normal = new Vector3d(0, 1, 2);

            // Get the first coordinate of the 2D polyline
            Point3dCollection acPts3d = new Point3dCollection();
            Vertex2d acFirstVer = null;

            foreach (ObjectId acObjIdVert in acPoly2d)
            {
                acFirstVer = acTrans.GetObject(acObjIdVert,
                                               OpenMode.ForRead) as Vertex2d;

                acPts3d.Add(acFirstVer.Position);

                break;
            }

            // Get the first point of the polyline and 
            // use the eleveation for the Z value
            Point3d pFirstVer = new Point3d(acFirstVer.Position.X,
                                            acFirstVer.Position.Y,
                                            acPoly2d.Elevation);

            // Translate the OCS to WCS
            Matrix3d mWPlane = Matrix3d.WorldToPlane(acPoly2d.Normal);
            Point3d pWCSPt = pFirstVer.TransformBy(mWPlane);

            Application.ShowAlertDialog("The first vertex has the following " +
                                        "coordinates:" +
                                        "\nOCS: " + pFirstVer.ToString() +
                                        "\nWCS: " + pWCSPt.ToString());
        }

        // Save the new objects to the database
        acTrans.Commit();
    }
}

Создание трехмерных объектов

В AutoCAD поддерживается создание и редактирование через API следующих типов трехмерных объектов:

  • каркасные объекты;
  • поверхности;
  • тремерных солиды;

Каркасные объекты

Любые плоские объекты можно рисовать в трехмерном пространстве несколькими способами:

  • Указывать при создании 3D-точки (за отметку Z будет принята только одна);
  • Установка свойства Elevation графического объекта или создание ПСК на заданной отметке;
  • Перемещение объекты на новую высоту через метод TransformBy;

Объекты, которые являются трехмерными по умолчанию можно сразу создавать для пространственных точек - отрезки (Line), 3d:полилинии (Polyline3d).

Полигональные сети

Полигональная сеть (класс PolygonMesh) представляет собой поверхность (совокупность 3D-граней) в виде регулярной сети с заданным числом строк (N штук) и столбцов (M штук). Информация об отметках хранится в виде матрицы (массива) размером M x N.

Для создания полигональной сети создайте экземпляр класса PolygonMesh, а затем задайте плотность сети (параметры M, N) и информацию об отметках. Конструктор класса PolygonMesh также имеет перегрузку для всех параметров:

  • тип сглаживания;
  • Количество вершин в направлении M;
  • Количество вершин в направлении N;
  • Коллекция вершин (Point3dCollection);
  • Флаг замкнутости сети в направлении M;
  • Флаг замкнутости сети в направлении N;

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

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("Create3DMesh")]
public static void Create3DMesh()
{
    // Get the current document and database, and start a transaction
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table record for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a polygon mesh
        using (PolygonMesh acPolyMesh = new PolygonMesh())
        {
            acPolyMesh.MSize = 4;
            acPolyMesh.NSize = 4;

            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acPolyMesh);
            acTrans.AddNewlyCreatedDBObject(acPolyMesh, true);

            // Before adding vertexes, the polyline must be in the drawing
            Point3dCollection acPts3dPMesh = new Point3dCollection();
            acPts3dPMesh.Add(new Point3d(0, 0, 0));
            acPts3dPMesh.Add(new Point3d(2, 0, 1));
            acPts3dPMesh.Add(new Point3d(4, 0, 0));
            acPts3dPMesh.Add(new Point3d(6, 0, 1));

            acPts3dPMesh.Add(new Point3d(0, 2, 0));
            acPts3dPMesh.Add(new Point3d(2, 2, 1));
            acPts3dPMesh.Add(new Point3d(4, 2, 0));
            acPts3dPMesh.Add(new Point3d(6, 2, 1));

            acPts3dPMesh.Add(new Point3d(0, 4, 0));
            acPts3dPMesh.Add(new Point3d(2, 4, 1));
            acPts3dPMesh.Add(new Point3d(4, 4, 0));
            acPts3dPMesh.Add(new Point3d(6, 4, 0));

            acPts3dPMesh.Add(new Point3d(0, 6, 0));
            acPts3dPMesh.Add(new Point3d(2, 6, 1));
            acPts3dPMesh.Add(new Point3d(4, 6, 0));
            acPts3dPMesh.Add(new Point3d(6, 6, 0));

            foreach (Point3d acPt3d in acPts3dPMesh)
            {
                PolygonMeshVertex acPMeshVer = new PolygonMeshVertex(acPt3d);
                acPolyMesh.AppendVertex(acPMeshVer);
                acTrans.AddNewlyCreatedDBObject(acPMeshVer, true);
            }
        }

        // Open the active viewport
        ViewportTableRecord acVportTblRec;
        acVportTblRec = acTrans.GetObject(acDoc.Editor.ActiveViewportId,
                                            OpenMode.ForWrite) as ViewportTableRecord;

        // Rotate the view direction of the current viewport
        acVportTblRec.ViewDirection = new Vector3d(-1, -1, 1);
        acDoc.Editor.UpdateTiledViewportsFromDatabase();

        // Save the new objects to the database
        acTrans.Commit();
    }
}

Многогранные сети

Многогранная сеть представляет собой триангулированную поверхность, представленную набором вершин и связанных с ними гранями. Создание многогранной сети аналогично созданию полигональной сети. Вы создаете объект класса PolyFaceMesh, а затем используйте свойства и методы класса для задания геометрии. Чтобы добавить вершину к многогранной сети, создайте объект PolyFaceMeshVertex и добавьте его к сети PolyFaceMesh с помощью метода AppendVertex.

Информация о гранях задается с помощью метода AppendFaceRecord, который принимает в себя объект класса FaceRecord. Рёбрам грани можно задать видимость, конкретный слой или цвет.

В примере ниже создается многогранная сеть в пространстве модели.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
 
[CommandMethod("CreatePolyfaceMesh")]
public static void CreatePolyfaceMesh()
{
    // Get the current document and database, and start a transaction
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table record for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a polyface mesh
        using (PolyFaceMesh acPFaceMesh = new PolyFaceMesh())
        {
            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acPFaceMesh);
            acTrans.AddNewlyCreatedDBObject(acPFaceMesh, true);

            // Before adding vertexes, the polyline must be in the drawing
            Point3dCollection acPts3dPFMesh = new Point3dCollection();
            acPts3dPFMesh.Add(new Point3d(4, 7, 0));
            acPts3dPFMesh.Add(new Point3d(5, 7, 0));
            acPts3dPFMesh.Add(new Point3d(6, 7, 0));

            acPts3dPFMesh.Add(new Point3d(4, 6, 0));
            acPts3dPFMesh.Add(new Point3d(5, 6, 0));
            acPts3dPFMesh.Add(new Point3d(6, 6, 1));

            foreach (Point3d acPt3d in acPts3dPFMesh)
            {
                PolyFaceMeshVertex acPMeshVer = new PolyFaceMeshVertex(acPt3d);
                acPFaceMesh.AppendVertex(acPMeshVer);
                acTrans.AddNewlyCreatedDBObject(acPMeshVer, true);
            }

            using (FaceRecord acFaceRec1 = new FaceRecord(1, 2, 5, 4))
            {
                acPFaceMesh.AppendFaceRecord(acFaceRec1);
                acTrans.AddNewlyCreatedDBObject(acFaceRec1, true);
            }

            using (FaceRecord acFaceRec2 = new FaceRecord(2, 3, 6, 5))
            {
                acPFaceMesh.AppendFaceRecord(acFaceRec2);
                acTrans.AddNewlyCreatedDBObject(acFaceRec2, true);
            }
        }

        // Open the active viewport
        ViewportTableRecord acVportTblRec;
        acVportTblRec = acTrans.GetObject(acDoc.Editor.ActiveViewportId,
                                            OpenMode.ForWrite) as ViewportTableRecord;

        // Rotate the view direction of the current viewport
        acVportTblRec.ViewDirection = new Vector3d(-1, -1, 1);
        acDoc.Editor.UpdateTiledViewportsFromDatabase();

        // Save the new objects to the database
        acTrans.Commit();
    }
}

Сети SubDMesh и грани Face

Несмотря на то, что PolyFaceMesh называется "многогранной", всё же она имеет ограничение на величину граней в ~65 тысяч объектов. Вероятно всего, вы не не столкнетесь в обычной практике программирования с этим лимитом, но в противном случае важно знать, что существует ещё один трехмерный объект "Сеть" с неограниченным числом граней, описываемый классом SubDMesh.

Объект "3D-грань" описывается классом Face и может сотоять из 3 или 4 вершин. В примере "Чтение сети" ниже именно она и создается на гранях сети SubDMesh.

Создание сети

В примере ниже создается многогранная сеть в пространстве модели.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("CreatePolyfaceMesh")]
public static void CreatePolyfaceMesh()
{
    // Get the current document and database, and start a transaction
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table record for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a polyface mesh
        using (PolyFaceMesh acPFaceMesh = new PolyFaceMesh())
        {
            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acPFaceMesh);
            acTrans.AddNewlyCreatedDBObject(acPFaceMesh, true);

            // Before adding vertexes, the polyline must be in the drawing
            Point3dCollection acPts3dPFMesh = new Point3dCollection();
            acPts3dPFMesh.Add(new Point3d(4, 7, 0));
            acPts3dPFMesh.Add(new Point3d(5, 7, 0));
            acPts3dPFMesh.Add(new Point3d(6, 7, 0));

            acPts3dPFMesh.Add(new Point3d(4, 6, 0));
            acPts3dPFMesh.Add(new Point3d(5, 6, 0));
            acPts3dPFMesh.Add(new Point3d(6, 6, 1));

            foreach (Point3d acPt3d in acPts3dPFMesh)
            {
                PolyFaceMeshVertex acPMeshVer = new PolyFaceMeshVertex(acPt3d);
                acPFaceMesh.AppendVertex(acPMeshVer);
                acTrans.AddNewlyCreatedDBObject(acPMeshVer, true);
            }

            using (FaceRecord acFaceRec1 = new FaceRecord(1, 2, 5, 4))
            {
                acPFaceMesh.AppendFaceRecord(acFaceRec1);
                acTrans.AddNewlyCreatedDBObject(acFaceRec1, true);
            }

            using (FaceRecord acFaceRec2 = new FaceRecord(2, 3, 6, 5))
            {
                acPFaceMesh.AppendFaceRecord(acFaceRec2);
                acTrans.AddNewlyCreatedDBObject(acFaceRec2, true);
            }
        }

        // Open the active viewport
        ViewportTableRecord acVportTblRec;
        acVportTblRec = acTrans.GetObject(acDoc.Editor.ActiveViewportId,
                                            OpenMode.ForWrite) as ViewportTableRecord;

        // Rotate the view direction of the current viewport
        acVportTblRec.ViewDirection = new Vector3d(-1, -1, 1);
        acDoc.Editor.UpdateTiledViewportsFromDatabase();

        // Save the new objects to the database
        acTrans.Commit();
    }
}

Чтение сети

Чтение сети имеет особенность, дело в том, что информация о гранях сохраняется в формат одномерного массива int, а грань может состоять из 3 или 4 вершин. В этом случае чтение информации о гранях должно быть аккуратным. В примере ниже приводится такая реализация. Для существующей сети в месте вершин создаются точки красного цвета, а также создаются грани типа Face.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.EditorInput;

[CommandMethod("ExtractSubDMesh")]
public void ExtractSubDMesh()
{
    Document acCurDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acCurDoc.Database;

    PromptEntityOptions selMeshOpts = new PromptEntityOptions("\nSelect a SubDMesh");
    selMeshOpts.AddAllowedClass(typeof(SubDMesh), true);
    PromptEntityResult selMeshResult = acCurDoc.Editor.GetEntity(selMeshOpts);
    if (selMeshResult.Status != PromptStatus.OK) return;

    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table record for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;


        SubDMesh? meshEntity = acTrans.GetObject(selMeshResult.ObjectId, OpenMode.ForRead) as SubDMesh;
        if (meshEntity == null) return;

        acCurDoc.Editor.WriteMessage($"SubDMesh Vertices {meshEntity.NumberOfVertices}");
        acCurDoc.Editor.WriteMessage($"SubDMesh Faces {meshEntity.NumberOfFaces}");
        acCurDoc.Editor.WriteMessage($"SubDMesh Faces's vertextes {meshEntity.FaceArray.Count}");

        for (int vertexIndex = 0; vertexIndex < meshEntity.NumberOfVertices; vertexIndex++)
        {
            DBPoint meshVertex = new DBPoint(meshEntity.Vertices[vertexIndex]);
            meshVertex.ColorIndex = 1;

            acBlkTblRec.AppendEntity(meshVertex);
            acTrans.AddNewlyCreatedDBObject(meshVertex, true);
        }

        // Because the faces can contains 3 or 4 vertexes, we need read it with accuracy in tmpFaceRecordDef
        int[] tmpFaceRecordDef = new int[] { };
        int tmpCounter = -1;
        for (int faceVertexIndex = 0; faceVertexIndex < meshEntity.FaceArray.Count; faceVertexIndex++)
        {
            int data = meshEntity.FaceArray[faceVertexIndex];

            if (tmpFaceRecordDef.Length == 0) tmpFaceRecordDef = new int[data];
            else tmpFaceRecordDef[tmpCounter] = data;

            tmpCounter++;

            if (tmpCounter == tmpFaceRecordDef.Length)
            {
                faceProcessing();
            }
        }
        faceProcessing();

        void faceProcessing()
        {
            if (tmpFaceRecordDef.Length < 3) return;
            // Create a 3dface
            Face meshFace = new Face();
            Point3d vertex1 = meshEntity.Vertices[tmpFaceRecordDef[0]];
            Point3d vertex2 = meshEntity.Vertices[tmpFaceRecordDef[1]];
            Point3d vertex3 = meshEntity.Vertices[tmpFaceRecordDef[2]];
            Point3d vertex4 = new Point3d();
            if (tmpFaceRecordDef.Length == 4) vertex4 = meshEntity.Vertices[tmpFaceRecordDef[3]];

            if (tmpFaceRecordDef.Length == 3) meshFace = new Face(vertex1, vertex2, vertex3, true, true, true, true);
            else meshFace = new Face(vertex1, vertex2, vertex3, vertex4, true, true, true, true);

            meshFace.ColorIndex = 1;

            acBlkTblRec.AppendEntity(meshFace);
            acTrans.AddNewlyCreatedDBObject(meshFace, true);

            // Reset temp data
            tmpCounter = -1;
            tmpFaceRecordDef = new int[] { };
        }

        acTrans.Commit();
    }
}

Трёхмерные тела

Твердотельный объект (класс Solid3d) представляет собой полнотелый объект (оболочка с объемом). Сложные твердотельные формы также проще создавать и редактировать, в отличие от каркасных тел и многогранных сетей. Вы можете создавать простые трехмерные тела, такие как параллелепипед, сфера и клин, используя методы и свойства класса Solid3d. Вы также можете создавать сложные тела, полученные на основе выдавливания\вращения контура вокруг заданной оси. Подобно сетям, твердотельные солиды отображаются в виде оболочек, пока вы не измените визуальный стиль отображения. Кроме того, вы можете извлекать информацию о геометрических свойствах тел (объем (Volume), моменты инерции (MomentOfInertia), центр тяжести (Centroid) и т. д.) с помощью вспомогательной структуры Solid3dMassProperties, возвращаемой через свойство MassProperties у Solid3d. На отображение твердотельного солида влияют текущий визуальный стиль и системные переменные, связанные с 3D-моделированием:

  • ISOLINES задает количество контурных линий, которыми отрис на криволинейных поверхностях 3D:тел;
  • FACETRES определяет плавность отрисовки перехода от скрытых, затененных и визуализированных объектов;

В примере ниже создается солид в виде клина.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("CreateWedge")]
public static void CreateWedge()
{
    // Get the current document and database, and start a transaction
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table record for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a 3D solid wedge
        using (Solid3d acSol3D = new Solid3d())
        {
            acSol3D.CreateWedge(10, 15, 20);

            // Position the center of the 3D solid at (5,5,0) 
            acSol3D.TransformBy(Matrix3d.Displacement(new Point3d(5, 5, 0) -
                                                        Point3d.Origin));

            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acSol3D);
            acTrans.AddNewlyCreatedDBObject(acSol3D, true);
        }

        // Open the active viewport
        ViewportTableRecord acVportTblRec;
        acVportTblRec = acTrans.GetObject(acDoc.Editor.ActiveViewportId,
                                            OpenMode.ForWrite) as ViewportTableRecord;

        // Rotate the view direction of the current viewport
        acVportTblRec.ViewDirection = new Vector3d(-1, -1, 1);
        acDoc.Editor.UpdateTiledViewportsFromDatabase();

        // Save the new objects to the database
        acTrans.Commit();
    }
}

Редактирование в 3D

В разделе описываются особенности редактирования в трехмерном пространстве (поворот, создание массивов, отзеркаливание).

Вращение в 3D

С помощью метода TransformBy или, конкретнее, метода Rotation матрицы трансформации можно применять к двумерным объектам поворот вокруг заданной точки. Направление вращения для двумерных объектов — вокруг оси Z. Для трехмерных объектов ось вращения не ограничивается осью Z, в качестве оси вращения может быть указан произвольный трехмерный вектор.

В примере ниже создается солид в виде параллепипеда, и далее он вращается на 30 градусов вокруг вектора, заданного двумя точками.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("Rotate_3DBox")]
public static void Rotate_3DBox()
{
    // Get the current document and database, and start a transaction
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;
        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;
        // Create a 3D solid box
        using (Solid3d acSol3D = new Solid3d())
        {
            acSol3D.CreateBox(5, 7, 10);
            // Position the center of the 3D solid at (5,5,0)
            acSol3D.TransformBy(Matrix3d.Displacement(new Point3d(5, 5, 0) -
                                                        Point3d.Origin));
            Matrix3d curUCSMatrix = acDoc.Editor.CurrentUserCoordinateSystem;
            CoordinateSystem3d curUCS = curUCSMatrix.CoordinateSystem3d;
            // Rotate the 3D solid 30 degrees around the axis that is
            // defined by the points (-3,4,0) and (-3,-4,0)
            Vector3d vRot = new Point3d(-3, 4, 0).
                            GetVectorTo(new Point3d(-3, -4, 0));
            acSol3D.TransformBy(Matrix3d.Rotation(0.5236,
                                                    vRot,
                                                    new Point3d(-3, 4, 0)));
            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acSol3D);
            acTrans.AddNewlyCreatedDBObject(acSol3D, true);
        }
        // Save the new objects to the database
        acTrans.Commit();
    }
}

Создание 3D массивов

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

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

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
 
static Point2d PolarPoints(Point2d pPt, double dAng, double dDist)
{
    return new Point2d(pPt.X + dDist * Math.Cos(dAng),
                       pPt.Y + dDist * Math.Sin(dAng));
}
 
[CommandMethod("CreateRectangular3DArray")]
public static void CreateRectangular3DArray()
{
    // Get the current document and database, and start a transaction
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table record for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                     OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a circle that is at 2,2 with a radius of 0.5
        using (Circle acCirc = new Circle())
        {
            acCirc.Center = new Point3d(2, 2, 0);
            acCirc.Radius = 0.5;

            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acCirc);
            acTrans.AddNewlyCreatedDBObject(acCirc, true);

            // Create a rectangular array with 4 rows, 4 columns, and 3 levels
            int nRows = 4;
            int nColumns = 4;
            int nLevels = 3;

            // Set the row, column, and level offsets along with the base array angle
            double dRowOffset = 1;
            double dColumnOffset = 1;
            double dLevelsOffset = 4;
            double dArrayAng = 0;

            // Get the angle from X for the current UCS 
            Matrix3d curUCSMatrix = acDoc.Editor.CurrentUserCoordinateSystem;
            CoordinateSystem3d curUCS = curUCSMatrix.CoordinateSystem3d;
            Vector2d acVec2dAng = new Vector2d(curUCS.Xaxis.X,
                                               curUCS.Xaxis.Y);

            // If the UCS is rotated, adjust the array angle accordingly
            dArrayAng = dArrayAng + acVec2dAng.Angle;

            // Use the upper-left corner of the objects extents for the array base point
            Extents3d acExts = acCirc.Bounds.GetValueOrDefault();
            Point2d acPt2dArrayBase = new Point2d(acExts.MinPoint.X,
                                                  acExts.MaxPoint.Y);

            // Track the objects created for each column
            DBObjectCollection acDBObjCollCols = new DBObjectCollection();
            acDBObjCollCols.Add(acCirc);

            // Create the number of objects for the first column
            int nColumnsCount = 1;
            while (nColumns > nColumnsCount)
            {
                Entity acEntClone = acCirc.Clone() as Entity;
                acDBObjCollCols.Add(acEntClone);

                // Caclucate the new point for the copied object (move)
                Point2d acPt2dTo = PolarPoints(acPt2dArrayBase,
                                               dArrayAng,
                                               dColumnOffset * nColumnsCount);

                Vector2d acVec2d = acPt2dArrayBase.GetVectorTo(acPt2dTo);
                Vector3d acVec3d = new Vector3d(acVec2d.X, acVec2d.Y, 0);
                acEntClone.TransformBy(Matrix3d.Displacement(acVec3d));

                acBlkTblRec.AppendEntity(acEntClone);
                acTrans.AddNewlyCreatedDBObject(acEntClone, true);

                nColumnsCount = nColumnsCount + 1;
            }

            // Set a value in radians for 90 degrees
            double dAng = 1.5708;

            // Track the objects created for each row and column
            DBObjectCollection acDBObjCollLvls = new DBObjectCollection();

            foreach (DBObject acObj in acDBObjCollCols)
            {
                acDBObjCollLvls.Add(acObj);
            }

            // Create the number of objects for each row
            foreach (Entity acEnt in acDBObjCollCols)
            {
                int nRowsCount = 1;

                while (nRows > nRowsCount)
                {
                    Entity acEntClone = acEnt.Clone() as Entity;
                    acDBObjCollLvls.Add(acEntClone);

                    // Caclucate the new point for the copied object (move)
                    Point2d acPt2dTo = PolarPoints(acPt2dArrayBase,
                                                   dArrayAng + dAng,
                                                   dRowOffset * nRowsCount);

                    Vector2d acVec2d = acPt2dArrayBase.GetVectorTo(acPt2dTo);
                    Vector3d acVec3d = new Vector3d(acVec2d.X, acVec2d.Y, 0);
                    acEntClone.TransformBy(Matrix3d.Displacement(acVec3d));

                    acBlkTblRec.AppendEntity(acEntClone);
                    acTrans.AddNewlyCreatedDBObject(acEntClone, true);

                    nRowsCount = nRowsCount + 1;
                }
            }

            // Create the number of levels for a 3D array
            foreach (Entity acEnt in acDBObjCollLvls)
            {
                int nLvlsCount = 1;

                while (nLevels > nLvlsCount)
                {
                    Entity acEntClone = acEnt.Clone() as Entity;

                    Vector3d acVec3d = new Vector3d(0, 0, dLevelsOffset * nLvlsCount);
                    acEntClone.TransformBy(Matrix3d.Displacement(acVec3d));

                    acBlkTblRec.AppendEntity(acEntClone);
                    acTrans.AddNewlyCreatedDBObject(acEntClone, true);

                    nLvlsCount = nLvlsCount + 1;
                }
            }
        }

        // Save the new objects to the database
        acTrans.Commit();
    }
}

Отзеркаливание в 3D

С помощью метода TransformBy объекта и метода Mirroring матрицы трансформации можно зеркально отображать объекты вдоль заданной плоскости, определяемой тремя точками.

В примере ниже создается солид в виде параллепипеда, затем создается его зеркальная копия, для контраста, красного цвета.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("MirrorABox3D")]
public static void MirrorABox3D()
{
    // Get the current document and database, and start a transaction
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table record for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;
        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;
        // Create a 3D solid box
        using (Solid3d acSol3D = new Solid3d())
        {
            acSol3D.CreateBox(5, 7, 10);
            // Position the center of the 3D solid at (5,5,0)
            acSol3D.TransformBy(Matrix3d.Displacement(new Point3d(5, 5, 0) -
                                                        Point3d.Origin));
            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acSol3D);
            acTrans.AddNewlyCreatedDBObject(acSol3D, true);
            // Create a copy of the original 3D solid and change the color of the copy
            Solid3d acSol3DCopy = acSol3D.Clone() as Solid3d;
            acSol3DCopy.ColorIndex = 1;
            // Define the mirror plane
            Plane acPlane = new Plane(new Point3d(1.25, 0, 0),
                                        new Point3d(1.25, 2, 0),
                                        new Point3d(1.25, 2, 2));
            // Mirror the 3D solid across the plane
            acSol3DCopy.TransformBy(Matrix3d.Mirroring(acPlane));
            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acSol3DCopy);
            acTrans.AddNewlyCreatedDBObject(acSol3DCopy, true);
        }
        // Save the new objects to the database
        acTrans.Commit();
    }
}

Редактирование трехмерных солидов

После создания твердого тела (солида) можно создавать более сложные фигуры, комбинируя несколько солидов: производя булевые операции объединения, вычитания, пересечения тел. Для выполнения этих комбинаций используйте метод BooleanOperation. Метод CheckInterference позволяет определить, пересекаются ли два твердых тела. Также можно находить сечения тел с помощью метода GetSection и разрещать тела на 2 части используя метод Slice.

Поиск пересечения двух тел

В примере ниже создаются параллелепипед и цилиндр. Затем создается копия цинидра и находится объём пересечения его с параллепипедом, новое тело добавляется в модель с красным цветом.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
 
[CommandMethod("FindInterferenceBetweenSolids")]
public static void FindInterferenceBetweenSolids()
{
    // Get the current document and database, and start a transaction
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table record for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a 3D solid box
        using (Solid3d acSol3DBox = new Solid3d())
        {
            acSol3DBox.CreateBox(5, 7, 10);
            acSol3DBox.ColorIndex = 7;

            // Position the center of the 3D solid at (5,5,0) 
            acSol3DBox.TransformBy(Matrix3d.Displacement(new Point3d(5, 5, 0) -
                                                            Point3d.Origin));

            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acSol3DBox);
            acTrans.AddNewlyCreatedDBObject(acSol3DBox, true);

            // Create a 3D solid cylinder
            // 3D solids are created at (0,0,0) so there is no need to move it
            using (Solid3d acSol3DCyl = new Solid3d())
            {
                acSol3DCyl.CreateFrustum(20, 5, 5, 5);
                acSol3DCyl.ColorIndex = 4;

                // Add the new object to the block table record and the transaction
                acBlkTblRec.AppendEntity(acSol3DCyl);
                acTrans.AddNewlyCreatedDBObject(acSol3DCyl, true);

                // Create a 3D solid from the interference of the box and cylinder
                Solid3d acSol3DCopy = acSol3DCyl.Clone() as Solid3d;

                // Check to see if the 3D solids overlap
                if (acSol3DCopy.CheckInterference(acSol3DBox) == true)
                {
                    acSol3DCopy.BooleanOperation(BooleanOperationType.BoolIntersect,
                                                    acSol3DBox.Clone() as Solid3d);

                    acSol3DCopy.ColorIndex = 1;
                }

                // Add the new object to the block table record and the transaction
                acBlkTblRec.AppendEntity(acSol3DCopy);
                acTrans.AddNewlyCreatedDBObject(acSol3DCopy, true);
            }
        }

        // Save the new objects to the database
        acTrans.Commit();
    }
}

Разрезание солида на 2 солида

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

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
 
[CommandMethod("SliceABox")]
public static void SliceABox()
{
    // Get the current document and database, and start a transaction
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table record for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                        OpenMode.ForRead) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Create a 3D solid box
        using (Solid3d acSol3D = new Solid3d())
        {
            acSol3D.CreateBox(5, 7, 10);
            acSol3D.ColorIndex = 7;

            // Position the center of the 3D solid at (5,5,0) 
            acSol3D.TransformBy(Matrix3d.Displacement(new Point3d(5, 5, 0) -
                                                        Point3d.Origin));

            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acSol3D);
            acTrans.AddNewlyCreatedDBObject(acSol3D, true);

            // Define the mirror plane
            Plane acPlane = new Plane(new Point3d(1.5, 7.5, 0),
                                        new Point3d(1.5, 7.5, 10),
                                        new Point3d(8.5, 2.5, 10));

            Solid3d acSol3DSlice = acSol3D.Slice(acPlane, true);
            acSol3DSlice.ColorIndex = 1;

            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acSol3DSlice);
            acTrans.AddNewlyCreatedDBObject(acSol3DSlice, true);
        }

        // Save the new objects to the database
        acTrans.Commit();
    }
}

Продвинутые технологии и методы организации работы

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

TODO: перевести про "Manage Raster Images (.NET)"

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

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

Работа с блоками

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

Расширенную информацию о работе с блоками см. в соответствующих разделах пользовательской справки для AutoCAD.

Создание определения блока

Чтобы создать новый блок, создайте экземпляр класса BlockTableRecord и используйте метод Add для добавления его к таблице BlockTable. После создания BlockTableRecord используйте свойство Name, чтобы присвоить имя (в дальнейшем при вставке в модель Вхождений блоков у них будет данное имя).

Затем, после создания BlockTableRecord, добавьте к блоку один или несколько объектов с помощью метода AppendEntity. После этого вы сможете создать Вхождение блока (BlockReference), разместив его в пространстве модели, или на любом из листов (в соответствующем листе BlockTableRecord). Вы также можете создать блок, используя метод WBlock для сохранения набора объектов в отдельный файл чертежа, который затем вставить в данный в виде внешней ссылки или определения блока.

В примере ниже создается новое определение блока с именем "CircleBlock", если оно ещё не существует, в определение блока добавляется окружность.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("CreatingABlock")]
public void CreatingABlock()
{
    // Get the current database and start a transaction
    Database acCurDb;
    acCurDb = Application.DocumentManager.MdiActiveDocument.Database;
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead) as BlockTable;
        if (!acBlkTbl.Has("CircleBlock"))
        {
            using (BlockTableRecord acBlkTblRec = new BlockTableRecord())
            {
                acBlkTblRec.Name = "CircleBlock";
                // Set the insertion point for the block
                acBlkTblRec.Origin = new Point3d(0, 0, 0);
                // Add a circle to the block
                using (Circle acCirc = new Circle())
                {
                    acCirc.Center = new Point3d(0, 0, 0);
                    acCirc.Radius = 2;
                    acBlkTblRec.AppendEntity(acCirc);
                    acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForWrite);
                    acBlkTbl.Add(acBlkTblRec);
                    acTrans.AddNewlyCreatedDBObject(acBlkTblRec, true);
                }
            }
        }
        // Save the new object to the database
        acTrans.Commit();
        // Dispose of the transaction
    }
}

Вставка Вхождения блока

Вставить блок в чертеж можно, создав экземпляр класса BlockReference и передав ему в конструкторе точку вставки и идентификатор определения блока (BlockTableRecord.ObjectId).

Вы также можете вставить в виде блока какой-либо иной чертеж (сперва получить содержимое его базы данных с помощью метода ReadDwgFile, а затем в метод Insert текущей базы передать Database для другого чертежа). Если вы внесете изменения в исходный чертеж после его вставки, эти изменения не повлияют на вставленный блок. Если вы хотите, чтобы вставленный блок отражал внесенные вами изменения в исходный чертеж, вы можете переопределить блок, повторно вставив измененный чертеж чертеж.

По умолчанию AutoCAD использует координаты (0, 0, 0) в качестве базовой точки при вставке файла чертежа в виде определения блока. Вы можете изменить базовую точку чертежа, открыв его и установив через метод SetSystemVariable новое значение переменной INSBASE. AutoCAD будет использовать её значение при следующей вставке чертежа в виде блока.

Объекты, входящие в состав вхождения блока нельзя считать, это можно сделать только для определения блока, либо разбив данное вхождение блока на составляющие примитивы (метод Explode).

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

В примере ниже создается определение блока с именем "CircleBlock", состоящего из простой окружности, и далее этот блок вставляется в пространство модели в виде Вхождение блока (BlockReference)

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("InsertingABlock")]
public void InsertingABlock()
{
    // Get the current database and start a transaction
    Database acCurDb;
    acCurDb = Application.DocumentManager.MdiActiveDocument.Database;

    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead) as BlockTable;

        ObjectId blkRecId = ObjectId.Null;

        if (!acBlkTbl.Has("CircleBlock"))
        {
            using (BlockTableRecord acBlkTblRec = new BlockTableRecord())
            {
                acBlkTblRec.Name = "CircleBlock";

                // Set the insertion point for the block
                acBlkTblRec.Origin = new Point3d(0, 0, 0);

                // Add a circle to the block
                using (Circle acCirc = new Circle())
                {
                    acCirc.Center = new Point3d(0, 0, 0);
                    acCirc.Radius = 2;

                    acBlkTblRec.AppendEntity(acCirc);

                    acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForWrite);
                    acBlkTbl.Add(acBlkTblRec);
                    acTrans.AddNewlyCreatedDBObject(acBlkTblRec, true);
                }

                blkRecId = acBlkTblRec.Id;
            }
        }
        else
        {
            blkRecId = acBlkTbl["CircleBlock"];
        }

        // Insert the block into the current space
        if (blkRecId != ObjectId.Null)
        {
            using (BlockReference acBlkRef = new BlockReference(new Point3d(0, 0, 0), blkRecId))
            {
                BlockTableRecord acCurSpaceBlkTblRec;
                acCurSpaceBlkTblRec = acTrans.GetObject(acCurDb.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;

                acCurSpaceBlkTblRec.AppendEntity(acBlkRef);
                acTrans.AddNewlyCreatedDBObject(acBlkRef, true);
            }
        }

        // Save the new object to the database
        acTrans.Commit();

        // Dispose of the transaction
    }
}

Разбивка блока на элементы

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

В примере ниже создается определение блока "CircleBlock", состоящее из простой окружности, для него создается Вхождение блока, которое разбивается на набор объектов, каждый из которых добавляется в пространство модели с красным цветом.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("ExplodingABlock")]
public void ExplodingABlock()
{
    // Get the current database and start a transaction
    Database acCurDb;
    acCurDb = Application.DocumentManager.MdiActiveDocument.Database;

    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead) as BlockTable;

        ObjectId blkRecId = ObjectId.Null;

        if (!acBlkTbl.Has("CircleBlock"))
        {
            using (BlockTableRecord acBlkTblRec = new BlockTableRecord())
            {
                acBlkTblRec.Name = "CircleBlock";

                // Set the insertion point for the block
                acBlkTblRec.Origin = new Point3d(0, 0, 0);

                // Add a circle to the block
                using (Circle acCirc = new Circle())
                {
                    acCirc.Center = new Point3d(0, 0, 0);
                    acCirc.Radius = 2;

                    acBlkTblRec.AppendEntity(acCirc);

                    acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForWrite);
                    acBlkTbl.Add(acBlkTblRec);
                    acTrans.AddNewlyCreatedDBObject(acBlkTblRec, true);
                }

                blkRecId = acBlkTblRec.Id;
            }
        }
        else
        {
            blkRecId = acBlkTbl["CircleBlock"];
        }

        // Insert the block into the current space
        if (blkRecId != ObjectId.Null)
        {
            using (BlockReference acBlkRef = new BlockReference(new Point3d(0, 0, 0), blkRecId))
            {
                BlockTableRecord acCurSpaceBlkTblRec;
                acCurSpaceBlkTblRec = acTrans.GetObject(acCurDb.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;

                acCurSpaceBlkTblRec.AppendEntity(acBlkRef);
                acTrans.AddNewlyCreatedDBObject(acBlkRef, true);

                using (DBObjectCollection dbObjCol = new DBObjectCollection())
                {
                    acBlkRef.Explode(dbObjCol);

                    foreach (DBObject dbObj in dbObjCol)
                    {
                        Entity acEnt = dbObj as Entity;

                        acCurSpaceBlkTblRec.AppendEntity(acEnt);
                        acTrans.AddNewlyCreatedDBObject(dbObj, true);

                        acEnt = acTrans.GetObject(dbObj.ObjectId, OpenMode.ForWrite) as Entity;

                        acEnt.ColorIndex = 1;
                        Application.ShowAlertDialog("Exploded Object: " + acEnt.GetRXClass().DxfName);
                    }
                }
            }
        }

        // Save the new object to the database
        acTrans.Commit();

        // Dispose of the transaction
    }
}

Переопределение блока

Используйте любой из методов и свойств класса BlockTableRecord для переопределения блока. При переопределении блока все ссылки на этот блок (Вхождения блока) на чертеже немедленно обновятся в соответствии с новым определением.

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

В примере ниже, если в чертеже есть определение блока с именем "CircleBlock", то для всех объектов-окружностей, составляющих блок, увеличивается радиус в 2 раза, после чего с помощью служебного метода RecordGraphicsModified обновляются все вхождения блока, чтобы они изменились вслед за редактированием определения.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("RedefiningABlock")]
public void RedefiningABlock()
{
    // Get the current database and start a transaction
    Database acCurDb;
    acCurDb = Application.DocumentManager.MdiActiveDocument.Database;
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead) as BlockTable;
        if (!acBlkTbl.Has("CircleBlock"))
        {
            using (BlockTableRecord acBlkTblRec = new BlockTableRecord())
            {
                acBlkTblRec.Name = "CircleBlock";
                // Set the insertion point for the block
                acBlkTblRec.Origin = new Point3d(0, 0, 0);
                // Add a circle to the block
                using (Circle acCirc = new Circle())
                {
                    acCirc.Center = new Point3d(0, 0, 0);
                    acCirc.Radius = 2;
                    acBlkTblRec.AppendEntity(acCirc);
                    acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForWrite);
                    acBlkTbl.Add(acBlkTblRec);
                    acTrans.AddNewlyCreatedDBObject(acBlkTblRec, true);
                    // Insert the block into the current space
                    using (BlockReference acBlkRef = new BlockReference(new Point3d(0, 0, 0), acBlkTblRec.Id))
                    {
                        BlockTableRecord acModelSpace;
                        acModelSpace = acTrans.GetObject(acCurDb.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;
                        acModelSpace.AppendEntity(acBlkRef);
                        acTrans.AddNewlyCreatedDBObject(acBlkRef, true);
                        Application.ShowAlertDialog("CircleBlock has been created.");
                    }
                }
            }
        }
        else
        {
            // Redefine the block if it exists
            BlockTableRecord acBlkTblRec =
                acTrans.GetObject(acBlkTbl["CircleBlock"], OpenMode.ForWrite) as BlockTableRecord;
            // Step through each object in the block table record
            foreach (ObjectId objID in acBlkTblRec)
            {
                DBObject dbObj = acTrans.GetObject(objID, OpenMode.ForRead) as DBObject;
                // Revise the circle in the block
                if (dbObj is Circle)
                {
                    Circle acCirc = dbObj as Circle;
                    acTrans.GetObject(objID, OpenMode.ForWrite);
                    acCirc.Radius = acCirc.Radius * 2;
                }
            }
            // Update existing block references
            foreach (ObjectId objID in acBlkTblRec.GetBlockReferenceIds(false, true))
            {
                BlockReference acBlkRef = acTrans.GetObject(objID, OpenMode.ForWrite) as BlockReference;
                acBlkRef.RecordGraphicsModified(true);
            }
            Application.ShowAlertDialog("CircleBlock has been revised.");
        }
        // Save the new object to the database
        acTrans.Commit();
        // Dispose of the transaction
    }
}

Работа с атрибутами

Определение атрибута представляет собой текстовый тэг для добавления некоторой строковой информации к блоку, например, номера, цены или имени производителя. У блока может быть неограниченно-много определений атрибутов, главное, чтобы каждый из них имел уникальное имя. Можно задать постоянное определение атрибута, в этом случае у него будет постоянное значение во всех Вхождениях блоков и оно не будет запрошено для ввода при ручном размещении Вхождения блока в чертеже.

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

Создание атрибутов

Чтобы создать новое определение атрибута необходимо создать экземпляр класса AttributeDefinition, а затем добавить его к целевому блоку, представленного объектом класса BlockTableRecord, с помощью метода AppendEntity. При определении атрибута следует указать высоту текста атрибута, режимы поведения, подсказку (Prompt) и значение тэга (Tag), точку вставки (Position) и значение атрибута по умолчанию .

Режимы поведения определения атрибута могут быть:

  • Constant (постоянный) - атрибут блока будет иметь фиксированное значение для всех Вхождений блока;
  • Invisible (невидимый) - атрибут не будет отображаться на чертеже и не будет выводиться на печать;
  • IsMTextAttributeDefinition - атрибут может содержать многострочный текст (значение будет задаваться через свойство MTextAttributeDefinition);
  • LockPositionInBlock - делает нередактируемым положение атрибута внутри Вхождения блока, после разблокировки атрибут можно перемещать относительно остальной части блока с помощью специальных ручек редактирования, а многострочным атрибутам можно изменять размер;
  • Preset - атрибут будет иметь значение, равное значению по умолчанию. В AutoCAD отображается как "Установленный";
  • Verifiable - будет запрашиваться подтверждение корректности значения атрибута при вставке Вхождения блока в чертеж. В AutoCAD отображается как "Контролируемый";

Примечание: системная переменная ATTDISP, переопределяющая видимость определений атрибутов, в nanoCAD не реализована. Подсказка появляется при вставке блока, содержащего атрибут, и задается с помощью свойства Prompt. Значение по умолчанию для атрибута задается с помощью свойства TextString. Если свойство Constant установлено в значение true, запрос значения для данного атрибута не будет выводиться в окне вставки блока INSERT. Тэг идентифицирует каждое определение атрибута и назначается с помощью свойства Tag. Можно использовать любые символы, кроме пробелов и восклицательных знаков, строчные буквы будут преобразованы в прописные. После того, как определение атрибута будет добавлено в Блок, при каждой вставке блока через команду INSERT можно указать различные значения каждому из атрибуту, если он не определен как постоянный (Constant = true). При программном создании Вхождения блока (BlockReference) он не будет содержать атрибутов, заданных в определении блока (BlockTableRecord) до тех пор, пока они не будут добавлены также программно к данному BlockReference через метод AppendAttribute. Перед добавлением атрибута к данному Вхождению блока (BlockReference) используйте метод SetAttributeFromBlock, чтобы скопировать свойства определения блока AttributeDefinition в объект AttributeReference (так как добавлять атрибуты можно только с помощью специального класса -- AttributeReference). Проверить наличие у блока каких-либо определений атрибутов можно с помощью свойства HasAttributeDefinitions. Определения атрибутов, созданные в пространстве модели или пространстве листа, не считаются связанными с какими-либо блоками.

Создание атрибута у блока

В примере ниже создается определение блока CircleBlockWithAttributes, представленного окружность и одним определением атрибута с именем "Door#".

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("AddingAttributeToABlock")]
public void AddingAttributeToABlock()
{
    // Get the current database and start a transaction
    Database acCurDb;
    acCurDb = Application.DocumentManager.MdiActiveDocument.Database;

    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead) as BlockTable;

        if (!acBlkTbl.Has("CircleBlockWithAttributes"))
        {
            using (BlockTableRecord acBlkTblRec = new BlockTableRecord())
            {
                acBlkTblRec.Name = "CircleBlockWithAttributes";

                // Set the insertion point for the block
                acBlkTblRec.Origin = new Point3d(0, 0, 0);

                // Add a circle to the block
                using (Circle acCirc = new Circle())
                {
                    acCirc.Center = new Point3d(0, 0, 0);
                    acCirc.Radius = 2;

                    acBlkTblRec.AppendEntity(acCirc);

                    // Add an attribute definition to the block
                    using (AttributeDefinition acAttDef = new AttributeDefinition())
                    {
                        acAttDef.Position = new Point3d(0, 0, 0);
                        acAttDef.Verifiable = true;
                        acAttDef.Prompt = "Door #: ";
                        acAttDef.Tag = "Door#";
                        acAttDef.TextString = "DXX";
                        acAttDef.Height = 1;
                        acAttDef.Justify = AttachmentPoint.MiddleCenter;
                        //Тут может возникнуть ошибка, см. примечание внизу
                        acBlkTblRec.AppendEntity(acAttDef);

                        acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForWrite);
                        acBlkTbl.Add(acBlkTblRec);
                        acTrans.AddNewlyCreatedDBObject(acBlkTblRec, true);
                    }
                }
            }
        }

        // Save the new object to the database
        acTrans.Commit();

        // Dispose of the transaction
    }
}

Примечание: в AutoCAD (2022) .NET API замечено, в некоторых случаях, если одновременно в теле using создания BlockTableRecord выполняется и добавление определений атрибутов, то AutoCAD может выбросить ошибку доступа в память и вылетать с фатальной ошибкой. Решение -- в теле using только создать блок без атрибутов, а после using-конструкции получить созданный BlockTableRecord на запись и добавить в него атрибуты. В nanoCAD .NET API таких проблем не встречалось.

Вставка блока с атрибутами

В примере ниже в пространство модели вставляется блок с именем CircleBlockWithAttributes и вхождением атрибута, заданного в определении блока. Если определение блока отсутствует, то оно создается.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("InsertingBlockWithAnAttribute")]
public void InsertingBlockWithAnAttribute()
{
    // Get the current database and start a transaction
    Database acCurDb;
    acCurDb = Application.DocumentManager.MdiActiveDocument.Database;

    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead) as BlockTable;

        ObjectId blkRecId = ObjectId.Null;

        if (!acBlkTbl.Has("CircleBlockWithAttributes"))
        {
            using (BlockTableRecord acBlkTblRec = new BlockTableRecord())
            {
                acBlkTblRec.Name = "CircleBlockWithAttributes";

                // Set the insertion point for the block
                acBlkTblRec.Origin = new Point3d(0, 0, 0);

                // Add a circle to the block
                using (Circle acCirc = new Circle())
                {
                    acCirc.Center = new Point3d(0, 0, 0);
                    acCirc.Radius = 2;

                    acBlkTblRec.AppendEntity(acCirc);

                    // Add an attribute definition to the block
                    using (AttributeDefinition acAttDef = new AttributeDefinition())
                    {
                        acAttDef.Position = new Point3d(0, 0, 0);
                        acAttDef.Prompt = "Door #: ";
                        acAttDef.Tag = "Door#";
                        acAttDef.TextString = "DXX";
                        acAttDef.Height = 1;
                        acAttDef.Justify = AttachmentPoint.MiddleCenter;
                        acBlkTblRec.AppendEntity(acAttDef);

                        acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForWrite);
                        acBlkTbl.Add(acBlkTblRec);
                        acTrans.AddNewlyCreatedDBObject(acBlkTblRec, true);
                    }
                }

                blkRecId = acBlkTblRec.Id;
            }
        }
        else
        {
            blkRecId = acBlkTbl["CircleBlockWithAttributes"];
        }

        // Insert the block into the current space
        if (blkRecId != ObjectId.Null)
        {
            BlockTableRecord acBlkTblRec;
            acBlkTblRec = acTrans.GetObject(blkRecId, OpenMode.ForRead) as BlockTableRecord;

            // Create and insert the new block reference
            using (BlockReference acBlkRef = new BlockReference(new Point3d(2, 2, 0), blkRecId))
            {
                BlockTableRecord acCurSpaceBlkTblRec;
                acCurSpaceBlkTblRec = acTrans.GetObject(acCurDb.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;

                acCurSpaceBlkTblRec.AppendEntity(acBlkRef);
                acTrans.AddNewlyCreatedDBObject(acBlkRef, true);

                // Verify block table record has attribute definitions associated with it
                if (acBlkTblRec.HasAttributeDefinitions)
                {
                    // Add attributes from the block table record
                    foreach (ObjectId objID in acBlkTblRec)
                    {
                        DBObject dbObj = acTrans.GetObject(objID, OpenMode.ForRead) as DBObject;

                        if (dbObj is AttributeDefinition)
                        {
                            AttributeDefinition acAtt = dbObj as AttributeDefinition;

                            if (!acAtt.Constant)
                            {
                                using (AttributeReference acAttRef = new AttributeReference())
                                {
                                    acAttRef.SetAttributeFromBlock(acAtt, acBlkRef.BlockTransform);
                                    acAttRef.Position = acAtt.Position.TransformBy(acBlkRef.BlockTransform);

                                    acAttRef.TextString = acAtt.TextString;

                                    acBlkRef.AttributeCollection.AppendAttribute(acAttRef);

                                    acTrans.AddNewlyCreatedDBObject(acAttRef, true);
                                }
                            }
                        }
                    }
                }
            }
        }

        // Save the new object to the database
        acTrans.Commit();

        // Dispose of the transaction
    }
}

Редактирование атрибутов

Определения атрибута блока наследуют общий класс Attribute, который предоставляет следующие свойства и методы для редактирования:

  • FieldLength : количество разрешенных символов в значении атрибута;
  • Height : высота текста атрибута;
  • HorizontalMode : горизонтальное выравнивание атрибута;
  • IsMirroredInX : признак, что текст будет ориентирован справа:налево при значении true;
  • IsMirroredInY : признак, что текст будет ориентирован снизу:вверх при значении true;
  • Position : точка вставки атрибута;
  • Prompt : подсказка для атрибута (отображается в диалоговом окне INSERT при вставке блока);
  • Rotation : поворот атрибута;
  • Tag : идентификатор атрибута;
  • TextString : значение атрибута;
  • VerticalMode : вертикальное выравнивание атрибута;

В примере ниже создается блок, к нему добавляется определение атрибута, затем создается вхождение блока. При добавлении в блок атрибутов у определения атрибута ставится свойство IsMirroredInX = true.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("RedefiningAnAttribute")]
public void RedefiningAnAttribute()
{
    // Get the current database and start a transaction
    Database acCurDb;
    acCurDb = Application.DocumentManager.MdiActiveDocument.Database;

    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead) as BlockTable;

        ObjectId blkRecId = ObjectId.Null;

        if (!acBlkTbl.Has("CircleBlockWithAttributes"))
        {
            using (BlockTableRecord acBlkTblRec = new BlockTableRecord())
            {
                acBlkTblRec.Name = "CircleBlockWithAttributes";

                // Set the insertion point for the block
                acBlkTblRec.Origin = new Point3d(0, 0, 0);

                // Add a circle to the block
                using (Circle acCirc = new Circle())
                {
                    acCirc.Center = new Point3d(0, 0, 0);
                    acCirc.Radius = 2;

                    acBlkTblRec.AppendEntity(acCirc);

                    // Add an attribute definition to the block
                    using (AttributeDefinition acAttDef = new AttributeDefinition())
                    {
                        acAttDef.Position = new Point3d(0, 0, 0);
                        acAttDef.Prompt = "Door #: ";
                        acAttDef.Tag = "Door#";
                        acAttDef.TextString = "DXX";
                        acAttDef.Height = 1;
                        acAttDef.Justify = AttachmentPoint.MiddleCenter;
                        acBlkTblRec.AppendEntity(acAttDef);

                        acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForWrite);
                        acBlkTbl.Add(acBlkTblRec);
                        acTrans.AddNewlyCreatedDBObject(acBlkTblRec, true);
                    }
                }

                blkRecId = acBlkTblRec.Id;
            }
        }
        else
        {
            blkRecId = acBlkTbl["CircleBlockWithAttributes"];
        }

        // Create and insert the new block reference
        if (blkRecId != ObjectId.Null)
        {
            BlockTableRecord acBlkTblRec;
            acBlkTblRec = acTrans.GetObject(blkRecId, OpenMode.ForRead) as BlockTableRecord;

            using (BlockReference acBlkRef = new BlockReference(new Point3d(2, 2, 0), blkRecId))
            {
                BlockTableRecord acCurSpaceBlkTblRec;
                acCurSpaceBlkTblRec = acTrans.GetObject(acCurDb.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;

                acCurSpaceBlkTblRec.AppendEntity(acBlkRef);
                acTrans.AddNewlyCreatedDBObject(acBlkRef, true);

                // Verify block table record has attribute definitions associated with it
                if (acBlkTblRec.HasAttributeDefinitions)
                {
                    // Add attributes from the block table record
                    foreach (ObjectId objID in acBlkTblRec)
                    {
                        DBObject dbObj = acTrans.GetObject(objID, OpenMode.ForRead) as DBObject;

                        if (dbObj is AttributeDefinition)
                        {
                            AttributeDefinition acAtt = dbObj as AttributeDefinition;

                            if (!acAtt.Constant)
                            {
                                using (AttributeReference acAttRef = new AttributeReference())
                                {
                                    acAttRef.SetAttributeFromBlock(acAtt, acBlkRef.BlockTransform);
                                    acAttRef.Position = acAtt.Position.TransformBy(acBlkRef.BlockTransform);

                                    acAttRef.TextString = acAtt.TextString;

                                    acBlkRef.AttributeCollection.AppendAttribute(acAttRef);
                                    acTrans.AddNewlyCreatedDBObject(acAttRef, true);
                                }
                            }

                            // Change the attribute definition to be displayed as backwards
                            acTrans.GetObject(objID, OpenMode.ForWrite);
                            acAtt.IsMirroredInX = true;
                        }
                    }
                }
            }
        }

        // Save the new object to the database
        acTrans.Commit();

        // Dispose of the transaction
    }
}

Примечание: в AutoCAD .NET API редактировать атрибут блока можно уже после создания вхождения атрибута, в nanoCAD механика событий несколько нарушена, поэтому для корректной работы необходимо действия с редактированием атрибутов делать до добавления их во Вхождение блока/

Чтение атрибутов блоков

Вы можете извлечь информацию об атрибутах из Вхождения блока, используя свойство AttributeCollection; оно возвращает набор ObjectId вхождений атрибутов (описываются классом AttributeReference), у атрибутов доступны свойства Tag и TextString, описывающие ключ и значение атрибута. Если свойство атрибута IsMTextAttribute вернет true, то значение атрибута можно получить через свойство MTextAttribute.

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

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("GettingAttributes")]
public void GettingAttributes()
{
    // Get the current database and start a transaction
    Database acCurDb;
    acCurDb = Application.DocumentManager.MdiActiveDocument.Database;

    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead) as BlockTable;

        ObjectId blkRecId = ObjectId.Null;

        if (!acBlkTbl.Has("TESTBLOCK"))
        {
            using (BlockTableRecord acBlkTblRec = new BlockTableRecord())
            {
                acBlkTblRec.Name = "TESTBLOCK";

                // Set the insertion point for the block
                acBlkTblRec.Origin = new Point3d(0, 0, 0);

                // Add an attribute definition to the block
                using (AttributeDefinition acAttDef = new AttributeDefinition())
                {
                    acAttDef.Position = new Point3d(5, 5, 0);
                    acAttDef.Prompt = "Attribute Prompt";
                    acAttDef.Tag = "AttributeTag";
                    acAttDef.TextString = "Attribute Value";
                    acAttDef.Height = 1;
                    acAttDef.Justify = AttachmentPoint.MiddleCenter;
                    acBlkTblRec.AppendEntity(acAttDef);

                    acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForWrite);
                    acBlkTbl.Add(acBlkTblRec);
                    acTrans.AddNewlyCreatedDBObject(acBlkTblRec, true);
                }

                blkRecId = acBlkTblRec.Id;
            }
        }
        else
        {
            blkRecId = acBlkTbl["CircleBlockWithAttributes"];
        }

        // Create and insert the new block reference
        if (blkRecId != ObjectId.Null)
        {
            BlockTableRecord acBlkTblRec;
            acBlkTblRec = acTrans.GetObject(blkRecId, OpenMode.ForRead) as BlockTableRecord;

            using (BlockReference acBlkRef = new BlockReference(new Point3d(5, 5, 0), acBlkTblRec.Id))
            {
                BlockTableRecord acCurSpaceBlkTblRec;
                acCurSpaceBlkTblRec = acTrans.GetObject(acCurDb.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;

                acCurSpaceBlkTblRec.AppendEntity(acBlkRef);
                acTrans.AddNewlyCreatedDBObject(acBlkRef, true);

                // Verify block table record has attribute definitions associated with it
                if (acBlkTblRec.HasAttributeDefinitions)
                {
                    // Add attributes from the block table record
                    foreach (ObjectId objID in acBlkTblRec)
                    {
                        DBObject dbObj = acTrans.GetObject(objID, OpenMode.ForRead) as DBObject;

                        if (dbObj is AttributeDefinition)
                        {
                            AttributeDefinition acAtt = dbObj as AttributeDefinition;

                            if (!acAtt.Constant)
                            {
                                using (AttributeReference acAttRef = new AttributeReference())
                                {
                                    acAttRef.SetAttributeFromBlock(acAtt, acBlkRef.BlockTransform);
                                    acAttRef.Position = acAtt.Position.TransformBy(acBlkRef.BlockTransform);

                                    acAttRef.TextString = acAtt.TextString;

                                    acBlkRef.AttributeCollection.AppendAttribute(acAttRef);
                                    acTrans.AddNewlyCreatedDBObject(acAttRef, true);
                                }
                            }
                        }
                    }

                    // Display the tags and values of the attached attributes
                    string strMessage = "";
                    AttributeCollection attCol = acBlkRef.AttributeCollection;

                    foreach (ObjectId objID in attCol)
                    {
                        DBObject dbObj = acTrans.GetObject(objID, OpenMode.ForRead) as DBObject;

                        AttributeReference acAttRef = dbObj as AttributeReference;

                        strMessage = strMessage + "Tag: " + acAttRef.Tag + "\n" +
                                        "Value: " + acAttRef.TextString + "\n";

                        // Change the value of the attribute
                        acAttRef.TextString = "NEW VALUE!";
                    }

                    Application.ShowAlertDialog("The attributes for blockReference " + acBlkRef.Name + " are:\n" + strMessage);

                    strMessage = "";
                    foreach (ObjectId objID in attCol)
                    {
                        DBObject dbObj = acTrans.GetObject(objID, OpenMode.ForRead) as DBObject;

                        AttributeReference acAttRef = dbObj as AttributeReference;

                        strMessage = strMessage + "Tag: " + acAttRef.Tag + "\n" +
                                        "Value: " + acAttRef.TextString + "\n";
                    }

                    Application.ShowAlertDialog("The attributes for blockReference " + acBlkRef.Name + " are:\n" + strMessage);
                }
            }
        }

        // Save the new object to the database
        acTrans.Commit();

        // Dispose of the transaction
    }
}

Работа с внешними ссылками

Внешняя ссылка (xref) добавляет связь с другим чертежом к данному; если иной чертеж вставить как блок в данный, то при его обновлении изменения не подтянутся. Однако при вставке чертежа в виде внешней ссылки, она будет обновляться при изменении исходного чертежа. Таким образом, чертеж, содержащий внешние ссылки, всегда отражает самые актуальные изменения каждого из подключенных по внешним ссылкам файлов.

Как и Вхождение блока, внешняя ссылка отображается в текущем чертеже как единый объект. Тем не менее, вес чертежа увеличивается незначительно при вставке в него внешней ссылки; кроме того, вы не можете вызвать процедуру разбивки (Explode) для внешней ссылки. Можно создавать вложенные внешние ссылки (то есть вставить чертеж со своими ссылками и т.д.).

Обновление внешних ссылок

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

Добавление внешних ссылок

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

Для загрузки чертежа в качестве внешней ссылки используйте метод AttachXref, он требует указания пути к чертежу, а также имени внешней ссылки в качестве второго аргумента. Метод вернёт ObjectId на BlockTableRecord (блок, которым станет внешняя ссылка). Далее необходимо вставить блок в модель, подав в конструктор BlockReference вторым аргументом возвращённый идентификатор блока и указав точку и параметры вставки. В примере ниже создается временный файл чертежа, в котором присутствует одна окружность и сохраняется в папку "C:\Temp". Далее он добавляется в качестве внешней ссылки в данный чертеж.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("AttachingExternalReference")]
public void AttachingExternalReference()
{
    string tmpDwg = @"C:\Temp\test.dwg";
    using (Database dbTmp = new Database())
    {
        using (Transaction acTrans = dbTmp.TransactionManager.StartTransaction())
        {
            // Open the Block table record for read
            BlockTable acBlkTbl;
            acBlkTbl = acTrans.GetObject(dbTmp.BlockTableId,
                                            OpenMode.ForRead) as BlockTable;
            // Open the Block table record Model space for write
            BlockTableRecord acBlkTblRec;
            acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                            OpenMode.ForWrite) as BlockTableRecord;
            using (Circle acCirc = new Circle(new Point3d(0, 0, 0), Vector3d.ZAxis, 2))
            {
                acBlkTblRec.AppendEntity(acCirc);
                acTrans.AddNewlyCreatedDBObject(acCirc, true);
            }
            acTrans.Commit();
        }
        dbTmp.SaveAs(tmpDwg, DwgVersion.Current);
    }
    // Get the current database and start a transaction
    Database acCurDb;
    acCurDb = Application.DocumentManager.MdiActiveDocument.Database;
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Create a reference to a DWG file
        ObjectId acXrefId = acCurDb.AttachXref(tmpDwg, "Test Srawing");
        // If a valid reference is created then continue
        if (!acXrefId.IsNull)
        {
            // Attach the DWG reference to the current space
            Point3d insPt = new Point3d(1, 1, 0);
            using (BlockReference acBlkRef = new BlockReference(insPt, acXrefId))
            {
                BlockTableRecord acBlkTblRec;
                acBlkTblRec = acTrans.GetObject(acCurDb.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;
                acBlkTblRec.AppendEntity(acBlkRef);
                acTrans.AddNewlyCreatedDBObject(acBlkRef, true);
            }
        }
        // Save the new objects to the database
        acTrans.Commit();
        // Dispose of the transaction
    }
}

Удаление внешней ссылки

Вы можете отсоединить одну или несколько внешних ссылок, удалив все их экземпляры (вхождения) из чертежа. Отсоединение внешней ссылки приводит к удалению из чертежа всех связанных с ссылкой ресурсов.

Для отсоединения внешней ссылки используйте метод DetachXref. Отсоединить вложенную внешнюю ссылку невозможно (только из-под Database вложенного чертежа).

В примере ниже создается временный файл чертежа, в котором присутствует одна окружность и сохраняется в папку "C:\Temp". Далее он добавляется в качестве внешней ссылки в данный чертеж, выводится информационное окно, после чего ссылка на данный файл удаляется.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("DetachingExternalReference")]
public void DetachingExternalReference()
{
    string tmpDwg = @"C:\Temp\test.dwg";
    using (Database dbTmp = new Database())
    {
        using (Transaction acTrans = dbTmp.TransactionManager.StartTransaction())
        {
            // Open the Block table record for read
            BlockTable acBlkTbl;
            acBlkTbl = acTrans.GetObject(dbTmp.BlockTableId,
                                            OpenMode.ForRead) as BlockTable;
            // Open the Block table record Model space for write
            BlockTableRecord acBlkTblRec;
            acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                            OpenMode.ForWrite) as BlockTableRecord;
            using (Circle acCirc = new Circle(new Point3d(0, 0, 0), Vector3d.ZAxis, 2))
            {
                acBlkTblRec.AppendEntity(acCirc);
                acTrans.AddNewlyCreatedDBObject(acCirc, true);
            }
            acTrans.Commit();
        }
        dbTmp.SaveAs(tmpDwg, DwgVersion.Current);
    }
    // Get the current database and start a transaction
    Database acCurDb;
    acCurDb = Application.DocumentManager.MdiActiveDocument.Database;
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Create a reference to a DWG file
        ObjectId acXrefId = acCurDb.AttachXref(tmpDwg, "Test Srawing");
        Application.ShowAlertDialog("The external reference is attached.");
        acCurDb.DetachXref(acXrefId);//тут может быть ошибка eXRefDependent
        Application.ShowAlertDialog("The external reference is detached.");
        // Save the new objects to the database
        acTrans.Commit();
        // Dispose of the transaction
    }
}

Примечание: в некоторых случаях в nanoCAD данный метод DetachXref может не сработать, возвращая ошибку eXRefDependent.

Обновление внешней ссылки

Для обновления внешних ссылок вызовите метод ReloadXrefs у объекта Database, передав ему в качестве аргумента ObjectIdCollection для идентификаторов определений блоков внешних ссылок (например, тех, что вернет метод AttachXref).

В примере ниже создается чертеж с простой графикой, добавляется в качестве внешней ссылки, а потом вызывается метод AttachXref для обновления связанной с ним внешней ссылки.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("ReloadingExternalReference")]
public void ReloadingExternalReference()
{
    string tmpDwg = @"C:\Temp\test.dwg";
    using (Database dbTmp = new Database())
    {
        using (Transaction acTrans = dbTmp.TransactionManager.StartTransaction())
        {
            // Open the Block table record for read
            BlockTable acBlkTbl;
            acBlkTbl = acTrans.GetObject(dbTmp.BlockTableId,
                                            OpenMode.ForRead) as BlockTable;
            // Open the Block table record Model space for write
            BlockTableRecord acBlkTblRec;
            acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                            OpenMode.ForWrite) as BlockTableRecord;
            using (Circle acCirc = new Circle(new Point3d(0, 0, 0), Vector3d.ZAxis, 2))
            {
                acBlkTblRec.AppendEntity(acCirc);
                acTrans.AddNewlyCreatedDBObject(acCirc, true);
            }
            acTrans.Commit();
        }
        dbTmp.SaveAs(tmpDwg, DwgVersion.Current);
    }
    // Get the current database and start a transaction
    Database acCurDb;
    acCurDb = Application.DocumentManager.MdiActiveDocument.Database;
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Create a reference to a DWG file
        ObjectId acXrefId = acCurDb.AttachXref(tmpDwg, "Test1");
        // If a valid reference is created then continue
        if (!acXrefId.IsNull)
        {
            // Attach the DWG reference to the current space
            Point3d insPt = new Point3d(1, 1, 0);
            using (BlockReference acBlkRef = new BlockReference(insPt, acXrefId))
            {
                BlockTableRecord acBlkTblRec;
                acBlkTblRec = acTrans.GetObject(acCurDb.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;
                acBlkTblRec.AppendEntity(acBlkRef);
                acTrans.AddNewlyCreatedDBObject(acBlkRef, true);
            }
            Application.ShowAlertDialog("The external reference is attached.");
            using (ObjectIdCollection acXrefIdCol = new ObjectIdCollection())
            {
                acXrefIdCol.Add(acXrefId);
                acCurDb.ReloadXrefs(acXrefIdCol);
            }
            Application.ShowAlertDialog("The external reference is reloaded.");
        }
        // Save the new objects to the database
        acTrans.Commit();
    }
}

Выгрузка внешней ссылки

Для выгрузки внешней ссылки используйте метод UnloadXrefs для базы данных чертежа Database. При выгрузке файла, на который есть ссылка и который не используется в текущем чертеже, производительность чертежа повышается за счет отсутствия необходимости считывать и отображать ненужную геометрию чертежа или информацию из его таблиц символов. Содержимое внешней ссылки, а также любых вложенные в неё внешние ссылки, не будут отображаться в текущем чертеже до тех пор, пока внешняя ссылка не будет загружена обратно (например, с помощью ReloadXrefs).

В примере ниже к чертежу подключается внешняя ссылка, после чего она выгружается.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("UnloadingExternalReference")]
public void UnloadingExternalReference()
{
    string tmpDwg = @"C:\Temp\test.dwg";
    using (Database dbTmp = new Database())
    {
        using (Transaction acTrans = dbTmp.TransactionManager.StartTransaction())
        {
            // Open the Block table record for read
            BlockTable acBlkTbl;
            acBlkTbl = acTrans.GetObject(dbTmp.BlockTableId,
                                            OpenMode.ForRead) as BlockTable;
            // Open the Block table record Model space for write
            BlockTableRecord acBlkTblRec;
            acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                            OpenMode.ForWrite) as BlockTableRecord;
            using (Circle acCirc = new Circle(new Point3d(0, 0, 0), Vector3d.ZAxis, 2))
            {
                acBlkTblRec.AppendEntity(acCirc);
                acTrans.AddNewlyCreatedDBObject(acCirc, true);
            }
            acTrans.Commit();
        }
        dbTmp.SaveAs(tmpDwg, DwgVersion.Current);
    }
    // Get the current database and start a transaction
    Database acCurDb;
    acCurDb = Application.DocumentManager.MdiActiveDocument.Database;
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Create a reference to a DWG file
        ObjectId acXrefId = acCurDb.AttachXref(tmpDwg, "Test1");
        // If a valid reference is created then continue
        if (!acXrefId.IsNull)
        {
            // Attach the DWG reference to the current space
            Point3d insPt = new Point3d(1, 1, 0);
            using (BlockReference acBlkRef = new BlockReference(insPt, acXrefId))
            {
                BlockTableRecord acBlkTblRec;
                acBlkTblRec = acTrans.GetObject(acCurDb.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;
                acBlkTblRec.AppendEntity(acBlkRef);
                acTrans.AddNewlyCreatedDBObject(acBlkRef, true);
            }
            Application.ShowAlertDialog("The external reference is attached.");
            using (ObjectIdCollection acXrefIdCol = new ObjectIdCollection())
            {
                acXrefIdCol.Add(acXrefId);
                acCurDb.UnloadXrefs(acXrefIdCol);
            }
            Application.ShowAlertDialog("The external reference is unloaded.");
        }
        // Save the new objects to the database
        acTrans.Commit();
    }
}

Внедрение внешней ссылки

Привязка внешней ссылки к чертежу с помощью метода Bind делает внешнюю ссылку постоянной частью чертежа в виде блока, она перестает быть внешней ссылкой, при обновлении исходного файла информация в созданном блоке не будет обновлена, при внедрении данных чертежа в текущий чертеж импортируются все связанные с базой данных целевого чертежа вспомогательные данные (слои, стили, типы линий и пр.). Основная цель внедрения файла внешней ссылки -- это использование его настроек (стилей, типов данных и пр.) в данном чертеже.

Метод BindXref требует указания двух параметра: xrefIds (набор идентификаторов внешних ссылок, BlockTableRecord) и флаг insertBind. Если параметр insertBind установлен в значение true, то все блоки, стили и пр. "именованные объекты" из переносимого чертежа будут скопированы в данный с префиксом в виде имени файла внешней ссылки и суффиксом-счетчиком для пересохранения одноименных определений. Если параметр insertBind будет установлен в false, то имена объектов будут скопированы без изменений. Рекомендуется использовать флаг true, если нет уверенности, что чертеж не содержит дублей.

Если внешняя ссылка имела в чертеже более одного вхождения, все из них станут "Вхождениями блоков".

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

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[CommandMethod("BindingExternalReference")]
public void BindingExternalReference()
{
    string tmpDwg = @"C:\Temp\test.dwg";
    using (Database dbTmp = new Database())
    {
        using (Transaction acTrans = dbTmp.TransactionManager.StartTransaction())
        {
            // Open the Block table record for read
            BlockTable acBlkTbl;
            acBlkTbl = acTrans.GetObject(dbTmp.BlockTableId,
                                            OpenMode.ForRead) as BlockTable;
            // Open the Block table record Model space for write
            BlockTableRecord acBlkTblRec;
            acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                            OpenMode.ForWrite) as BlockTableRecord;
            using (Circle acCirc = new Circle(new Point3d(0, 0, 0), Vector3d.ZAxis, 2))
            {
                acBlkTblRec.AppendEntity(acCirc);
                acTrans.AddNewlyCreatedDBObject(acCirc, true);
            }
            acTrans.Commit();
        }
        dbTmp.SaveAs(tmpDwg, DwgVersion.Current);
    }
    // Get the current database and start a transaction
    Database acCurDb;
    acCurDb = Application.DocumentManager.MdiActiveDocument.Database;
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Create a reference to a DWG file
        ObjectId acXrefId = acCurDb.AttachXref(tmpDwg, "Test1");
        // If a valid reference is created then continue
        if (!acXrefId.IsNull)
        {
            // Attach the DWG reference to the current space
            Point3d insPt1 = new Point3d(10, 10, 0);
            Point3d insPt2 = new Point3d(30, 10, 0);
            using (BlockReference acBlkRef = new BlockReference(insPt1, acXrefId))
            {
                BlockTableRecord acBlkTblRec;
                acBlkTblRec = acTrans.GetObject(acCurDb.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;
                acBlkTblRec.AppendEntity(acBlkRef);
                acTrans.AddNewlyCreatedDBObject(acBlkRef, true);
            }
            using (BlockReference acBlkRef = new BlockReference(insPt2, acXrefId))
            {
                BlockTableRecord acBlkTblRec;
                acBlkTblRec = acTrans.GetObject(acCurDb.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;
                acBlkTblRec.AppendEntity(acBlkRef);
                acTrans.AddNewlyCreatedDBObject(acBlkRef, true);
            }
            Application.ShowAlertDialog("The external reference is attached.");
            using (ObjectIdCollection acXrefIdCol = new ObjectIdCollection())
            {
                acXrefIdCol.Add(acXrefId);
                acCurDb.BindXrefs(acXrefIdCol, false);
            }
            Application.ShowAlertDialog("The external reference is bound.");
        }
        // Save the new objects to the database
        acTrans.Commit();
    }
}

Подрезка внешней ссылки

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

Для определения свойств границы обрезки для внешней ссылки используются вспомогательные структуры SpatialFilter и SpatialFilterDefinition из пространства имён Autodesk.AutoCAD.DatabaseServices.Filters. Используйте свойство Enabled объекта SpatialFilterDefinition для отображения или скрытия границы обрезки.

В примере ниже формируется демонстрационный чертеж, содержащий множество окружностей переменного диаметра между точками (0,0) и (2000, 2000), в данном чертеже на него создается внешняя ссылка, создается её экземпляр в чертеже и для него задается граница подрезки в виде прямоугольника с крайними точками (200, 300) и (1200, 1100).

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.DatabaseServices.Filters;

[CommandMethod("ClippingExternalReference")]
public void ClippingExternalReference()
{
    string tmpDwg = @"C:\Temp\test.dwg";
    using (Database dbTmp = new Database())
    {
        using (Transaction acTrans = dbTmp.TransactionManager.StartTransaction())
        {
            // Open the Block table record for read
            BlockTable acBlkTbl;
            acBlkTbl = acTrans.GetObject(dbTmp.BlockTableId,
                                            OpenMode.ForRead) as BlockTable;
            // Open the Block table record Model space for write
            BlockTableRecord acBlkTblRec;
            acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                            OpenMode.ForWrite) as BlockTableRecord;
            System.Random r = new System.Random();
            for (int i = 0; i < 2000; i += 20)
            {
                for (int j = 0; j < 2000; j += 20)
                {
                    using (Circle acCirc = new Circle(new Point3d(i, j, 0), Vector3d.ZAxis, r.Next(1, 12)))
                    {
                        acBlkTblRec.AppendEntity(acCirc);
                        acTrans.AddNewlyCreatedDBObject(acCirc, true);
                    }
                }
            }
            acTrans.Commit();
        }
        dbTmp.SaveAs(tmpDwg, DwgVersion.Current);
    }
    // Get the current database and start a transaction
    Database acCurDb;
    acCurDb = Application.DocumentManager.MdiActiveDocument.Database;
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Create a reference to a DWG file
        ObjectId acXrefId = acCurDb.AttachXref(tmpDwg, "Test 2");
        // If a valid reference is created then continue
        if (!acXrefId.IsNull)
        {
            // Attach the DWG reference to the current space
            Point3d insPt = new Point3d(1, 1, 0);
            using (BlockReference acBlkRef = new BlockReference(insPt, acXrefId))
            {
                BlockTableRecord acBlkTblRec;
                acBlkTblRec = acTrans.GetObject(acCurDb.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;
                acBlkTblRec.AppendEntity(acBlkRef);
                acTrans.AddNewlyCreatedDBObject(acBlkRef, true);
                Application.ShowAlertDialog("The external reference is attached.");
                Matrix3d mat = acBlkRef.BlockTransform;
                mat.Inverse();
                Point2dCollection ptCol = new Point2dCollection();
                // Define the first corner of the clipping boundary
                Point3d pt3d = new Point3d(200, 300, 0);
                pt3d.TransformBy(mat);
                ptCol.Add(new Point2d(pt3d.X, pt3d.Y));
                // Define the second corner of the clipping boundary
                pt3d = new Point3d(1200, 1100, 0);
                pt3d.TransformBy(mat);
                ptCol.Add(new Point2d(pt3d.X, pt3d.Y));
                // Define the normal and elevation for the clipping boundary
                Vector3d normal;
                double elev = 0;
                if (acCurDb.TileMode == true)
                {
                    normal = acCurDb.Ucsxdir.CrossProduct(acCurDb.Ucsydir);
                    elev = acCurDb.Elevation;
                }
                else
                {
                    normal = acCurDb.Pucsxdir.CrossProduct(acCurDb.Pucsydir);
                    elev = acCurDb.Pelevation;
                }
                // Set the clipping boundary and enable it
                using (SpatialFilter filter = new SpatialFilter())
                {
                    SpatialFilterDefinition filterDef = new SpatialFilterDefinition(ptCol, normal, elev, 0, 0, true);
                    filter.Definition = filterDef;
                    // Define the name of the extension dictionary and entry name
                    string dictName = "ACAD_FILTER";
                    string spName = "SPATIAL";
                    // Check to see if the Extension Dictionary exists, if not create it
                    if (acBlkRef.ExtensionDictionary.IsNull)
                    {
                        acBlkRef.CreateExtensionDictionary();
                    }
                    // Open the Extension Dictionary for write
                    DBDictionary extDict = acTrans.GetObject(acBlkRef.ExtensionDictionary, OpenMode.ForWrite) as DBDictionary;
                    // Check to see if the dictionary for clipped boundaries exists,
                    // and add the spatial filter to the dictionary
                    if (extDict.Contains(dictName))
                    {
                        DBDictionary filterDict = acTrans.GetObject(extDict.GetAt(dictName), OpenMode.ForWrite) as DBDictionary;
                        if (filterDict.Contains(spName))
                        {
                            filterDict.Remove(spName);
                        }
                        filterDict.SetAt(spName, filter);
                    }
                    else
                    {
                        using (DBDictionary filterDict = new DBDictionary())
                        {
                            extDict.SetAt(dictName, filterDict);
                            acTrans.AddNewlyCreatedDBObject(filterDict, true);
                            filterDict.SetAt(spName, filter);
                        }
                    }
                    // Append the spatial filter to the drawing
                    acTrans.AddNewlyCreatedDBObject(filter, true);
                }
            }
            Application.ShowAlertDialog("The external reference is clipped.");
        }
        // Save the new objects to the database
        acTrans.Commit();
        // Dispose of the transaction
    }
}

Работа с XRecord и XData

Существует 2 механизма хранения произвольных данных у объекта - с помощью Xrecord и XData. Принципиальное отличие вариантов в объеме хранимых данных. Если XRecord имеет лимит до 2 Гб данных, то XData ограничено 16 Кб.

XData само по себе не является классом или структурой, это лишь название свойства, значением которого выступает ResultBuffer. Свойство XData имеют все объекты, унаследованные от DBObject и именно данные в нём имеют ограничение на 16 Кб. Xrecord кстати тоже имеет свойство Xdata, и там такого ограничения нет (т.к. свойство XData у DBObject имеет модификатор virtual, что означает, что оно переопределено у Xrecord).

Xrecord хранятся в словарях, а словари хранятся как при объектах, также унаследованные от DBObject (идентификатор словаря в значении свойства ExtensionDictionary), так и на уровне базы данных.

И XRecord и XData апеллириют набором данных, представленных в виде ResultBuffer. Для того, чтобы сохранить некоторую информацию в ResultBuffer, в чертеже должно быть зарегистрировано приложение (иметься запись RegAppTableRecord), для которого можно сохранить перечень данных разного типа, в отличие от стандартных DxfCode.

В примере ниже получается набор объектов из пользовательского выбора, и в свойство XData каждого из объектов добавляется ResultBuffer из пары значений -- имени приложения и некоторой текстовой строки (или иного типа данных для ResultBuffer, см. соответствующую статью.

Пример работы с XRecord в составе словарей см. в статье про словари.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.EditorInput;

[CommandMethod("AttachXDataToSelectionSetObjects")]
public void AttachXDataToSelectionSetObjects()
{
    // Get the current database and start a transaction
    Database acCurDb;
    acCurDb = Application.DocumentManager.MdiActiveDocument.Database;

    Document acDoc = Application.DocumentManager.MdiActiveDocument;

    string appName = "MY_APP";
    string xdataStr = "This is some xdata";

    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Request objects to be selected in the drawing area
        PromptSelectionResult acSSPrompt = acDoc.Editor.GetSelection();

        // If the prompt status is OK, objects were selected
        if (acSSPrompt.Status == PromptStatus.OK)
        {
            // Open the Registered Applications table for read
            RegAppTable acRegAppTbl;
            acRegAppTbl = acTrans.GetObject(acCurDb.RegAppTableId, OpenMode.ForRead) as RegAppTable;

            // Check to see if the Registered Applications table record for the custom app exists
            if (acRegAppTbl.Has(appName) == false)
            {
                using (RegAppTableRecord acRegAppTblRec = new RegAppTableRecord())
                {
                    acRegAppTblRec.Name = appName;

                    acTrans.GetObject(acCurDb.RegAppTableId, OpenMode.ForWrite);
                    acRegAppTbl.Add(acRegAppTblRec);
                    acTrans.AddNewlyCreatedDBObject(acRegAppTblRec, true);
                }
            }

            // Define the Xdata to add to each selected object
            using (ResultBuffer rb = new ResultBuffer())
            {
                rb.Add(new TypedValue((int)DxfCode.ExtendedDataRegAppName, appName));
                rb.Add(new TypedValue((int)DxfCode.ExtendedDataAsciiString, xdataStr));

                SelectionSet acSSet = acSSPrompt.Value;

                // Step through the objects in the selection set
                foreach (SelectedObject acSSObj in acSSet)
                {
                    // Open the selected object for write
                    Entity acEnt = acTrans.GetObject(acSSObj.ObjectId,
                                                        OpenMode.ForWrite) as Entity;

                    // Append the extended data to each object
                    acEnt.XData = rb;
                }
            }
        }

        // Save the new object to the database
        acTrans.Commit();

        // Dispose of the transaction
    }
}

В примере ниже сохраненная в примере выше информация выводится в командную строку для каждого из объектов из пользовательского выбора

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.EditorInput;

[CommandMethod("ViewXData")]
public void ViewXData()
{
    // Get the current database and start a transaction
    Database acCurDb;
    acCurDb = Application.DocumentManager.MdiActiveDocument.Database;

    Document acDoc = Application.DocumentManager.MdiActiveDocument;

    string appName = "MY_APP";
    string msgstr = "";

    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Request objects to be selected in the drawing area
        PromptSelectionResult acSSPrompt = acDoc.Editor.GetSelection();

        // If the prompt status is OK, objects were selected
        if (acSSPrompt.Status == PromptStatus.OK)
        {
            SelectionSet acSSet = acSSPrompt.Value;

            // Step through the objects in the selection set
            foreach (SelectedObject acSSObj in acSSet)
            {
                // Open the selected object for read
                Entity acEnt = acTrans.GetObject(acSSObj.ObjectId,
                                                 OpenMode.ForRead) as Entity;

                // Get the extended data attached to each object for MY_APP
                ResultBuffer rb = acEnt.GetXDataForApplication(appName);

                // Make sure the Xdata is not empty
                if (rb != null)
                {
                    // Get the values in the xdata
                    foreach (TypedValue typeVal in rb)
                    {
                        msgstr = msgstr + "\n" + typeVal.TypeCode.ToString() + ":" + typeVal.Value;
                    }
                }
                else
                {
                    msgstr = "NONE";
                }

                // Display the values returned
                Application.ShowAlertDialog(appName + " xdata on " + acEnt.GetType().ToString() + ":\n" + msgstr);

                msgstr = "";
            }
        }

        // Ends the transaction and ensures any changes made are ignored
        acTrans.Abort();

        // Dispose of the transaction
    }
}

Работа со словарями

Словари - это специальные неграфические объекты, имеющие структуру "ключ:значение"; ключём выступают строки, значением - ObjectId.

Для проверки, имеет ли словарь элемент с данным строковым ключем имеется метод Has(string)

В явном виде (объектами класса DBDictionary) словари нигде не хранятся, их необходимо получать через ObjectId и приведением к классу DBDictionary.

В чертеже имеются постоянные словари, описывающие служебные коллекции объектов чертежа.

У прочих объектов, наследующих DBObject по умолчанию словарей может не быть, если свойство DBObject.ExtensionDictionary возвращает ObjectId.Null, то словарь необходимо создать служебным методом DBObject.CreateExtensionDictionary, после этого свойство ExtensionDictionary должно указать на корректный ObjectId.

Ниже приведен расширенный пример регистрации в чертеже приложения SEMANTIC_APP, создании для объекта словаря, записи в словарь пользовательского словаря SEMANTICS с набором Xrecord, значением XData которого являются пользовательские строковые свойства, сохраненные под приложением SEMANTIC_APP:

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using System.Collections.Generic;

private const string SEMANTIC_APP = "SEMANTIC_APP";
private const string SEMANTIC_DIСT = "SEMANTICS";
private void checkRegApp()
{
    Database cadDb = Application.DocumentManager.MdiActiveDocument.Database;
    using (Transaction cadTrans = cadDb.TransactionManager.StartTransaction())
    {
        RegAppTable acRegAppTbl;
        acRegAppTbl = cadTrans.GetObject(cadDb.RegAppTableId,
                                              OpenMode.ForRead) as RegAppTable;
        // Проверить наличие SEMANTIC_APP
        if (acRegAppTbl.Has(SEMANTIC_APP) == false)
        {
            using (RegAppTableRecord acRegAppTblRec = new RegAppTableRecord())
            {
                acRegAppTblRec.Name = SEMANTIC_APP;
                cadTrans.GetObject(cadDb.RegAppTableId, OpenMode.ForWrite);
                acRegAppTbl.Add(acRegAppTblRec);
                cadTrans.AddNewlyCreatedDBObject(acRegAppTblRec, true);
            }
        }

        cadTrans.Commit();
    }
}
private void saveObjectProps(ObjectId dwgEntId, Dictionary<string, string> properties)
{
    checkRegApp();
    Database cadDb = Application.DocumentManager.MdiActiveDocument.Database;
    using (Transaction cadTrans = cadDb.TransactionManager.StartTransaction())
    {
        RegAppTable acRegAppTbl;
        acRegAppTbl = cadTrans.GetObject(cadDb.RegAppTableId,
                                              OpenMode.ForRead) as RegAppTable;
        // Проверить наличие SEMANTIC_APP
        if (acRegAppTbl.Has(SEMANTIC_APP) == false)
        {
            using (RegAppTableRecord acRegAppTblRec = new RegAppTableRecord())
            {
                acRegAppTblRec.Name = SEMANTIC_APP;
                cadTrans.GetObject(cadDb.RegAppTableId, OpenMode.ForWrite);
                acRegAppTbl.Add(acRegAppTblRec);
                cadTrans.AddNewlyCreatedDBObject(acRegAppTblRec, true);
            }
        }

        DBObject dwgEnt = cadTrans.GetObject(dwgEntId, OpenMode.ForWrite);

        if (dwgEnt.ExtensionDictionary == ObjectId.Null) dwgEnt.CreateExtensionDictionary();
        DBDictionary? entDict = cadTrans.GetObject(dwgEnt.ExtensionDictionary, OpenMode.ForWrite) as DBDictionary;
        if (entDict == null) return;


        // Если словарь уже существует, удалим его
        ObjectId propsDictId;
        if (entDict.Contains(SEMANTIC_DIСT))
        {
            entDict.Remove(SEMANTIC_DIСT);
        }

        DBDictionary propsDict = new DBDictionary();
        entDict.SetAt(SEMANTIC_DIСT, propsDict);
        cadTrans.AddNewlyCreatedDBObject(propsDict, true);
        propsDictId = propsDict.ObjectId;



        propsDict = (DBDictionary)cadTrans.GetObject(propsDictId, OpenMode.ForWrite);

        // Создаем Xrecord для всех значений свойств
        int indexCounter = 0;
        foreach (var propValueInfo in properties)
        {
            string propKey = $"Property_{indexCounter}";
            ObjectId xRecordId;

            Xrecord xrec = new Xrecord();
            propsDict.SetAt(propKey, xrec);
            cadTrans.AddNewlyCreatedDBObject(xrec, true);
            xRecordId = xrec.ObjectId;

            Xrecord propValueInfoRecord = (Xrecord)cadTrans.GetObject(xRecordId, OpenMode.ForWrite);
            using (ResultBuffer propValueInfoRecord_RB = new ResultBuffer())
            {
                propValueInfoRecord_RB.Add(new TypedValue((int)DxfCode.ExtendedDataRegAppName, SEMANTIC_APP));
                propValueInfoRecord_RB.Add(new TypedValue((int)DxfCode.ExtendedDataAsciiString, propValueInfo.Key));
                propValueInfoRecord_RB.Add(new TypedValue((int)DxfCode.ExtendedDataAsciiString, propValueInfo.Value)); //
                propValueInfoRecord.XData = propValueInfoRecord_RB;
            }
            indexCounter++;
        }
        cadTrans.Commit();
    }
}

private void listObjectProperies(ObjectId entId)
{
    Document doc = Application.DocumentManager.MdiActiveDocument;
    Database cadDb = doc.Database;
    using (Transaction cadTrans = cadDb.TransactionManager.StartTransaction())
    {
        // Получаем словарь объекта, если его нет -- то создаем
        DBObject dwgEnt = cadTrans.GetObject(entId, OpenMode.ForWrite);

        DBDictionary? entDict = cadTrans.GetObject(dwgEnt.ExtensionDictionary, OpenMode.ForRead) as DBDictionary;
        if (entDict == null)
        {
            doc.Editor.WriteMessage("У объекта отсутствуют данные");
            return;
        }

        if (!entDict.Contains(SEMANTIC_DIСT))
        {
            if (entDict == null)
            {
                doc.Editor.WriteMessage("У объекта отсутствуют данные для " + SEMANTIC_APP);
                return;
            }
        }
        ObjectId propsDictId = entDict.GetAt(SEMANTIC_DIСT);
        DBDictionary? propsDict = cadTrans.GetObject(propsDictId, OpenMode.ForRead) as DBDictionary;
        if (propsDict == null)
        {
            doc.Editor.WriteMessage("Не удалось привести к словарю");
            return;
        }

        doc.Editor.WriteMessage($"\nProperties for " + entId.ToString());
        foreach (var propValueInfoRaw in propsDict)
        {
            Xrecord? propValueInfoDef = cadTrans.GetObject(propValueInfoRaw.Value, OpenMode.ForRead) as Xrecord;

            if (propValueInfoDef == null) continue;
            ResultBuffer propValueInfoDefData = propValueInfoDef.GetXDataForApplication(SEMANTIC_APP);
            if (propValueInfoDefData == null) continue;
            {
                var propArray = propValueInfoDefData.AsArray();

                doc.Editor.WriteMessage($"\nProperty " +
                    $"Name:{propArray[1].Value} Value:{propArray[2].Value}");
            }
        }
        doc.Editor.WriteMessage($"\nEnd Properties!\n");
    }
}

[CommandMethod("SaveExtProperties")]
public void DemoSaveProperties()
{
    Document doc = Application.DocumentManager.MdiActiveDocument;
    PromptEntityOptions selOpts = new PromptEntityOptions("Select entity");
    selOpts.SetRejectMessage("Ent is not valid");
    selOpts.AddAllowedClass(typeof(Entity), false);
    PromptEntityResult selResult = doc.Editor.GetEntity(selOpts);
    if (selResult.Status != PromptStatus.OK) return;

    saveObjectProps(selResult.ObjectId, new Dictionary<string, string>()
        {
            {"Handle", selResult.ObjectId.Handle.ToString() },
            {"DxfName", selResult.ObjectId.ObjectClass.DxfName }
        });
}

[CommandMethod("ListExtProperties")]
public void DemoListExtProperties()
{
    Document doc = Application.DocumentManager.MdiActiveDocument;
    PromptEntityOptions selOpts = new PromptEntityOptions("Select entity");
    selOpts.SetRejectMessage("Ent is not valid");
    selOpts.AddAllowedClass(typeof(Entity), false);
    PromptEntityResult selResult = doc.Editor.GetEntity(selOpts);
    if (selResult.Status != PromptStatus.OK) return;

    listObjectProperies(selResult.ObjectId);
}

Работа с листами и печатью

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

Пространства модели и листов

Пространство модели — это среда для создания графических объектов, отрисованных в реальном масштабе.

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

Работа с листами

Вся геометрия чертежа содержится в листах. Пространство модели также является листом с фиксированным именем Model, его нельзя переименовать, в одном чертеже может находиться только одного пространство модели.

В чертеже может быть несколько (до 255) различных листов со своими настройками.

Запись BlockTableRecord из таблицы BlockTable с именем "*MODEL_SPACE" относится к пространству модели; запись с именем "*PAPER_SPACE" относится к активному листу (так как листов может быть несколько).

Существует два подхода к работе с листами в чертеже. Если вы работаете с текущим чертежом, вы можете использовать диспетчер листов, который представлен классом LayoutManager. Если вы работаете не с текущим чертежом, а, например, считываете чертеж из-под его Database, вы можете работать с именованным словарем Layout. Впрочем, вы можете работать со словарём в любой момент, однако LayoutManager значительно упрощает выполнение некоторых задач (только для активного чертежа, так как он получается лишь для открытого файла с помощью статического поля Current).

Перебор листов

В примере ниже в командную строку выводятся имена всех листов, имеющихся в документе

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

// List all the layouts in the current drawing
[CommandMethod("ListLayouts")]
public void ListLayouts()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Get the layout dictionary of the current database
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        DBDictionary lays = 
            acTrans.GetObject(acCurDb.LayoutDictionaryId, 
                OpenMode.ForRead) as DBDictionary;

        acDoc.Editor.WriteMessage("\nLayouts:");

        // Step through and list each named layout and Model
        foreach (DBDictionaryEntry item in lays)
        {
            acDoc.Editor.WriteMessage("\n  " + item.Key);
        }

        // Abort the changes to the database
        acTrans.Abort();
    }
}

Создание нового листа

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

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

// Create a new layout with the LayoutManager
[CommandMethod("CreateLayout")]
public void CreateLayout()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Get the layout and plot settings of the named pagesetup
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Reference the Layout Manager
        LayoutManager acLayoutMgr = LayoutManager.Current;

        // Create the new layout with default settings
        ObjectId objID = acLayoutMgr.CreateLayout("newLayout");

        // Open the layout
        Layout acLayout = acTrans.GetObject(objID,
                                            OpenMode.ForRead) as Layout;

        // Set the layout current if it is not already
        if (acLayout.TabSelected == false)
        {
            acLayoutMgr.CurrentLayout = acLayout.LayoutName;
        }

        // Output some information related to the layout object
        acDoc.Editor.WriteMessage("\nTab Order: " + acLayout.TabOrder +
                                  "\nTab Selected: " + acLayout.TabSelected +
                                  "\nBlock Table Record ID: " +
                                  acLayout.BlockTableRecordId.ToString());

        // Save the changes made
        acTrans.Commit();
    }
}

Вставка листа из другого чертежа

В примере ниже создается пустой чертеж, далее в текущий чертеж вставляется лист из созданного файла

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

// Import a layout from an external drawing
[CommandMethod("ImportLayout")]
public void ImportLayout()
{
    string tmpDwg = @"C:\Temp\test.dwg";
    using (Database dbTmp = new Database())
    {
        dbTmp.SaveAs(tmpDwg, DwgVersion.Current);
    }
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;
    // Specify the layout name and drawing file to work with
    string layoutName = "Лист1";
    // Create a new database object and open the drawing into memory
    Database acExDb = new Database(false, true);
    acExDb.ReadDwgFile(tmpDwg, FileOpenMode.OpenForReadAndAllShare, true, "");
    // Create a transaction for the external drawing
    using (Transaction acTransEx = acExDb.TransactionManager.StartTransaction())
    {
        // Get the layouts dictionary
        DBDictionary layoutsEx =
            acTransEx.GetObject(acExDb.LayoutDictionaryId,
                                OpenMode.ForRead) as DBDictionary;
        // Check to see if the layout exists in the external drawing
        if (layoutsEx.Contains(layoutName) == true)
        {
            // Get the layout and block objects from the external drawing
            Layout layEx =
                layoutsEx.GetAt(layoutName).GetObject(OpenMode.ForRead) as Layout;
            BlockTableRecord blkBlkRecEx =
                acTransEx.GetObject(layEx.BlockTableRecordId,
                                    OpenMode.ForRead) as BlockTableRecord;
            // Get the objects from the block associated with the layout
            ObjectIdCollection idCol = new ObjectIdCollection();
            foreach (ObjectId id in blkBlkRecEx)
            {
                idCol.Add(id);
            }
            // Create a transaction for the current drawing
            using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
            {
                // Get the block table and create a new block
                // then copy the objects between drawings
                BlockTable blkTbl =
                    acTrans.GetObject(acCurDb.BlockTableId,
                                      OpenMode.ForWrite) as BlockTable;
                using (BlockTableRecord blkBlkRec = new BlockTableRecord())
                {
                    int layoutCount = layoutsEx.Count - 1;
                    blkBlkRec.Name = "*Paper_Space" + layoutCount.ToString();
                    blkTbl.Add(blkBlkRec);
                    acTrans.AddNewlyCreatedDBObject(blkBlkRec, true);
                    acExDb.WblockCloneObjects(idCol,
                                              blkBlkRec.ObjectId,
                                              new IdMapping(),
                                              DuplicateRecordCloning.Ignore,
                                              false);
                    // Create a new layout and then copy properties between drawings
                    DBDictionary layouts =
                        acTrans.GetObject(acCurDb.LayoutDictionaryId,
                                          OpenMode.ForWrite) as DBDictionary;
                    using (Layout lay = new Layout())
                    {
                        lay.LayoutName = layoutName;
                        lay.AddToLayoutDictionary(acCurDb, blkBlkRec.ObjectId);
                        acTrans.AddNewlyCreatedDBObject(lay, true);
                        lay.CopyFrom(layEx);
                        DBDictionary plSets =
                            acTrans.GetObject(acCurDb.PlotSettingsDictionaryId,
                                              OpenMode.ForRead) as DBDictionary;
                        // Check to see if a named page setup was assigned to the layout,
                        // if so then copy the page setup settings
                        if (lay.PlotSettingsName != "")
                        {
                            // Check to see if the page setup exists
                            if (plSets.Contains(lay.PlotSettingsName) == false)
                            {
                                acTrans.GetObject(acCurDb.PlotSettingsDictionaryId, OpenMode.ForWrite);
                                using (PlotSettings plSet = new PlotSettings(lay.ModelType))
                                {
                                    plSet.PlotSettingsName = lay.PlotSettingsName;
                                    plSet.AddToPlotSettingsDictionary(acCurDb);
                                    acTrans.AddNewlyCreatedDBObject(plSet, true);
                                    DBDictionary plSetsEx =
                                        acTransEx.GetObject(acExDb.PlotSettingsDictionaryId,
                                                            OpenMode.ForRead) as DBDictionary;
                                    PlotSettings plSetEx =
                                        plSetsEx.GetAt(lay.PlotSettingsName).GetObject(
                                                       OpenMode.ForRead) as PlotSettings;
                                    plSet.CopyFrom(plSetEx);
                                }
                            }
                        }
                    }
                }
                // Regen the drawing to get the layout tab to display
                acDoc.Editor.Regen();
                // Save the changes made
                acTrans.Commit();
            }
        }
        else
        {
            // Display a message if the layout could not be found in the specified drawing
            acDoc.Editor.WriteMessage("\\nLayout '" + layoutName +
                                      "' could not be imported from '" + tmpDwg + "'.");
        }
        // Discard the changes made to the external drawing file
        acTransEx.Abort();
    }
    // Close the external drawing file
    acExDb.Dispose();
}

Настройки листов

Настройки листов определяют, как выводимый лист будет выведен на печать. Настройки включают формат бумаги, масштаб печати, границы печатаемой область листа, начало координат, имя принтера. Большинство настроек, связанных с выводом листа на печать, доступны только для чтения. Изменение настроек печати для листа в основном проходит через специальные объекты PlotSettings и PlotSettingsValidator.

Примечание: обращаем внимание, что в nanoCAD настройки печати не хранятся в pc3-файлах, соответственно все примеры далее вам необходимо будет корректировать.

Формат бумаги и единицы измерения

Выбор размера (формата) бумаги зависит от используемого для вывода устройства или плоттера. Каждое устройство имеет свой стандартный список доступных форматов бумаги, который можно получить с помощью метода GetCanonicalMediaNameList у класса PlotSettingsValidator (получается через статическое поля класса Current). Другой метод GetLocaleMediaName можно использовать для отображения локализованного наименования формата, какой доступен в диалоговых окнах со стороны UI. Формат бумаги, назначенный листу можно запросить с помощью свойства CanonicalMediaName у объекта Layout.

Вы также можете получить единицы измерения листа с помощью свойства PlotPaperUnits. Это свойство возвращает одно из трех значений, определенных перечислением PlotPaperUnit: дюймы, миллиметры или пиксели. Если ваше устройство печати настроено для растрового вывода, размер вывода будет возвращен в пикселях. Задать единицы измерения можно с помощью метода SetPlotPaperUnits класса PlotSettingsValidator.

В примере ниже показывается размеров бумаги для конфигурации "DWF6 ePlot.pc3"

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.PlottingServices;

// Lists the available local media names for a specified plot configuration (PC3) file
[CommandMethod("PlotterLocalMediaNameList")]
public static void PlotterLocalMediaNameList()
{
    // Get the current document and database, and start a transaction
    Document acDoc = Application.DocumentManager.MdiActiveDocument;

    using(PlotSettings plSet = new PlotSettings(true))
    {
        PlotSettingsValidator acPlSetVdr = PlotSettingsValidator.Current;

        // Set the Plotter and page size
        acPlSetVdr.SetPlotConfigurationName(plSet, "DWF6 ePlot.pc3",
                                            "ANSI_A_(8.50_x_11.00_Inches)");

        acDoc.Editor.WriteMessage("\nCanonical and Local media names: ");

        int cnt = 0;

        foreach (string mediaName in acPlSetVdr.GetCanonicalMediaNameList(plSet))
        {
            // Output the names of the available media for the specified device
            acDoc.Editor.WriteMessage("\n  " + mediaName + " | " +
                                      acPlSetVdr.GetLocaleMediaName(plSet, cnt));

            cnt = cnt + 1;
        }
    }
}

Начало координат печати

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

Обычно начало координат устанавливается в (0, 0). Иногда пространство для печати может быть отцентровано на данном листе, в этом случае свойство PlotCentered вернет true.

Область печати

При выводе листа на печать, сама область печати определяется свойством PlotType, её значением выступает флаги перечисления PlotType;

  • Display : Печатает все, что отрисовано в пространстве модели. Этот флаг недоступен при выполнении печати из листов;
  • Extents : Печатает все, что находится в пределах установленных границ выбранного пространства;
  • Limits : Печатает все, что находится в пределах границ текущего пространства;
  • View : Печатается вид, заданный свойством PlotViewName
  • Window : Печатает все, что находится в окне, указанном свойством PlotWindowArea;
  • Layout : Печатает все, что находится в пределах полей указанного формата бумаги. Этот параметр недоступен при печати из пространства модели; При создании нового листа по умолчанию используется параметр Layout.

Масштаб печати

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

Если свойство UseStandardScale установлено в значение true, то используется один из стандартных масштабов, его значение можно установить с помощью свойства StdScale. В противном случае, при UseStandardScale = false, будет использоваться пользовательский масштаб, значение для него можно установить через свойство CustomPrintScale.

При просмотре чернового варианта чертежа точный масштаб не всегда важен. Вы можете вызвать метод SetStdScaleType для значения StdScaleType.ScaleToFit, чтобы распечатать лист в максимально возможном размере, соответствующем размеру выходного изображения.

Масштаб веса линий

Свойство ScaleLineweights, установленное в значение true, позволяет при смене масштаба печати пропорционально изменять масштаб типов линий, в противном случае используется масштаб по умолчанию = 1:1.

Устройства печати

Имя устройства печати, назначенное листу, хранится в свойстве PlotConfigurationName. Имя будет содержать одно из системных устройств вывода, если не назначено устройство по умолчанию. Вы можете получить список всех возможных устройств вывода из числа системных и AutoCAD, получив их список через метод GetPlotDeviceList объекта PlotSettingsValidator.

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

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.PlottingServices;

// Lists the available plotters (plot configuration [PC3] files)
[CommandMethod("PlotterList")]
public static void PlotterList()
{
    // Get the current document and database, and start a transaction
    Document acDoc = Application.DocumentManager.MdiActiveDocument;

    acDoc.Editor.WriteMessage("\nPlot devices: ");

    foreach (string plotDevice in PlotSettingsValidator.Current.GetPlotDeviceList())
    {
        // Output the names of the available plotter devices
        acDoc.Editor.WriteMessage("\n  " + plotDevice);
    }
}

Получение и редактирование настроек печати

В примере ниже показывается процесс получения настроек печати для текущего листа и их изменение

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.PlottingServices;
 
// Changes the plot settings for a layout directly
[CommandMethod("ChangeLayoutPlotSettings")]
public static void ChangeLayoutPlotSettings()
{
    // Get the current document and database, and start a transaction
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Reference the Layout Manager
        LayoutManager acLayoutMgr = LayoutManager.Current;

        // Get the current layout and output its name in the Command Line window
        Layout acLayout = acTrans.GetObject(acLayoutMgr.GetLayoutId(acLayoutMgr.CurrentLayout),
                                            OpenMode.ForRead) as Layout;

        // Output the name of the current layout and its device
        acDoc.Editor.WriteMessage("\nCurrent layout: " + acLayout.LayoutName);

        acDoc.Editor.WriteMessage("\nCurrent device name: " + acLayout.PlotConfigurationName);

        // Get a copy of the PlotSettings from the layout
        using (PlotSettings acPlSet = new PlotSettings(acLayout.ModelType))
        {
            acPlSet.CopyFrom(acLayout);

            // Update the PlotConfigurationName property of the PlotSettings object
            PlotSettingsValidator acPlSetVdr = PlotSettingsValidator.Current;
            acPlSetVdr.SetPlotConfigurationName(acPlSet, "DWG To PDF.pc3", "ANSI_B_(11.00_x_17.00_Inches)");

            // Zoom to show the whole paper
            acPlSetVdr.SetZoomToPaperOnUpdate(acPlSet, true);

            // Update the layout
            acTrans.GetObject(acLayoutMgr.GetLayoutId(acLayoutMgr.CurrentLayout), OpenMode.ForWrite);
            acLayout.CopyFrom(acPlSet);
        }

        // Output the name of the new device assigned to the layout
        acDoc.Editor.WriteMessage("\nNew device name: " + acLayout.PlotConfigurationName);

        // Save the new objects to the database
        acTrans.Commit();
    }

    // Update the display
    acDoc.Editor.Regen();
}

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

Видовые экраны

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

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

TODO Use Shaded Viewports (.NET)

Переключение пространства

Редактировать модель из пространства листа невозможно. Чтобы получить доступ к модели для данного ВЭ Viewport , переключитесь из пространства листа в пространство модели, используя методы SwitchToModelSpace и SwitchToPaperSpace класса Editor. В результате вы сможете работать с моделью, сохраняя при этом видимым лист. В видовых экранах Viewport возможности редактирования и изменения настроек вида практически такие же, как и в объектах ViewportTableRecord.

Тем не менее, редактируя настройки ВЭ, у вас больше возможностей: например, вы можете замораживать или размораживать слои в некоторых видовых экранах, не затрагивая остальные экраны, включать или выключать отображение геометрии в видовом окне (альтернатива команды ON_OFF_Viewport).

При работе в объекте видового экрана вы можете находиться либо в пространстве модели, либо в пространстве листа. Определить, работаете ли вы в пространстве модели, можно, проверив текущие значения системных переменных TILEMODE и CVPORT. Если TILEMODE равно 0, а CVPORT имеет значение, отличное от 2, вы работаете в пространстве листа; если TILEMODE равно 0, а CVPORT равно 2, вы работаете в пространстве модели. Если TILEMODE равно 1, вы работаете в пространстве модели с видовым экраном по умолчанию в единственном числе.

Примечание: Перед переключением в пространство модели из листа необходимо установить свойство "On = true" хотя бы для одного объекта Viewport на листе. Пример ниже содержит код, инвертирующий активное пространство чертежа (пространство модели меняется на область листа и наоборот).

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
 
[CommandMethod("ToggleSpace")]
public static void ToggleSpace()
{
  // Get the current document
  Document acDoc = Application.DocumentManager.MdiActiveDocument;
 
  // Get the current values of CVPORT and TILEMODE
  object oCvports = Application.GetSystemVariable("CVPORT");
  object oTilemode = Application.GetSystemVariable("TILEMODE");
 
  // Check to see if the Model layout is active, TILEMODE is 1 when
  // the Model layout is active
  if (System.Convert.ToInt16(oTilemode) == 0)
  {
      // Check to see if Model space is active in a viewport,
      // CVPORT is 2 if Model space is active 
      if (System.Convert.ToInt16(oCvports) == 2)
      {
          acDoc.Editor.SwitchToPaperSpace();
      }
      else
      {
          acDoc.Editor.SwitchToModelSpace();
      }
  }
  else
  {
      // Switch to the previous Paper space layout
      Application.SetSystemVariable("TILEMODE", 0);
  }
}

Создание видовых экранов

Видовые окна в пространстве листа создаются путем создания экземпляров классов Viewport и добавления их к объекту BloctTableRecord для данного листа. Конструктор объекта Viewport не принимает никаких параметров. После создания экземпляра объекта Viewport вы можете задать его расположение на листе с помощью свойств CenterPoint, Width и Height.

Также вы можете задать свойства самого вида, такие как направление просмотра (свойство ViewDirection), фокусное расстояние для перспетивного вида (свойство LensLength) и флаг отображения сетки (свойство GridOn). Вы также можете управлять свойствами самого видового окна, такими как слой (свойство Layer), тип линии (свойство Linetype) и масштабирование типов линий (свойство LinetypeScale).

Создание ВЭ

В примере ниже приводится код, делающий активым область листов, создающий там плавающий ВЭ, задающий вид для данного ВЭ и делающий данный ВЭ активным. Установка ВЭ активным делается с помощью обращения к ARX-методу acedSetCurrentVPort, для которого прописывается соответствующая точка входа EntryPoint.

using System.Runtime.InteropServices;

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl,
 EntryPoint = "?acedSetCurrentVPort@@YA?AW4ErrorStatus@Acad@@PBVAcDbViewport@@@Z")]
extern static private int acedSetCurrentVPort(IntPtr AcDbVport);

[CommandMethod("CreateFloatingViewport")]
public static void CreateFloatingViewport()
{
    // Get the current document and database, and start a transaction
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                     OpenMode.ForRead) as BlockTable;

        // Open the Block table record Paper space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.PaperSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Switch to the previous Paper space layout
        Application.SetSystemVariable("TILEMODE", 0);
        acDoc.Editor.SwitchToPaperSpace();

        // Create a Viewport
        using (Viewport acVport = new Viewport())
        {
            acVport.CenterPoint = new Point3d(3.25, 3, 0);
            acVport.Width = 6;
            acVport.Height = 5;

            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acVport);
            acTrans.AddNewlyCreatedDBObject(acVport, true);

            // Change the view direction
            acVport.ViewDirection = new Vector3d(1, 1, 1);

            // Enable the viewport
            acVport.On = true;

            // Activate model space in the viewport
            acDoc.Editor.SwitchToModelSpace();

            // Set the new viewport current via an imported ObjectARX function
            acedSetCurrentVPort(acVport.UnmanagedObject);
        }

        // Save the new objects to the database
        acTrans.Commit();
    }
}

Примечание: аналогичная процедура acedSetCurrentVPort для nanoCAD будет выглядеть так:

[DllImport("NrxHostGate.dll", CallingConvention = CallingConvention.Cdecl,
          EntryPoint = "?ncedSetCurrentVPort@@YA?AW4ErrorStatus@Nano@@PEBVNcDbViewport@@@Z")]
extern static private int acedSetCurrentVPort(System.IntPtr AcDbVport);

Примечание: Чтобы задать параметры вида (направление обзора, фокусное расстояние и т. д.), свойство On объекта Viewport должно быть установлено в значение false, а перед установкой текущего состояния видового окна свойство On должно быть установлено в значение true.

Создание нескольких ВЭ

В примере ниже создается 4 ВЭ, каждый из которых имеет фиксированное направление вида (ViewDirection)

using System.Runtime.InteropServices;

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl,
 EntryPoint = "?acedSetCurrentVPort@@YA?AW4ErrorStatus@Acad@@PBVAcDbViewport@@@Z")]
extern static private int acedSetCurrentVPort(IntPtr AcDbVport);

[CommandMethod("FourFloatingViewports")]
public static void FourFloatingViewports()
{
  // Get the current document and database, and start a transaction
  Document acDoc = Application.DocumentManager.MdiActiveDocument;
  Database acCurDb = acDoc.Database;

  using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
  {
      // Open the Block table for read
      BlockTable acBlkTbl;
      acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                   OpenMode.ForRead) as BlockTable;

      // Open the Block table record Paper space for write
      BlockTableRecord acBlkTblRec;
      acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.PaperSpace],
                                      OpenMode.ForWrite) as BlockTableRecord;

      // Switch to the previous Paper space layout
      Application.SetSystemVariable("TILEMODE", 0);
      acDoc.Editor.SwitchToPaperSpace();

      Point3dCollection acPt3dCol = new Point3dCollection();
      acPt3dCol.Add(new Point3d(2.5, 5.5, 0));
      acPt3dCol.Add(new Point3d(2.5, 2.5, 0));
      acPt3dCol.Add(new Point3d(5.5, 5.5, 0));
      acPt3dCol.Add(new Point3d(5.5, 2.5, 0));

      Vector3dCollection acVec3dCol = new Vector3dCollection();
      acVec3dCol.Add(new Vector3d(0, 0, 1));
      acVec3dCol.Add(new Vector3d(0, 1, 0));
      acVec3dCol.Add(new Vector3d(1, 0, 0));
      acVec3dCol.Add(new Vector3d(1, 1, 1));

      double dWidth = 2.5;
      double dHeight = 2.5;

      Viewport acVportLast = null;
      int nCnt = 0;

      foreach (Point3d acPt3d in acPt3dCol)
      {
          using (Viewport acVport = new Viewport())
          {
              acVport.CenterPoint = acPt3d;
              acVport.Width = dWidth;
              acVport.Height = dHeight;

              // Add the new object to the block table record and the transaction
              acBlkTblRec.AppendEntity(acVport);
              acTrans.AddNewlyCreatedDBObject(acVport, true);

              // Change the view direction
              acVport.ViewDirection = acVec3dCol[nCnt];

              // Enable the viewport
              acVport.On = true;

              // Record the last viewport created
              acVportLast = acVport;

              // Increment the counter by 1
              nCnt = nCnt + 1;
          }
      }

      if (acVportLast != null)
      {
          // Activate model space in the viewport
          acDoc.Editor.SwitchToModelSpace();

          // Set the new viewport current via an imported ObjectARX function
          acedSetCurrentVPort(acVportLast.UnmanagedObject);
      }

      // Save the new objects to the database
      acTrans.Commit();
  }
}

Создание непрямоугольных ВЭ

В примере ниже создается прямоугольный ВЭ, а затем используется окружность для задания его границы

using System.Runtime.InteropServices;

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;

[DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl,
 EntryPoint = "?acedSetCurrentVPort@@YA?AW4ErrorStatus@Acad@@PBVAcDbViewport@@@Z")]
extern static private int acedSetCurrentVPort(IntPtr AcDbVport);

[CommandMethod("CreateNonRectangularFloatingViewport")]
public static void CreateNonRectangularFloatingViewport()
{
    // Get the current document and database, and start a transaction
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                     OpenMode.ForRead) as BlockTable;

        // Open the Block table record Paper space for write
        BlockTableRecord acBlkTblRec;
        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.PaperSpace],
                                        OpenMode.ForWrite) as BlockTableRecord;

        // Switch to the previous Paper space layout
        Application.SetSystemVariable("TILEMODE", 0);
        acDoc.Editor.SwitchToPaperSpace();

        // Create a Viewport
        using (Viewport acVport = new Viewport())
        {
            acVport.CenterPoint = new Point3d(9, 6.5, 0);
            acVport.Width = 2.5;
            acVport.Height = 2.5;

            // Set the scale to 1" = 8'
            acVport.CustomScale = 96;

            // Create a circle
            using (Circle acCirc = new Circle())
            {
                acCirc.Center = acVport.CenterPoint;
                acCirc.Radius = 1.25;

                // Add the new object to the block table record and the transaction
                acBlkTblRec.AppendEntity(acCirc);
                acTrans.AddNewlyCreatedDBObject(acCirc, true);

                // Clip the viewport using the circle  
                acVport.NonRectClipEntityId = acCirc.ObjectId;
                acVport.NonRectClipOn = true;
            }

            // Add the new object to the block table record and the transaction
            acBlkTblRec.AppendEntity(acVport);
            acTrans.AddNewlyCreatedDBObject(acVport, true);

            // Change the view direction
            acVport.ViewDirection = new Vector3d(0, 0, 1);

            // Enable the viewport
            acVport.On = true;

            // Activate model space in the viewport
            acDoc.Editor.SwitchToModelSpace();

            // Set the new viewport current via an imported ObjectARX function
            acedSetCurrentVPort(acVport.UnmanagedObject);
        }

        // Save the new objects to the database
        acTrans.Commit();
    }
}

Редактирование вида в ВЭ

Для изменения отображения вида у объекта Viewport вы можете задать обратиться к соответстующим свойствам:

  • ViewCenter — центр вида ВЭ;
  • ViewDirection — направление взгляда (вида) ВЭ;.
  • ViewHeight — задает высоту вида в пространстве модели в ВЭ в системе координат дисплея (DCS);
  • ViewTarget — задает фокусную точку, куда направлен взгляд в направлении ViewDirection;

Масштабирование вида

Масштабный коэффициент - это отношение размера выводимого на печать чертежа к фактическому размеру модели, отображаемой в ВЭ.

Для задания масштаба видового экрана используются свойства StandardScale и CustomScale. Свойство StandardScale принимает значение, основанное на перечислении StandardScaleType; тогда как свойство CustomScale принимает действительное число, представляющее отношение единиц между пространством модели и пространством листа. Например, для масштаба 1:4, оно составляет 0.25.

Масштаб типов линий в ВЭ

В пространстве листа задание масштаба типам линий может осуществляться двумя способами. Масштаб может быть основан на единицах измерения пространства, где был создан объект (в модели или на листе), а может быть и постоянным значением для данного листа. Можно использовать системную переменную PSLTSCALE, задающую масштаб типа линии в ВЭ на листе. Если она установлена в значение 1, то величина масштаба будет определяться настройками ВЭ индивидуально, если в значение 0, то масштабирование будет проводиться по тому пространству, где отображаемый объект расположен.

Стили печати

Стили печати используются для переопределения настроек отображения объекта в печатаемом виде. Стиль печати, назначенный листу, хранится в свойстве CurrentStyleSheet. Для назначения стиля печати объекту PlotSettings используется метод SetCurrentStyleSheet объекта PlotSettingsValidator. ​​Для определения типа стиля печати можно использовать свойство PlotStyleMode текущей базы данных. Список всех доступных стилей печати можно получить с помощью метода GetPlotStyleSheetList объекта PlotSettingsValidator.

Перечисленные стили печати совпадают с теми, которые отображаются в диалоговых окнах «Печать» или «Параметры листа».

В примере ниже выводятся все доступные стили печати в командную строку

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

// Lists the available plot styles
[CommandMethod("PlotStyleList")]
public static void PlotStyleList()
{
    // Get the current document and database, and start a transaction
    Document acDoc = Application.DocumentManager.MdiActiveDocument;

    acDoc.Editor.WriteMessage("\nPlot styles: ");

    foreach (string plotStyle in PlotSettingsValidator.Current.GetPlotStyleSheetList())
    {
        // Output the names of the available plot styles
        acDoc.Editor.WriteMessage("\n  " + plotStyle);
    }
}

Визуальные стили

Визуальные стили управляют отображением объектов в видовых экранах и при выводе их на печать. Визуальные стили хранятся в чертеже как часть словаря визуальных стилей (VisualStyleDictionaryId), можно создать новый визуальный стиль через класс DBVisualStyle и различные перечисления из пространства имён Autodesk.AutoCAD.GraphicsInterface.

Получение перечня визуальных стилей

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

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Colors;
using Autodesk.AutoCAD.GraphicsInterface;

// Lists the available visual styles
[CommandMethod("ListVisualStyle")]
public static void ListVisualStyle()
{
    // Get the current document and database, and start a transaction
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        DBDictionary vStyles = acTrans.GetObject(acCurDb.VisualStyleDictionaryId, 
                                                 OpenMode.ForRead) as DBDictionary;

        // Output a message to the Command Line history
        acDoc.Editor.WriteMessage("\nVisual styles: ");

        // Step through the dictionary
        foreach (DBDictionaryEntry entry in vStyles)
        {
            // Get the dictionary entry
            DBVisualStyle vStyle = vStyles.GetAt(entry.Key).GetObject(OpenMode.ForRead) as DBVisualStyle;

            // If the visual style is not marked for internal use then output its name
            if (vStyle.InternalUseOnly == false)
            {
                // Output the name of the visual style
                acDoc.Editor.WriteMessage("\n  " + vStyle.Name);
            }
        }
    }
}

Примечание: в nanoCAD NET API нельзя считать и отредактировать визуальные стили. Ошибка в API, известная.

Создание нового визуального стиля

В примере ниже создается новый визуальный стиль с именем MyVS.

Примечание: в nanoCAD NET API код ниже будет полностью несовместим, в Teigha.GraphicsInterface совсем другие названия соответствующих структур, классов, перечислений; но тем не менее соответствие есть, но не везде очевидное.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Colors;
using Autodesk.AutoCAD.GraphicsInterface;

// Creates a new visual style
[CommandMethod("CreateVisualStyle")]
public static void CreateVisualStyle()
{
    // Get the current document and database, and start a transaction
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        DBDictionary vStyles = acTrans.GetObject(acCurDb.VisualStyleDictionaryId,
                                                 OpenMode.ForRead) as DBDictionary;

        try
        {
            // Check to see if the "MyVS" exists or not
            DBVisualStyle vStyle = default(DBVisualStyle);
            if (vStyles.Contains("MyVS") == true)
            {
                vStyle = acTrans.GetObject(vStyles.GetAt("MyVS"), OpenMode.ForWrite) as DBVisualStyle;
            }
            else
            {
                acTrans.GetObject(acCurDb.VisualStyleDictionaryId, OpenMode.ForWrite);

                // Create the visual style
                vStyle = new DBVisualStyle();
                vStyles.SetAt("MyVS", vStyle);

                // Add the visual style to the dictionary
                acTrans.AddNewlyCreatedDBObject(vStyle, true);
            }

            // Set the description of the visual style
            vStyle.Description = "My Visual Style";
            vStyle.Type = VisualStyleType.Custom;

            // Face Settings (Opacity, Face Style, Lighting Quality, Color, 
            //                Monochrome color, Opacity, and Material Display)
            vStyle.SetTrait(VisualStyleProperty.FaceModifier, (int)VSFaceModifiers.FaceOpacityFlag, VisualStyleOperation.Set);
            vStyle.SetTrait(VisualStyleProperty.FaceLightingModel, (int)VSFaceLightingModel.Gooch, VisualStyleOperation.Set);
            vStyle.SetTrait(VisualStyleProperty.FaceLightingQuality, (int)VSFaceLightingQuality.PerPixelLighting, VisualStyleOperation.Set);
            vStyle.SetTrait(VisualStyleProperty.FaceColorMode, (int)VSFaceColorMode.ObjectColor, VisualStyleOperation.Set);
            vStyle.SetTrait(VisualStyleProperty.FaceMonoColor, Color.FromColorIndex(ColorMethod.ByAci, 1), VisualStyleOperation.Set);
            vStyle.SetTrait(VisualStyleProperty.FaceOpacity, 0.5, VisualStyleOperation.Set);
            vStyle.SetTrait(VisualStyleProperty.DisplayStyle, (int)VSDisplayStyles.MaterialsFlag + (int)VSDisplayStyles.TexturesFlag, VisualStyleOperation.Set);

            // Lighting (Enable Highlight Intensity, 
            //           Highlight Intensity, and Shadow Display)
            vStyle.SetTrait(VisualStyleProperty.FaceModifier, (int)vStyle.GetTrait(VisualStyleProperty.FaceModifier) + (int)VSFaceModifiers.SpecularFlag, VisualStyleOperation.Set);
            vStyle.SetTrait(VisualStyleProperty.DisplayStyle, (int)vStyle.GetTrait(VisualStyleProperty.DisplayStyle) + (int)VSDisplayStyles.LightingFlag, VisualStyleOperation.Set);
            vStyle.SetTrait(VisualStyleProperty.FaceSpecular, 45.0, VisualStyleOperation.Set);
            vStyle.SetTrait(VisualStyleProperty.DisplayShadowType, (int)VSDisplayShadowType.Full, VisualStyleOperation.Set);

            // Environment Settings (Backgrounds)
            vStyle.SetTrait(VisualStyleProperty.DisplayStyle, (int)vStyle.GetTrait(VisualStyleProperty.DisplayStyle) + (int)VSDisplayStyles.BackgroundsFlag, VisualStyleOperation.Set);

            // Edge Settings (Show, Number of Lines, Color, and Always on Top)
            vStyle.SetTrait(VisualStyleProperty.EdgeModel, (int)VSEdgeModel.Isolines, VisualStyleOperation.Set);
            vStyle.SetTrait(VisualStyleProperty.EdgeIsolines, 6, VisualStyleOperation.Set);
            vStyle.SetTrait(VisualStyleProperty.EdgeColor, Color.FromColorIndex(ColorMethod.ByAci, 2), VisualStyleOperation.Set);
            vStyle.SetTrait(VisualStyleProperty.EdgeModifier, (int)vStyle.GetTrait(VisualStyleProperty.EdgeModifier) + (int)VSEdgeModifiers.AlwaysOnTopFlag, VisualStyleOperation.Set);

            // Occluded Edges (Show, Color, and Linetype)
            if (!(((int)vStyle.GetTrait(VisualStyleProperty.EdgeStyle) + (int)VSEdgeStyles.ObscuredFlag) > 0))
            {
                vStyle.SetTrait(VisualStyleProperty.EdgeStyle, (int)vStyle.GetTrait(VisualStyleProperty.EdgeStyle) + (int)VSEdgeStyles.ObscuredFlag, VisualStyleOperation.Set);
            }
            vStyle.SetTrait(VisualStyleProperty.EdgeObscuredColor, Color.FromColorIndex(ColorMethod.ByAci, 3), VisualStyleOperation.Set);
            vStyle.SetTrait(VisualStyleProperty.EdgeObscuredLinePattern, (int)VSEdgeLinePattern.DoubleMediumDash, VisualStyleOperation.Set);

            // Intersection Edges (Color and Linetype)
            if (!(((int)vStyle.GetTrait(VisualStyleProperty.EdgeStyle) + (int)VSEdgeStyles.IntersectionFlag) > 0))
            {
                vStyle.SetTrait(VisualStyleProperty.EdgeStyle, (int)vStyle.GetTrait(VisualStyleProperty.EdgeStyle) + (int)VSEdgeStyles.IntersectionFlag, VisualStyleOperation.Set);
            }
            vStyle.SetTrait(VisualStyleProperty.EdgeIntersectionColor, Color.FromColorIndex(ColorMethod.ByAci, 4), VisualStyleOperation.Set);
            vStyle.SetTrait(VisualStyleProperty.EdgeIntersectionLinePattern, (int)VSEdgeLinePattern.ShortDash, VisualStyleOperation.Set);

            // Silhouette Edges (Color and Width)
            if (!(((int)vStyle.GetTrait(VisualStyleProperty.EdgeStyle) + (int)VSEdgeStyles.SilhouetteFlag) > 0))
            {
                vStyle.SetTrait(VisualStyleProperty.EdgeStyle, (int)vStyle.GetTrait(VisualStyleProperty.EdgeStyle) + (int)VSEdgeStyles.SilhouetteFlag, VisualStyleOperation.Set);
            }
            vStyle.SetTrait(VisualStyleProperty.EdgeSilhouetteColor, Color.FromColorIndex(ColorMethod.ByAci, 5), VisualStyleOperation.Set);
            vStyle.SetTrait(VisualStyleProperty.EdgeSilhouetteWidth, 2, VisualStyleOperation.Set);

            // Edge Modifiers (Enable Line Extensions, Enable Jitter, 
            //                 Line Extensions, Jitter, Crease Angle, 
            //                 and Halo Gap)
            if (!(((int)vStyle.GetTrait(VisualStyleProperty.EdgeModifier) + (int)VSEdgeModifiers.EdgeOverhangFlag) > 0))
            {
                vStyle.SetTrait(VisualStyleProperty.EdgeModifier, (int)vStyle.GetTrait(VisualStyleProperty.EdgeModifier) + (int)VSEdgeModifiers.EdgeOverhangFlag, VisualStyleOperation.Set);
            }
            if (!(((int)vStyle.GetTrait(VisualStyleProperty.EdgeModifier) + (int)VSEdgeModifiers.EdgeJitterFlag) > 0))
            {
                vStyle.SetTrait(VisualStyleProperty.EdgeModifier, (int)vStyle.GetTrait(VisualStyleProperty.EdgeModifier) + (int)VSEdgeModifiers.EdgeJitterFlag, VisualStyleOperation.Set);
            }
            vStyle.SetTrait(VisualStyleProperty.EdgeOverhang, 3, VisualStyleOperation.Set);
            vStyle.SetTrait(VisualStyleProperty.EdgeJitterAmount, (int)VSEdgeJitterAmount.JitterMedium, VisualStyleOperation.Set);
            vStyle.SetTrait(VisualStyleProperty.EdgeCreaseAngle, 0.3, VisualStyleOperation.Set);
            vStyle.SetTrait(VisualStyleProperty.EdgeHaloGap, 5, VisualStyleOperation.Set);
        }
        catch (Autodesk.AutoCAD.Runtime.Exception es)
        {
            System.Windows.Forms.MessageBox.Show(es.Message);
        }
        finally
        {
            acTrans.Commit();
        }
    }
}

Настройки печати

Объект PlotSettings похож на объект Layout, поскольку оба содержат идентичную информацию для печати, так как класс Layout является производным от класса PlotSettings. Основное различие заключается в том, что объект Layout имеет связанный с ним объект BlockTableRecord, содержащий перечень печатаемых объектов. ​​Объект PlotSettings не связан с конкретным объектом BlockTableRecord, а хранится в словаре PlotSettings в чертеже. Объекты PlotSettings известны как "параметры страницы" в пользовательском интерфейсе AutoCAD и доступны через Диспетчер параметров страниц. Параметры страницы могут быть применены к листу или использованы для переопределения настроек листа при печати или публикации.

Вывод настроек печати

В примере ниже содержится код, выводящий в командную строку названиях всех настроек печати

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.PlottingServices;

// Lists the available page setups
[CommandMethod("ListPageSetup")]
public static void ListPageSetup()
{
    // Get the current document and database
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    // Start a transaction
    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        DBDictionary plSettings = acTrans.GetObject(acCurDb.PlotSettingsDictionaryId,
                                                    OpenMode.ForRead) as DBDictionary;

        acDoc.Editor.WriteMessage("\nPage Setups: ");

        // List each named page setup
        foreach (DBDictionaryEntry item in plSettings)
        {
            acDoc.Editor.WriteMessage("\n  " + item.Key);
        }

        // Abort the changes to the database
        acTrans.Abort();
    }
}

Создание нового набора настроек печати

В примере ниже создается новый набор настрое печати

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.PlottingServices;

// Creates a new page setup or edits the page set if it exists
[CommandMethod("CreateOrEditPageSetup")]
public static void CreateOrEditPageSetup()
{
    // Get the current document and database, and start a transaction
    Document acDoc = Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {

        DBDictionary plSets = acTrans.GetObject(acCurDb.PlotSettingsDictionaryId,
                                                OpenMode.ForRead) as DBDictionary;
        DBDictionary vStyles = acTrans.GetObject(acCurDb.VisualStyleDictionaryId,
                                                 OpenMode.ForRead) as DBDictionary;

        PlotSettings acPlSet = default(PlotSettings);
        bool createNew = false;

        // Reference the Layout Manager
        LayoutManager acLayoutMgr = LayoutManager.Current;

        // Get the current layout and output its name in the Command Line window
        Layout acLayout = acTrans.GetObject(acLayoutMgr.GetLayoutId(acLayoutMgr.CurrentLayout),
                                            OpenMode.ForRead) as Layout;

        // Check to see if the page setup exists
        if (plSets.Contains("MyPageSetup") == false)
        {
            createNew = true;

            // Create a new PlotSettings object: 
            //    True - model space, False - named layout
            acPlSet = new PlotSettings(acLayout.ModelType);
            acPlSet.CopyFrom(acLayout);

            acPlSet.PlotSettingsName = "MyPageSetup";
            acPlSet.AddToPlotSettingsDictionary(acCurDb);
            acTrans.AddNewlyCreatedDBObject(acPlSet, true);
        }
        else
        {
            acPlSet = plSets.GetAt("MyPageSetup").GetObject(OpenMode.ForWrite) as PlotSettings;
        }

        // Update the PlotSettings object
        try
        {
            PlotSettingsValidator acPlSetVdr = PlotSettingsValidator.Current;

            // Set the Plotter and page size
            acPlSetVdr.SetPlotConfigurationName(acPlSet, "DWF6 ePlot.pc3", "ANSI_B_(17.00_x_11.00_Inches)");

            // Set to plot to the current display
            if (acLayout.ModelType == false)
            {
                acPlSetVdr.SetPlotType(acPlSet, Autodesk.AutoCAD.DatabaseServices.PlotType.Layout);
            }
            else
            {
                acPlSetVdr.SetPlotType(acPlSet, Autodesk.AutoCAD.DatabaseServices.PlotType.Extents);

                acPlSetVdr.SetPlotCentered(acPlSet, true);
            }

            // Use SetPlotWindowArea with PlotType.Window
            //acPlSetVdr.SetPlotWindowArea(plSet,
            //                             new Extents2d(New Point2d(0.0, 0.0),
            //                             new Point2d(9.0, 12.0)));

            // Use SetPlotViewName with PlotType.View
            //acPlSetVdr.SetPlotViewName(plSet, "MyView");

            // Set the plot offset
            acPlSetVdr.SetPlotOrigin(acPlSet, new Point2d(0, 0));

            // Set the plot scale
            acPlSetVdr.SetUseStandardScale(acPlSet, true);
            acPlSetVdr.SetStdScaleType(acPlSet, StdScaleType.ScaleToFit);
            acPlSetVdr.SetPlotPaperUnits(acPlSet, PlotPaperUnit.Inches);
            acPlSet.ScaleLineweights = true;

            // Specify if plot styles should be displayed on the layout
            acPlSet.ShowPlotStyles = true;

            // Rebuild plotter, plot style, and canonical media lists 
            // (must be called before setting the plot style)
            acPlSetVdr.RefreshLists(acPlSet);

            // Specify the shaded viewport options
            acPlSet.ShadePlot = PlotSettingsShadePlotType.AsDisplayed;

            acPlSet.ShadePlotResLevel = ShadePlotResLevel.Normal;

            // Specify the plot options
            acPlSet.PrintLineweights = true;
            acPlSet.PlotTransparency = false;
            acPlSet.PlotPlotStyles = true;
            acPlSet.DrawViewportsFirst = true;

            // Use only on named layouts - Hide paperspace objects option
            // plSet.PlotHidden = true;

            // Specify the plot orientation
            acPlSetVdr.SetPlotRotation(acPlSet, PlotRotation.Degrees000);

            // Set the plot style
            if (acCurDb.PlotStyleMode == true)
            {
                acPlSetVdr.SetCurrentStyleSheet(acPlSet, "acad.ctb");
            }
            else
            {
                acPlSetVdr.SetCurrentStyleSheet(acPlSet, "acad.stb");
            }

            // Zoom to show the whole paper
            acPlSetVdr.SetZoomToPaperOnUpdate(acPlSet, true);
        }
        catch (Autodesk.AutoCAD.Runtime.Exception es)
        {
            System.Windows.Forms.MessageBox.Show(es.Message);
        }

        // Save the changes made
        acTrans.Commit();

        if (createNew == true)
        {
            acPlSet.Dispose();
        }
    }
}

Задание настроек листу

В примере ниже созданный набор настроек печати "MyPageSetup" задается активному листу.


Работа с событиями

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

Виды событий в AutoCAD

Некоторые из основных событий, представленные в AutoCAD, для соответствующих классов и процессов:

  • Application : закрытие, открытие приложения, изменение системных переменных, переход и закрытие окна модели;
  • Database : сохранение чертежа, добавление\удаление объектов, вставка блоков, подключение внешних ссылок;
  • Document : закрытие документа, смена переменных;
  • DocumentCollection : создание и закрытие документа, становление документа активным, блокировка документа;
  • Editor : различный пользовательский ввод;
  • Graphics : создание и редактирование видов;
  • Plotting : вывод листа на печать;
  • Publishing : публикация листа;
  • Runtime : загрузка и выгрузка модулей, смена переменных;
  • Windows : изменение положения иконок, панелей.

Функции, реагирующие на события, называются обработчиками событий и выполняются автоматически каждый раз, когда срабатывает соответствующее событие. Информация, содержащаяся в аргументах, возвращаемых событием, передается от обработчика событий к объекту, например имя системной переменной в событии SystemVariableChanging в виде SystemVariableChangingEventArgs.

Рекомендации к работе с событиями

Важно иметь в виду, что события только предоставляют информацию о действиях, происходящих в AutoCAD. Во время срабатывания некоторых событий AutoCAD может находиться в процессе выполнения операции, поэтому обработчики событий имеют некоторые ограничения, чтобы обеспечить стабильную работу с приложением и его данными в чертеже. Ниже перечислены эти ограничения и особенности:

  • Последовательность событий не всегда верная : при создании обработчиков событий не полагайтесь на то, что последовательность событий будет происходить именно в том порядке, в котором вы ожидаете. Например, если вы отдадите команду OPEN, будут запущены события CommandWillStart, DocumentCreateStarted, DocumentCreated и CommandEnded. Однако они могут происходить каждый раз в разном порядке. Единственное, на что вы можете полагаться, это на то, что большинство событий происходят парами: начальное и конечное событие;
  • Последовательность операция не всегда верная : Если вы удалите объект 1, а затем объект 2, не полагайтесь на то, что вы получите событие ObjectErased для объекта 1, а затем для объекта 2. Вы можете получить событие ObjectErased для объекта 2 первым;
  • Нельзя выполнять интерактивные функции из обработчика событий : Попытки выполнения интерактивных функций из обработчика событий могут привести к серьезным проблемам, поскольку AutoCAD может все еще обрабатывает команду в момент срабатывания события. Поэтому всегда следует избегать запросов ввода в командной строке, а также запросов выбора объектов и использования метода SendStringToExecute из обработчиков событий;
  • Нельзя запускать диалоговые окна из обработчика событий. Диалоговые окна считаются относятся к интерактивным функциями и могут помешать текущей работе AutoCAD. Сообщения (MessageBox) и предупреждающие окна (Application.ShowAlertDialog) не считаются интерактивными и могут использоваться безопасно; однако вывод сообщения из некоторых обработчиков событий, таких как EnterModal, LeaveModal, DocumentActivated и DocumentToBeDeactivated, может привести к неожиданным последствиям;
  • Вы можете редактировать любой объект в БД чертежа, но следует избегать изменения объекта, вызвавшего событие. Очевидно, что любой объект, вызвавший событие, может быть открыт, и операция редактирования может продолжаться. Поэтому избегайте изменение объекта из обработчика событий для того же объекта, безопасно можно только считывать информацию;
  • Не выполняйте никаких действий из обработчика событий, которые могут вызвать то же событие. Если вы выполните то же действие в обработчике события, который вызывает то же событие, вы создадите бесконечный цикл. Например, никогда не пытайтесь открыть объект из события ObjectOpenedForModify, иначе AutoCAD просто продолжит открывать объекты и выбросит исключение или вылетит;
  • Во время отображения модальных диалоговых окон в AutoCAD события не обрабатываются и не происходят;

Подписка на событие и прекращение

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

Подписка на событие

Подписка на событие осуществляется добавлением нового обработчика для события. Обработчик требует некоторой процедуры, которая определена где-либо в вашем проекте (как правило, это некоторый метод класса). Большинство типов обработчиков в основном требуют 2 аргумента -- первый типа object, а второй -- возвращаемые аргументы для данного события. Регистрация события осуществляется с помощью оператора "+=" в C#

В приведенном ниже коде регистрируется процедура с именем appSysVarChanged для события SystemVariableChanged. Процедура appSysVarChanged принимает два параметра: Object и SystemVariableChangedEventArgs. Объект SystemVariableChangedEventArgs возвращает имя системной переменной, измененной при регистрации события.

Отмена подписки на событие

Для отмены события необходимо удалить привязанный к нему обработчик, используется тот же синтаксис, что и для добавления события, за исключением того, что оператор для удаления для C# это -=. Пример ниже содержит загружаемый в приложение класс, в теле метода Initialize происходит подписка на событие, а в теле Terminate -- отмена подписки.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;

public class Loader : IExtensionApplication
{
    public void Initialize()
    {
        Application.SystemVariableChanged +=
            new SystemVariableChangedEventHandler(appSysVarChanged);
    }
    public void Terminate()
    {
        Application.SystemVariableChanged -=
            new SystemVariableChangedEventHandler(appSysVarChanged);
    }
    private void appSysVarChanged(object sender, SystemVariableChangedEventArgs e)
    {
        //...
    }
}

События приложения AutoCAD

События приложения AutoCAD (вернее, для статического класса Application) позволяют отлавливать действия с окном самого AutoCAD и действиями на уровне всего приложения:

  • BeginDoubleClick : при двукратном нажатии на ЛКМ;
  • BeginQuit : перед закрытием AutoCAD;
  • Idle : срабатывает при загрузке AutoCAD (появление меню);
  • QuitAborted : при попытке прервать завершение работы AutoCAD;
  • QuitWillStart : после срабатывания события BeginQuit и перед завершением работы AutoCAD;
  • SystemVariableChanged : после изменения системной переменной;
  • SystemVariableChanging : перед изменением системной переменной;

Примечание: в nanoCAD .NET API не реализованы события BeginCustomizationMode, DisplayingCustomizeDialog, DisplayingDraftingSettingsDialog, DisplayingOptionDialog, EndCustomizationMode, EnterModal, LeaveModal, PreTranslateMessage

В примере ниже приведен код, осуществляющий подписку на событие SystemVariableChanged. После того, как системная переменная была изменена, появится модальное диалоговое окно с сообщением, какое новое значение у измененной переменной. После запуска метода AddAppEvent для запуска связанной с обработчиком события процедуры (вывода модального окна с новым значением переменной) измените вручную в AutoCAD значение любой системной переменной

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;

[CommandMethod("AddAppEvent")]
public void AddAppEvent()
{
    Application.SystemVariableChanged +=
        new SystemVariableChangedEventHandler(appSysVarChanged);
}
[CommandMethod("RemoveAppEvent")]
public void RemoveAppEvent()
{
    Application.SystemVariableChanged :=
        new SystemVariableChangedEventHandler(appSysVarChanged);
}
public void appSysVarChanged(object senderObj,
                             Autodesk.AutoCAD.ApplicationServices.
                             SystemVariableChangedEventArgs sysVarChEvtArgs)
{
    object oVal = Application.GetSystemVariable(sysVarChEvtArgs.Name);
    // Display a message box with the system variable name and the new value
    Application.ShowAlertDialog(sysVarChEvtArgs.Name + " was changed." +
                                "\\nNew value: " + oVal.ToString());
}

События коллекции документов

Объект DocumentCollection, возвращаемый через свойство DocumentManager статического класса Application предоставляет ряд событий по контролю за состоянием документа среди группы других документов в данной сессии приложения:

  • DocumentActivated : Событие, возникающее при переходе к данному dwg:чертежу. Соответствует функции ObjectARX AcApDocManagerReactor.documentActivated();
  • DocumentActivationChanged : Событие, возникающее после событий DocumentActivated или DocumentDestroyed;
  • DocumentBecameCurrent : Событие, возникающее при переключении между открытыми dwg:документами. Событие возникает всегда, когда текущим становится другой документ;
  • DocumentCreated : Событие, возникающее после создания нового объекта типа Document. Соответствует функции ObjectARX API AcApDocManagerReactor.documentCreated();
  • DocumentCreateStarted : Событие, возникающее перед созданием документа, когда база данных еще не доступна. Соответствует функции ObjectARX API AcApDocManagerReactor.documentCreateStarted();
  • DocumentCreationCanceled : Событие, возникающее после отмены создания dwg-документа. Событие возникает, когда пользователь отменил создание dwg:документа. Данное событие может возникнуть только в режиме MDI и после события DocumentCreateStarted. Соответствует функции ObjectARX API AcApDocManagerReactor.documentCreationCanceled();
  • DocumentDestroyed : Событие, возникающее, когда документ полностью закрыт и база данных, соответствующая этому документу,также уничтожена в памяти. Соответствует функции ObjectARX API AcApDocManagerReactor.documentDestroyed();
  • DocumentLockModeChanged : Событие, возникающее при каждой блокировке/разблокировке dwg:документа. Вызов блокировки или разблокировки можно отличить. При разблокировке документа имя команды начинается с символа '#'. Блокировка документа может быть запрещена, а разблокировка — нет. Соответствует функции ObjectARX API AcApDocManagerReactor.documentLockModeChanged();
  • DocumentLockModeChangeVetoed : Событие, возникающее при запрете блокировки dwg:документа. Попытка блокировки документа может быть запрещена реактором, получившим событие DocumentLockModeChanged. Если запрет произошел, все реакторы получат данное событие и все реакторы смогут получить информацию о запрете блокировки, даже если не все реакторы получили событие DocumentLockModeChanged до того, как будет запрещена блокировка. Соответствует функции ObjectARX API AcApDocManagerReactor.documentLockModeChangeVetoed();
  • DocumentLockModeWillChange : Событие, возникающее перед изменением режима блокировки dwg:документа. Событие, возникающее перед тем, как будет изменен режим блокировки dwg-документа. Оно не может быть запрещено. Соответствует функции ObjectARX API AcApDocManagerReactor.documentLockModeWillChange();
  • DocumentToBeActivated : Событие, возникающее перед активацией документа. Событие, генерируемое перед активацией документа. Соответствует функции ObjectARX API AcApDocManagerReactor.documentToBeActivated();
  • DocumentToBeDeactivated : Событие, возникающее перед тем, как фокус переходит на иной dwg-документ . Соответствует функции ObjectARX API AcApDocManagerReactor.documentToBeDeactivated();
  • DocumentToBeDestroyed : Событие, возникающее перед уничтожением документа. Событие, возникающее перед уничтожением документа. Соответствует функции ObjectARX API AcApDocManagerReactor.documentToBeDestroyed();

Примечание: в nanoCAD .NET API не реализовано событие DocumentToBeDeactivated

В примере ниже содержится пример подписки на событие DocumentActivated. Для запуска связанной с обработчиком события процедуры (вывода модального окна) создайте или переключитесь на новый чертеж в AutoCAD

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;

[CommandMethod("AddDocColEvent")]
public void AddDocColEvent()
{
  Application.DocumentManager.DocumentActivated +=
      new DocumentCollectionEventHandler(docColDocAct);
}
[CommandMethod("RemoveDocColEvent")]
public void RemoveDocColEvent()
{
  Application.DocumentManager.DocumentActivated :=
      new DocumentCollectionEventHandler(docColDocAct);
}
public void docColDocAct(object senderObj,
                         DocumentCollectionEventArgs docColDocActEvtArgs)
{
  Application.ShowAlertDialog(docColDocActEvtArgs.Document.Name +
                              " was activated.");
}

События документа Document

События для окна чертежа (класса Document) позволяют отлавливать действия, совершаемые на уровне документа AutoCAD:

  • BeginDocumentClose : Событие, генерируемое перед закрытием документа. Событие, генерируемое перед закрытием документа. Позволяет разработчику вызвать метод DocumentBeginCloseEventArgs.Veto() при обработке этого события, чтобы остановить закрытие документа. Соответствует функции ObjectARX API AcEditorReactor.docCloseAborted();
  • CloseAborted : Событие, возникающее после того, как в обработчике отменяется закрытие документа. Это событие попадет во все реакторы, которые получают событие beginDocClose. Соответствует функции ObjectARX API AcEditorReactor.docCloseAborted();
  • CloseWillStart : Событие, возникающее после BeginDocumentClose и перед закрытием чертежа. Соответствует функции ObjectARX API AcEditorReactor.docCloseWillStart();
  • CommandCancelled : Событие, означающее, что команда cmdStr была отменена пользователем или другим приложением, и не смогла быть успешно завершена. Соответствует функции ObjectARX API AcEditorReactor.commandcanceled();
  • CommandFailed : Событие, означающее что команда cmdStr аварийно завершена. Соответствует функции ObjectARX API AcEditorReactor.commandFailed();
  • CommandWillStart : Событие, возникающее перед началом выполнения команды cmdStr. Соответствует функции ObjectARX API AcEditorReactor.commandWillStart();
  • ImpliedSelectionChanged : Событие, возникающее при изменении набора предварительно выбранных объектов в документе (pickfirst). Оно возникает для всех действий, добавляющих или удаляющих объекты из этого набора. Действия, меняющие геометрию или свойства объектов из этого набора (сдвиг, удлинение и т.д.) не вызывают этого события. Соответствует функции ObjectARX API AcEditorReactor.pickfirstModified();
  • LispCancelled : Событие, возникающее при прерывании выполнения lisp выражения в документе. Соответствует функции ObjectARX API AcEditorReactor.lispCancelled();
  • LispEnded : Событие, возникающее при прерывании выполнения lisp выражения в документе. Соответствует функции ObjectARX API AcEditorReactor.lispEnded();
  • LispWillStart : Событие, возникающее перед началом выполнения lisp выражения в документе. Соответствует функции ObjectARX API AcEditorReactor.lispWillStart();
  • UnknownCommand : Событие, возникающее при вызове команды, которую AutoCAD не распознает. Соответствует функции ObjectARX API AcEditorReactor.unknownCommand();
  • ViewChanged : Событие возникает, когда параметры текущего вида были изменены;

Примечание: в nanoCAD .NET API не реализованы события BeginDwgOpen, CommandEnded, EndDwgOpen, LayoutSwitched.

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

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;

[CommandMethod("AddDocEvent")]
public void AddDocEvent()
{
  // Get the current document
  Document acDoc = Application.DocumentManager.MdiActiveDocument;
  acDoc.BeginDocumentClose +=
      new DocumentBeginCloseEventHandler(docBeginDocClose);
}
[CommandMethod("RemoveDocEvent")]
public void RemoveDocEvent()
{
  // Get the current document
  Document acDoc = Application.DocumentManager.MdiActiveDocument;
  acDoc.BeginDocumentClose :=
      new DocumentBeginCloseEventHandler(docBeginDocClose);
}
public void docBeginDocClose(object senderObj,
                             DocumentBeginCloseEventArgs docBegClsEvtArgs)
{
  // Display a message box prompting to continue closing the document
  if (System.Windows.Forms.MessageBox.Show(
                       "The document is about to be closed." +
                       "\nDo you want to continue?",
                       "Close Document",
                       System.Windows.Forms.MessageBoxButtons.YesNo) ==
                       System.Windows.Forms.DialogResult.No)
  {
      docBegClsEvtArgs.Veto();
  }
}

События для объекта чертежа

События для объектов доступны, в основном, для класса DBObject, часть для Database. Они позволяют реагировать на действия с объектами -- добавление, редактирование, удаление. События, применимые к объектам, можно разбить на 2 большие группы - регистрируемые для конкретного объекта и на уровне всей базы данных чертежа сразу для любого объекта.

  • Cancelled : если открытие объекта на чтение или запись было прервано;
  • Copied : после того, как была создана копия объекта;
  • Erased : когда объект стал помеченным к удалению;
  • Goodbye : когда объект удаляется из памяти, поскольку связанная с ним база данных чертежа уничтожена;
  • Modified : при редактировании объекта;
  • ModifiedXData : когда изменяется подключенная к объекту XData;
  • ModifyUndone : cрабатывает при отмене предыдущих изменений, внесенных в объект;
  • ObjectClosed : при закрытии объекта;
  • OpenedForModify : при открытии на изменение (OpenMode.ForWrite);
  • Reappended : когда объект удаляется из базы данных после операции Undo и добавляется в БД через операцию Redo;
  • SubObjectModified : при изменении составляющей части объекта;

События для Database:

  • ObjectAppended : когда объект добавляется в БД чертежа;
  • ObjectErased : при удалении объекта;
  • ObjectModified : при изменении объекта;
  • ObjectOpenedForModify : при открытии на изменение (OpenMode.ForWrite);
  • ObjectReappended : когда объект удаляется из базы данных после операции Undo и добавляется в БД через операцию Redo;
  • ObjectUnappended : когда объект удаляется из базы данных после операции Undo;

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

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
 
// Global variable for polyline object
Polyline acPoly = null;
 
[CommandMethod("AddPlObjEvent")]
public void AddPlObjEvent()
{
  // Get the current document and database, and start a transaction
  Document acDoc = Application.DocumentManager.MdiActiveDocument;
  Database acCurDb = acDoc.Database;
 
  using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
  {
      // Open the Block table record for read
      BlockTable acBlkTbl;
      acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
                                   OpenMode.ForRead) as BlockTable;
 
      // Open the Block table record Model space for write
      BlockTableRecord acBlkTblRec;
      acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
                                      OpenMode.ForWrite) as BlockTableRecord;
 
      // Create a closed polyline
      acPoly = new Polyline();
      acPoly.AddVertexAt(0, new Point2d(1, 1), 0, 0, 0);
      acPoly.AddVertexAt(1, new Point2d(1, 2), 0, 0, 0);
      acPoly.AddVertexAt(2, new Point2d(2, 2), 0, 0, 0);
      acPoly.AddVertexAt(3, new Point2d(3, 3), 0, 0, 0);
      acPoly.AddVertexAt(4, new Point2d(3, 2), 0, 0, 0);
      acPoly.Closed = true;
 
      // Add the new object to the block table record and the transaction
      acBlkTblRec.AppendEntity(acPoly);
      acTrans.AddNewlyCreatedDBObject(acPoly, true);
 
      acPoly.Modified += new EventHandler(acPolyMod);
 
      // Save the new object to the database
      acTrans.Commit();
  }
}
 
[CommandMethod("RemovePlObjEvent")]
public void RemovePlObjEvent()
{
  if (acPoly != null)
  {
      // Get the current document and database, and start a transaction
      Document acDoc = Application.DocumentManager.MdiActiveDocument;
      Database acCurDb = acDoc.Database;
 
      using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
      {
          // Open the polyline for read
          acPoly = acTrans.GetObject(acPoly.ObjectId,
                                     OpenMode.ForRead) as Polyline;
 
          if (acPoly.IsWriteEnabled == false)
          {
              acTrans.GetObject(acPoly.ObjectId, OpenMode.ForWrite);
          }
 
          acPoly.Modified -= new EventHandler(acPolyMod);
          acPoly = null;
      }
  }
}
 
public void acPolyMod(object senderObj,
                      EventArgs evtArgs)
{
  Application.ShowAlertDialog("The area of " +
                              acPoly.ToString() + " is: " +
                              acPoly.Area);
}

Подписка на события из ActiveX (COM)

AutoCAD ActiveX (COM) API содержит некоторую функциональность, отсутствующую в настоящем .NET API, там также есть возможность подписки на события, но этот процесс будет выглядеть немного по-другому, подписываться будет необходимо на соответствующие события у интерфейсов. В примере ниже приводится процесс подписки на событие NewDrawing, срабатывающее после создания нового документа в AutoCAD.

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Interop;

AcadApplication acAppCom;
[CommandMethod("AddCOMEvent")]
public void AddCOMEvent()
{
    // Set the global variable to hold a reference to the application and
    // register the BeginFileDrop COM event
    acAppCom = Application.AcadApplication as AcadApplication;
    acAppCom.NewDrawing +=
        new _DAcadApplicationEvents_NewDrawingEventHandler(appNewDrawing);
}
[CommandMethod("RemoveCOMEvent")]
public void RemoveCOMEvent()
{
    // Unregister the COM event handle
    acAppCom.NewDrawing -=
        new _DAcadApplicationEvents_NewDrawingEventHandler(appNewDrawing);
    acAppCom = null;
}
public void appNewDrawing()
{
    Application.ShowAlertDialog("Drawing " + Application.DocumentManager.MdiActiveDocument.Name + " is now active!");
}

Разработка приложений на C#

Многие задачи в AutoCAD могут успешно закрываться за счёт использования .NET API, оно более функционально чем Lisp или ActiveX и намного более проще по сравнению с ObjectARX. Данный раздел содержит рекомендации к обходу ошибок при разработке, порядке распространения созданных утилит.

Об обработке ошибок

В большинстве сред разработки (IDE) предусмотрена обработка ошибок. В C# и VB.NET стандартная реакция на ошибку — отображение сообщения об ошибке и завершение работы приложения. Хотя такое поведение и допустимо при отладке приложения, оно не приемлемо для пользователя.

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

При перехвате ошибки на стороне кода (например, при использовании стандартной конструкции try\catch) для управляемых процедур (Managed API) функция сможет продолжить работу, а не вылетит с "Фатальной ошибкой".

Обработка ошибок особенно важна при обработке пользовательского ввода (не важно, осуществятся ли ввод через некоторый текстовый файл, оконную форму или через командную строку AutoCAD), важно также помнить и о настройках системной локализации.

Типы ошибок

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

  • Ошибки компиляции возникают во время сборки приложения. В основном это синтаксические ошибки, а также проблемы с областью видимости переменных и типами данных. В C# и VB.NET эти типы ошибок обнаруживаются средой разработки. При вводе неправильной строки кода она подчёркивается, информация о проблеме отображается во всплывающей подсказке при наведении курсора на подчёркнутый текст. Ошибки компиляции необходимо исправлять до сборки .NET-приложения;
  • Ошибки выполнения найти и исправить немного сложнее. Они возникают во время выполнения кода и часто связаны с информацией, введенной пользователем, или с файлами во время их чтения. Например, если приложение требует от пользователя ввести имя чертежа, а пользователь вводит имя для несуществующего чертежа, возникает ошибка выполнения, если не проверить имя на корректность. В этих случаях необходимо ставить себя на место пользователя и во всех процедурах, ожидающих какие:либо данные предусматривать функции их проверки на корректность;
  • Логические ошибки сложнее всего обнаружить и исправить. Программа может успешно собираться и выполняться без ошибок, но результат не будет соответствовать ожиданию. Иногда для их обнаружения можно внимательно посмотреть на код и найти опечатку\потерянный оператор и т.д., а иногда дефект может быть и в самой функции в API приложения;

В настоящей документации в основном упоминаются только возможные ошибки во время выполнения процедур в AutoCAD (runtime).

Перехват runtime-ошибок

На языке программирования C# вы можете использовать специальные операторы try\catch\finally. В примере ниже показана пример использования подобных конструкций, хотя в данном контексте более корректной была бы проверка, существует ли файл по указанному пути с помощью системного метода System.IO.File.Exists().

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

[CommandMethod("NoErrorHandler")]
public void NoErrorHandler()
{
    // Create a new database with no document window
    using (Database acDb = new Database(false, true))
    {
        // Read the drawing file named "Drawing123.dwg" on the C: drive.
        // If the "Drawing123.dwg" file does not exist, an eFileNotFound
        // exception is tossed and the program halts.
        acDb.ReadDwgFile("c:\\Drawing123.dwg",
                         System.IO.FileShare.None, false, "");
    }
    // Message will not be displayed since the exception caused by
    // ReadDwgFile is not handled.
    Application.ShowAlertDialog("End of command reached");
}
[CommandMethod("ErrorTryCatchFinally")]
public void ErrorTryCatchFinally()
{
    // Create a new database with no document window
    using (Database acDb = new Database(false, true))
    {
        try
        {
            // Read the drawing file named "Drawing123.dwg" on the C: drive.
            // If the "Drawing123.dwg" file does not exist, an eFileNotFound
            // exception is tossed and the catch statement handles the error.
            acDb.ReadDwgFile("c:\\Drawing123.dwg",
                             System.IO.FileShare.None, false, "");
        }
        catch (Autodesk.AutoCAD.Runtime.Exception Ex)
        {
            Application.ShowAlertDialog("The following exception was caught:\\n" +
                                        Ex.Message);
        }
        finally
        {
            // Message is displayed since the exception caused
            // by ReadDwgFile is handled.
            Application.ShowAlertDialog("End of command reached");
        }
    }
}

Использование объекта Exception

Перехватываемое в теле блока catch исключение описывается классом Autodesk.AutoCAD.Runtime.Exception, который является дочерним классом от System.Exception. Свойство ErrorStatus данного исключения вернет один из кодов ошибки, специфичной для AutoCAD, описываемых одноименным перечислением из того же пространства имен Autodesk.AutoCAD.Runtime.

Операции пользовательского ввода

Методы ввода данных со стороны пользователя из класса Editor имеют встроенную защиту от ошибок неверного ввода данных, если пользователь будет вводить некорректные данные, то выбор сбрасывается и запрашивается снова. С помощью специальных операций PromptXXXOption (PromptIntegerOptions, PromptStringOptions и пр.) с соответствующими им методами GetXXX или SelectXXX, принимающими данную конфигурацию PromptXXXOption, можно задавать дополнительные условия для пользовательского ввода, чтобы проверять вводимые значения.

Распространение приложения

.NET приложения могут распространяться в двух режимах

  • С отладочными символами (Debug), имеют больший размер библиотек;
  • Релизные версии (Release), не содержат отладочной информации, для распространения стабильного кода.

Как правило, при разработке используются Debug:версии, а пользователям отдаются Release:версии.