Создание пользовательских примитивов в САПР на MultiCAD .NET API

от автора

Одним из главных недостатков традиционного .NET API в .dwg совместимых САПР является невозможность создания пользовательских примитивов (Custom Entities) на .NET. Пользовательские примитивы создаются на С++, для их использования в .NET необходимо создать управляемые обёртки на C++/CLI.

Технология MultiCAD .NET позволяет создавать пользовательские примитивы, не выходя за рамки управляемого кода. Помимо отсутствия промежуточных объектов на C++, в MultiCAD .NET максимально используются стандартные для .NET механизмы, как следствие нет необходимости во многих привычных для САПР программистов операциях: не нужно вручную описывать сериализацию, свойства в инспектор можно вывести без создания COM объекта и т.п.

В качестве демонстрации MultiCAD .NET мы рассмотрим пример приложения CustomObjects, содержащийся в комплекте поставки SDK. Этот пример создает пользовательский примитив, который представляет собой прямоугольную рамку с находящимся внутри текстом:

Sample TextInBox MultiCAD .NET Entity

Чертежи, содержащие наш тестовый примитив, могут быть открыты в любой .dwg совместимой САПР. Для изменения примитива необходимо загрузить сборку, содержащую код примитива, причём во все поддерживаемые САПР платформы загружается одна и та же сборка без перекомпиляции. Технология является родной для nanoCAD, для загрузки модуля в AutoCAD требуется модуль расширения (Object Enabler). Как это работает смотрите под катом.

Класс пользовательского примитива

Для создания нового типа примитива необходимо написать класс, наследованный от McCustomBase — базового класса для всех пользовательских примитивов. Кроме этого, для объявленного класса необходимо использовать два атрибута:

  1. атрибут [CustomEntity] с указанием типа класса, его GUID, имени, которое будет использоваться для всех таких объектов в базе данных чертежа и локального имени,
  2. атрибут [Serializable], для того, чтобы воспользоваться стандартным механизмом сериализации в .NET Framework.

[CustomEntity(typeof(TextInBox), "1C925FA1-842B-49CD-924F-4ABF9717DB62", "TextInBox", "TextInBox Sample Entity")] [Serializable] public class TextInBox : McCustomBase {   // First and second vertices of the box   private Point3d _pnt1 = new Point3d(50, 50, 0);   private Point3d _pnt2 = new Point3d(150, 100, 0);    // Text inside the box   private String _text = "Text field"; } 

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

Отображение геометрии

Для отображения объекта используется метод OnDraw(). В качестве параметра этого метода выступает объект класса GeometryBuilder, который, собственно, и будет использоваться для отрисовки пользовательского примитива.

public override void OnDraw(GeometryBuilder dc) {   dc.Clear();    // Set the color to ByObject value   dc.Color = McDbEntity.ByObject;    // Draw box with choosen coordinates   dc.DrawPolyline(new Point3d[] { _pnt1,                                    new Point3d(_pnt1.X, _pnt2.Y, 0),                                    _pnt2,                                    new Point3d(_pnt2.X, _pnt1.Y, 0),                                    _pnt1});    // Set text height   dc.TextHeight = 2.5 * DbEntity.Scale;	    // Set text color   dc.Color = Color.Blue;    // Draw text at the box center   dc.DrawMText(new Point3d((_pnt2.X + _pnt1.X) / 2.0, (_pnt2.Y + _pnt1.Y) / 2.0, 0),                 Vector3d.XAxis,                 Text,                 HorizTextAlign.Center,                 VertTextAlign.Center); } 
Добавление объекта в чертеж, интерактивный ввод координат

Для добавления пользовательского объекта в чертеж используется метод PlaceObject(), который в нашем случае, кроме собственно операции добавления объекта в базу, будет использоваться и для интерактивного ввода координат объекта. За интерактивный ввод в MultiCAD .NET отвечает класс InputJig, содержащий необходимую нам функциональность:

  • public InputResult GetPoint(string promt) — получает точку на чертеже, выбранную пользователем, с возможностью вывода подсказки;
  • public void ExcludeObject(McObjectId ObjectId) — исключает указанный объект из привязки при указании точки на чертеже. В нашем случае мы исключим наш объект из привязки, чтобы избежать привязки к самому себе.
  • public EventHandler MouseMove — обработчик события движения мышкой. Будем его использовать для интерактивной перерисовки объекта при передвижении мыши.

Реализация метода PlaceObject() будет выглядеть следующим образом:

public override hresult PlaceObject(PlaceFlags lInsertType) {   InputJig jig = new InputJig();    // Get the first box point from the jig   InputResult res = jig.GetPoint("Select first point:");   if (res.Result != InputResult.ResultCode.Normal)     return hresult.e_Fail;   _pnt1 = res.Point;    // Add the object to the database   DbEntity.AddToCurrentDocument();    // Exclude the object from snap points   jig.ExcludeObject(ID);    // Monitoring mouse moving and interactive entity redrawing    jig.MouseMove = (s, a) => {TryModify(); _pnt2 = a.Point; DbEntity.Update(); };    // Get the second box point from the jig   res = jig.GetPoint("Select second point:");   if (res.Result != InputResult.ResultCode.Normal)   {     DbEntity.Erase();     return hresult.e_Fail;   }   _pnt2 = res.Point;      return hresult.s_Ok; } 
Редактирование и трансформация объекта

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

  • public virtual List OnGetGripPoints() — получает список ручек для объекта;
  • public virtual void OnMoveGripPoints(List indexes, Vector3d offset, bool isStretch) — обработчик перемещения ручек;
  • public virtual void OnTransform(Matrix3d tfm) — определяет как должен трансформироваться объект;
  • public virtual hresult OnEdit(Point3d pnt, EditFlags lInsertType) — определяет процедуру редактирования объекта;

Ручки представляют собой специальные точки, отмеченные маркером, которые используются для трансформации объекта. Выведем ручки в угловых точках рамки, заданных пользователем:

public override List OnGetGripPoints() {   List arr = new List();   arr.Add(_pnt1);   arr.Add(_pnt2);   return arr; } 

Теперь, после выбора объекта на чертеже, в заданных точках будут отображены ручки:

image

Добавим возможность перемещения определяющих угловых точек с помощью перетаскивания ручек путем определения метода-обработчика OnMoveGripPoints():

public override void OnMoveGripPoints(List indexes, Vector3d offset, bool isStretch) {   if (!TryModify())      return;        if (indexes.Count == 2)   {     _pnt1 += offset;     _pnt2 += offset;   }   else if (indexes.Count == 1)   {     if (indexes[0] == 0)       _pnt1 += offset;     else       _pnt2 += offset;   } } 

Параметр indexes здесь содержит список номеров ручек, offset — вектор перемещения ручек.

Затем определим метод OnTransform() таким образом, чтобы при трансформации объекта рассчитывались новые координаты для обеих определяющих угловых точек:

public override void OnTransform(Matrix3d tfm) {   //Save Undo state and set the object status to "Changed"   if (!TryModify())      return;      _pnt1 = _pnt1.TransformBy(tfm);   _pnt2 = _pnt2.TransformBy(tfm); } 

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

public override hresult OnEdit(Point3d pnt, EditFlags lInsertType) {   TextInBox_Form frm = new TextInBox_Form();   frm.textBox1.Text = Text;   frm.ShowDialog();   Text = frm.textBox1.Text;   return hresult.s_Ok; } 

image

Добавление свойств объекта в инспектр свойств

MultiCAD .NET API предоставляет возможность добавления свойств пользовательского объекта в инспектор свойств объекта, независимо от платформы, где будет открыт .dwg файл, будь то AutoCAD или nanoCAD. Это делается путем добавления для соответствующего общедоступного свойства объекта следующих атрибутов:

  • DisplayNameAttribute — определяет имя свойства, которое будет отображаться в инспекторе;
  • DescriptionAttribute — задает описание свойства;
  • CategoryAttribute — определяет имя категории, в которой будет отображаться данное свойство.

Воспользуемся этой возможностью и добавим свойство Text в палитру свойств объекта:

[DisplayName("Текстовая метка")] [Description("Описание метки")] [Category("Текстовый объект")] public String Text {   get   {     return _text;   }   set   {     //Save Undo state and set the object status to "Changed"     if (!TryModify())        return;          // Set new text value     _text = value;   } } 

После этого, значение текстовой строки нашего объекта будет отображаться в инспекторе объектов:

image

Итак, мы создали первую версию примитива, который можно вставить в чертёж формата .dwg и отредактировать несколькими привычными пользователям САПР способами. Но жизнь на месте не стоит, и функционал примитивов приходится наращивать. В одной из следующих статей мы рассмотрим вторую версию примитива, куда мы добавим новые поля, и расскажем, какие возможности по работе с версиями примитивов предоставляет MultiCAD.NET API.

ссылка на оригинал статьи http://habrahabr.ru/company/nanosoft/blog/184482/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *