Боковая панель

Создание пользовательского примитива

Программный комплекс Топоматик Робур позволяет разрабатывать пользовательские примитивы. Примитив - это элемент модели чертежа. Каждый примитив умеет отобразить себя в чертеже, и при необходимости в окне 3D вида.

Создание пользовательского примитива

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

  1. Создание класса примитива, наследника от DwgEntity и связь его с чертежом и контроллером через атрибуты DesignAliasAttribute и EntityControllerAttribute.
  2. Создание класса контроллера примитива, наследника от DwgEntityController. Он служит для графического отображения на плане и 3D-виде
  3. Создание команды добавления примитива на план

Подготовка модуля

Создайте и настройте новый модуль для подключения к программному комплексу Топоматик Робур. С помощью диалогового окна Менеджер ссылок добавьте ссылки на следующие библиотеки:

  • Topomatic.Cad.Foundation.dll - базовые математические типы и операции
  • Topomatic.Cad.View.dll - элемент управления для отображения слоёв моделей
  • Topomatic.ComponentModel.dll – атрибуты свойств класса
  • Topomatic.Dwg.dll – элементы области рисования
  • Topomatic.Dwg.Layer.dll – слои области рисования
  • Topomatic.Visualization.dll - элементы информационной модели

Пользовательский примитив

В этом примере мы создадим точечный пользовательский примитив и свяжем его с и элементом ИМ содержащим 3D-модель.

Создадим класс примитива. Класс должен наследовать абстрактный тип DwgEntity. Для связи с элементом ИМ также добавим свойство Element типа ImElement. Перекроем необходимые методы и свойства, а также добавим свои для возможности изменять состояние примитива через окно свойств. Класс декорируется атрибутами DesignAliasAttribute и EntityControllerAttribute.

В методе OnRegen() необходимо переопределять границы примитива (свойство Bounds).

Для изменения отображения и положения примитива в пространстве добавим свойства:

  1. Position – координаты примитива
  2. Angle – угол поворота
  3. Element – элемент ИМ

Добавим метод GetPlan() возвращающий блок (DwgBlock) содержащий примитивы для отображения на плане. В нашем случает блок формируется с помощью метода GetView() элемента ИМ (класс ImViewElement). Добавьте в программу новый класс со следующим содержанием:

CustomDwgEntity.cs
[EntityController(typeof(CustomDwgEntityController))]
[DesignAlias("CUSTOMDWGENTITY")]
public class CustomDwgEntity : DwgEntity, IPointObject
{
    public const string PARENT_SMDX = "SmdxElement";
    private ImElement m_Element;
    private Vector3D m_Position;
    private Vector3D m_Normal = new Vector3D(0, 0, 1);
    private double m_Angle = 0;
    private Drawing m_Plan;
 
    #region Properties
    [Browsable(false)]
    public Matrix Matrix
        {
            get
            {
                return Matrix.CreateExtrudedInsertion(m_Position, Vector3D.One, m_Normal, m_Angle);
            }
        }
 
    [DisplayName("Положение")]
    public Vector3D Position
        {
            get
            {
                return m_Position;
            }
            set
            {
                if (Position != value)
                {
                    m_Position = value;
                }
            }
        }
    [Angle]
    [DisplayName("Угол поворота")]
    public double Angle
        {
            get
            {
                return m_Angle;
            }
            set
            {
                if (Angle != value)
                {
                    m_Angle = value;
                }
            }
        }
 
    Vector3D IPointObject.BasePoint
        {
            get { return m_Position; }
        }
 
    [DisplayName("3D модель"), ImObjectPropertyProvider(PARENT_SMDX, false)]
    public ImElement Element
        {
            get
            {
                return m_Element;
            }
            set
            {
                BeginUpdate();
                try
                {
                    m_Element = value;
                    m_Plan = null;
                    Invalidate();
                }
                finally
                {
                    EndUpdate();
                }
            }
        }
 
    [Browsable(false)]
    public Vector3D Normal
        {
            get
            {
                return m_Normal;
            }
            set
            {
                if (Normal != value)
                {
                    Vector3D position, scale;
                    double angle;
                    var view1 = Matrix.GetElementView(out position, out scale, out angle);
                    m_Normal = value;
                    var view2 = Matrix.GetElementView(out position, out scale, out angle);
                    if (view1 != view2)
                    {
                        m_Plan = null;
                    }
                    Invalidate();
                }
            }
        }
 
    public override bool IsBreakable
        {
            get
            {
                return (m_Element != null) && (m_Element.IsAssembly);
            }
        }
 
    public override bool IsPurged
        {
            get
            {
                return m_Element == null;
            }
        }
 
    public override bool IsProxyGraphics
        {
            get
            {
                return false;
            }
        }
 
    public override string EntityName
        {
            get { return TypeExplorer.GetSerializableString(GetType()); }
        }
 
    [Browsable(false)]
    public BoundingBox3D Bounds3d
        {
            get
            {
                if (IsInvalid)
                {
                    Regen(EventArgs.Empty);
                }
 
                var cache = GetCache();
                if (cache != null)
                {
                    var bounds = cache.Bounds;
                    bounds.Min += m_Position;
                    bounds.Max += m_Position;
                    return bounds;
                }
 
                return new BoundingBox3D();
            }
        }
    #endregion
 
    protected override void OnRegen(EventArgs e)
        {
            var matrix = Matrix;
            var plan = GetPlan(1.0);
            if ((plan != null) && (plan.Count > 0))
            {
                Vector3D position, scale;
                double angle;
                matrix.GetElementView(out position, out scale, out angle);
                Bounds = plan.Bounds * Matrix.CreateInsertion(position, scale, angle);
            }
            else
            {
                Invalidate();
            }
        }
 
    protected override void OnBreak(IList<DwgEntity> list)
        {
            if (m_Element != null)
            {
                var matrix = Matrix;
                var model = m_Element.GetModel();
                if (model != null)
                {
                    var element = new Static3DElement(
                        m_Element.Name, m_Element.GetObjectType(), 
                        m_Element.GetProperties().Clone(),
                        model, new ImDocuments(m_Element));
                    ExtractEntities(list, matrix, model, element);
                }
                if (m_Element.IsAssembly)
                {
                    foreach (var item in m_Element.GetReferences())
                    {
                        var m = item.GetMatrix() * matrix;
                        ExtractEntities(list, m, model, item.Element);
                    }
                }
            }
        }
 
    protected override void OnAssign(DwgEntity source)
        {
            var entity = source as CustomDwgEntity;
            if (entity != null)
            {
                if (entity.m_Element != null)
                {
                    m_Element = entity.m_Element.Clone();
                    m_Plan = entity.m_Plan;
                    m_Position = entity.m_Position;
                    m_Normal = entity.m_Normal;
                    m_Angle = entity.m_Angle;
                }
                else
                {
                    m_Element = null;
                }
            }
        }
 
    protected override void OnTransform(Matrix matrix)
        {
            var scale = Vector3D.One;
            (Matrix * matrix).GetExtrudedInsertion(out m_Position, out scale, out m_Normal, out m_Angle);
        }
 
    protected override void OnSaveToStg(StgNode node)
        {
            m_Position.SaveToStg(node.AddNode("Position"));
            m_Normal.SaveToStg(node.AddNode("Normal"), Vector3D.UnitZ);
            node.AddDouble("Angle", m_Angle, 0.0);
        }
 
    protected override void OnSaveToStg(StgNode node, ISerializationContext context)
        {
            base.OnSaveToStg(node, context);
            if (m_Element != null)
            {
                m_Element.SaveToStg(node.AddNode("Model"), context);
            }
            if (m_Plan != null)
            {
                node.AddInt32("PlanSign", context.AddSerializable(m_Plan));
            }
        }
 
    protected override void OnLoadFromStg(StgNode node)
        {
            if (node.IsExists("Element"))
            {
                var context = new SerializationContext(this);
                context.LoadFromStg(node.GetNode("Context"));
                m_Element = ImElement.LoadFromStg(node.GetNode("Element"), context);
            }
            m_Position = Vector3D.LoadFromStg(node.GetNode("Position"));
            m_Normal = Vector3D.LoadFromStg(node.GetNode("Normal"), Vector3D.UnitZ);
            m_Angle = node.GetDouble("Angle", 0);
        }
 
    protected override void OnLoadFromStg(StgNode node, ISerializationContext context)
        {
            base.OnLoadFromStg(node, context);
            if (node.IsExists("Model"))
            {
                m_Element = ImElement.LoadFromStg(node.GetNode("Model"), context);
            }
            if (node.IsExists("PlanSign"))
            {
                m_Plan = context.GetSerializable<Drawing>(node.GetInt32("PlanSign"));
            }
        }
 
    public double? Fire(Ray3D ray)
        {
            if (IsInvalid)
            {
                Regen(EventArgs.Empty);
            }
            var cache = GetCache();
            if (cache != null)
            {
                ray.Position -= Position;
                return cache.Fire(ray);
            }
            return null;
        }
 
    [Browsable(false)]
    public DwgBlock GetPlan(double mapscale)
        {
            var sscale = mapscale.ToString(CultureInfo.InvariantCulture);
            if (m_Plan == null)
            {
                if (m_Element != null)
                {
                    Vector3D position, scale;
                    double angle;
                    var view = Matrix.GetElementView(out position, out scale, out angle);
                    m_Plan = new Drawing();
                    var vel = m_Element as ImViewElement;
                    if (vel != null)
                    {
                        try
                        {
                            vel.GetView(view, m_Plan.Blocks.Add(sscale), Matrix.Identity, mapscale);
                        }
                        catch
                        { }
                    }
                }
            }
            if (m_Plan == null)
            {
                return null;
            }
            var block = m_Plan.Blocks[sscale];
            if (block == null)
            {
                if (m_Element != null)
                {
                    Vector3D position, scale;
                    double angle;
                    var view = Matrix.GetElementView(out position, out scale, out angle);
                    m_Plan = new Drawing();
                    var vel = m_Element as ImViewElement;
                    if (vel != null)
                    {
                        try
                        {
                            block = m_Plan.Blocks.Add(sscale);
                            vel.GetView(view, block, Matrix.Identity, mapscale);
                        }
                        catch
                        { }
                    }
                }
            }
            return block;
        }
 
    public GeometryModelsCache GetCache()
        {
            var builder = new GeometryModelsCacheBuilder(Position);
            var cache = builder.Create(Element, Matrix);
            return cache;
        }
 
    private void ExtractEntities(IList<DwgEntity> list, Matrix matrix, GeometryModel3D model, ImElement element)
        {
            Vector3D position, scale, normal;
            double angle;
            matrix.GetExtrudedInsertion(out position, out scale, out normal, out angle);
            var e = new CustomDwgEntity();
            e.CopyProperties(this);
            e.Position = position;
            e.Normal = normal;
            e.Angle = angle;
            e.Element = element;
            e.m_Plan = null;
            list.Add(e);
        }
}

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

Контроллер отвечает за отображение примитива на плане, а также на 3D-виде, если это необходимо. Создадим класс контроллера. Класс должен наследовать абстрактный тип DwgEntityController. Перекроем необходимые методы и свойства. Если требуется отображение на 3D-виде, то дополнительно нужно перекрыть свойство SupportPaint3d, а также методы OnPaintEntity3d() для отрисовки и Fire() для выделения 3D-модели в указанной точке. Добавьте в программу новый класс со следующим содержанием:

CustomDwgEntityController.cs
public class CustomDwgEntityController : DwgEntityController
{
    public override bool SupportPaint3d
        {
            get
            {
                return true;
            }
        }
 
    protected override void OnPaintEntity(DwgEntity entity, PaintEntityEventArgs args)
    {
        var e = (CustomDwgEntity)entity;
        var block = e.GetPlan(args.AnnotationScale);
        if (block != null)
        {
            Vector3D position, scale;
            double angle;
            e.Matrix.GetElementView(out position, out scale, out angle);
            PaintEntityEventArgs.PaintEntities(e, block, position, scale, angle, args);
        }
    }
 
    public override IEnumerable GetGrips(DwgEntity entity, object cadview)
    {
        var e = (CustomDwgEntity)entity;
        var has_grips = false;
        var grip = new EntityGrip(((CadView)cadview), entity);
        grip.Location = e.Position;
        var z = e.Position.Z;
        grip.Move += delegate (Vector3D vertex)
        {
            e.Position = new Vector3D(vertex.Pos, z);
        };
        yield return grip;
    }
 
    protected override void OnPaintEntity3d(DwgEntity entity, PaintEntityEventArgs args)
    {
        var e = (CustomDwgEntity)entity;
        var dc = args.Pen.DeviceContext;
        if (args.Pen.DeviceContext.gContains(e.Bounds3d))
        {
            var cache = e.GetCache();
            if (cache != null)
            {
                var pivot = dc.Pivot;
                dc.Pivot = e.Position;
                try
                {
                    cache.Paint(dc);
                }
                finally
                {
                    dc.Pivot = pivot;
                }
            }
        }
    }
 
    public override double? Fire(DwgEntity entity, Ray3D ray)
    {
        var e = (CustomDwgEntity)entity;
        return e.Fire(ray);
    }
}

Команда вставки примитива на план

В теле программного модуля объявите команду, и декорируйте её атрибутом «cmd». Команда «insert_custom_entity» предложит пользователю указать точку на плане, выбрать 3D-модель из библиотеки и добавит пользовательский примитив в чертёж активной модели. 3D-модели будут отфильтрованы в соответствии с константой PARENT_SMDX класса CustomDwgEntity. В списке будут доступны только те модели, чей smdx-тип унаследован от «SmdxElement».

Module.cs
[cmd("insert_custom_entity")]
private void InsertCustomEntity()
{
    var cadView = this.CadView;
    if (DrawingLayer.GetDrawingLayer(cadView) is DrawingLayer drawingLayer)
    {
        var drawing = drawingLayer.Drawing;
        Vector3D point;
        if (!CadCursors.GetPoint(cadView, out point, "Укажите точку вставки примитива")) return;
        if (ImObjectPropertyProvider.SelectObject(CustomDwgEntity.PARENT_SMDX) is ImElement imElement)
        {
            var block = drawing.ActiveSpace;
            var entity = new CustomDwgEntity{Element = imElement, Position = point};
            block.Add(entity);
 
            cadView.Unlock();
            cadView.Invalidate();
        }
    }
}

Теперь необходимо сформировать наш plugin-файл. Заполните его следующим образом.

TutorialCustomDwgEntity.plugin
{
  "assemblies": {
    "TutorialCustomDwgEntity": {
      "assembly": "TutorialCustomDwgEntity.dll, TutorialCustomDwgEntity.ModulePluginHost"
    }
  },
 
  "actions": {
    "id_insert_custom_entity": {
      "cmd": "insert_custom_entity",
      "title": "Пользовательский примитив",
      "description": "Вставка пользовательского примитива"
    }
  },
 
  "menubars": {
    "rbproj": {
      "items": [
        {
          "id": "tutorial_menu",
          "title": "Tutorial",
          "items": [
            "id_insert_custom_entity"
          ]
        }
      ]
    }
  }
}

Результатом запуска проекта будет появление в главном меню пункта «Tutorial», с подпунктами, которые будут работать в соответствии с описанными выше алгоритмами.

Исходный код примера расположен в проекте «TutorialCustomDwgEntity».
developers/tutorial/customentity.txt · Последние изменения: 2022/12/15 13:14 — proxor