8 July 2013

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

Блог компании Нанософт corporate blogAPICAD/CAM
Одним из главных недостатков традиционного .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.

Обсуждение статьи доступно также и на нашем форуме: forum.nanocad.ru/index.php?showtopic=6504.

Перевод статьи на английский: Creating custom entities in CAD with MultiCAD .NET API.
Tags:nanocad.netapimulticad
Hubs: Блог компании Нанософт corporate blog API CAD/CAM
+13
10k 22
Comments 7