Инструменты пользователя

Инструменты сайта


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

developers:tutorial:customentity

Это старая версия документа.


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

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

Особенностью разработки примитива в комплексе Топоматик Робур является возможность связать его с 3D-моделью, а также элементом информационной модели (ИМ). 3D-модель служит для отображения объекта на 3D виде. Элемент ИМ обладает семантическими свойствами, отображаемыми при работе со сводной информационной моделью.

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

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

  1. Создание класса примитива
  2. Создание класса обёртки примитива для работы с инспектором свойств
  3. Создание класса контроллера примитива для определения его графического отображения
  4. Создание команды добавления примитива на план

Для связывания примитива с 3D-моделью рассмотрим процесс создания пользовательской библиотеки 3D-объектов. Во время создания библиотеки, мы также создадим пользовательские элементы ИМ и свяжем их с 3D-моделями. Этот процесс не является обязательным. Подготовка модуля Создайте и настройте новый модуль для подключения к программному комплексу Топоматик Робур. С помощью диалогового окна Менеджер ссылок добавьте ссылки на следующие библиотеки:

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

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

В этом примере мы создадим точечный пользовательский примитив и свяжем его с пользовательской 3D-моделью и элементом ИМ. Класс примитива содержит много кода, поэтому для удобства сделаем его разделяемым применив модификатор partial.

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

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

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

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

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

CustomDwgEntity.cs
[EntityController(typeof(CustomDwgEntityController))]
[DesignAlias("CUSTOMDWGENTITY")]
public partial class CustomDwgEntity : DwgEntity, IConstructionModelHolder, IPointObject
{
    public const string PARENT_SMDX = "SmdxCustomVolumeElement";
    private ImElement m_Element;
    private GeometryModelsCache m_Cache;
    private Vector3D m_Position;
    private Vector3D m_Normal = new Vector3D(0, 0, 1);
    private Vector3D m_Scale = new Vector3D(1, 1, 1);
    private double m_Angle = 0;
    private Drawing m_Plan;
    private int m_Changes;
 
    #region Properties
    [Browsable(false)]
    public Matrix Matrix
        {
            get
            {
                return Matrix.CreateExtrudedInsertion(m_Position, m_Scale, m_Normal, m_Angle);
            }
        }
 
    [DisplayName("Положение")]
    public Vector3D Position
        {
            get
            {
                return m_Position;
            }
            set
            {
                if (Position != value)
                {
                    BeginChange();
                    try
                    {
                        m_Position = value;
                    }
                    finally
                    {
                        EndChange();
                    }
                }
            }
        }
 
    [Browsable(false)]
    public double Rotation
        {
            get
            {
                return Angle;
            }
            set
            {
                Angle = value;
            }
        }
 
    [Angle]
    [DisplayName("Угол поворота")]
    public double Angle
        {
            get
            {
                return m_Angle;
            }
            set
            {
                if (Angle != value)
                {
                    BeginChange();
                    try
                    {
                        m_Angle = value;
                    }
                    finally
                    {
                        EndChange();
                    }
                }
            }
        }
 
    Vector3D IPointObject.BasePoint
        {
            get { return m_Position; }
        }
 
    [GlobalVector, DisplayName("Масштаб")]
    public Vector3D Scale
        {
            get
            {
                return m_Scale;
            }
            set
            {
                if (Scale != value)
                {
                    BeginChange();
                    try
                    {
                        m_Scale = value;
                    }
                    finally
                    {
                        EndChange();
                    }
                }
            }
        }
 
    [DisplayName("3D модель"), ImObjectPropertyProvider(PARENT_SMDX, false)]
    public ImElement Element
        {
            get
            {
                return m_Element;
            }
            set
            {
                BeginUpdate();
                try
                {
                    m_Element = value;
                    m_Plan = null;
                    m_Cache = null;
                    Invalidate();
                }
                finally
                {
                    EndUpdate();
                }
            }
        }
 
    [Browsable(false)]
    public Vector3D Normal
        {
            get
            {
                return m_Normal;
            }
            set
            {
                if (Normal != value)
                {
                    BeginChange();
                    try
                    {
                        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();
                    }
                    finally
                    {
                        EndChange();
                    }
                }
            }
        }
 
    [Browsable(false)]
    public GeometryModelsCache Cache
        {
            get
            {
                if (m_Cache == null)
                {
                    if (m_Element != null)
                    {
                        var builder = new GeometryModelsCacheBuilder(Position);
                        m_Cache = builder.Create(m_Element, Matrix);
                    }
                }
                return m_Cache;
            }
        }
 
    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()); }
        }
 
    [Category("Информационная модель"), TypedObjectProvider(null)]
    public TypedObject TypedObject
        {
            get
            {
                return new CustomElementWrapper(this);
            }
        }
 
    [Browsable(false)]
    public BoundingBox3D Bounds3d
        {
            get
            {
                if (IsInvalid)
                {
                    Regen(EventArgs.Empty);
                }
                var cache = Cache;
                if (cache != null)
                {
                    var bounds = cache.Bounds;
                    bounds.Min += m_Position;
                    bounds.Max += m_Position;
                    return bounds;
                }
                else
                {
                    return new BoundingBox3D();
                }
            }
        }
 
    [Browsable(false)]
    public bool HasCache3D
        {
            get
            {
                return m_Cache != null;
            }
        }
    #endregion
 
    protected override void OnRegen(EventArgs e)
        {
            var matrix = Matrix;
            if (m_Cache != null)
            {
                m_Cache.Dispose();
            }
            m_Cache = null;
            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 m = matrix;
                    Vector3D position, scale, normal;
                    double angle;
                    m.GetExtrudedInsertion(out position, out scale, out normal, out angle);
                    var e = new CustomDwgEntity();
                    e.CopyProperties(this);
                    e.Position = position;
                    e.Scale = scale;
                    e.Normal = normal;
                    e.Angle = angle;
                    e.Element = new Static3DElement(m_Element.Name, m_Element.GetObjectType(), m_Element.GetProperties().Clone(), model, new ImDocuments(m_Element));
                    e.m_Plan = null;
                    e.m_Cache = null;
                    list.Add(e);
                }
                if (m_Element.IsAssembly)
                {
                    foreach (var item in m_Element.GetReferences())
                    {
                        var m = item.GetMatrix() * matrix;
                        Vector3D position, scale, normal;
                        double angle;
                        m.GetExtrudedInsertion(out position, out scale, out normal, out angle);
                        var e = new CustomDwgEntity();
                        e.CopyProperties(this);
                        e.Position = position;
                        e.Scale = scale;
                        e.Normal = normal;
                        e.Angle = angle;
                        e.Element = item.Element;
                        e.m_Plan = null;
                        e.m_Cache = null;
                        list.Add(e);
                    }
                }
            }
        }
 
    protected override void OnAssign(DwgEntity source)
        {
            var model = source as CustomDwgEntity;
            if (model != null)
            {
                if (model.m_Element != null)
                {
                    m_Element = model.m_Element.Clone();
                    m_Cache = model.m_Cache;
                    m_Plan = model.m_Plan;
                    m_Position = model.m_Position;
                    m_Normal = model.m_Normal;
                    m_Scale = model.m_Scale;
                    m_Angle = model.m_Angle;
                }
                else
                {
                    m_Element = null;
                }
            }
        }
 
    protected override void OnTransform(Matrix matrix)
        {
            BeginChange();
            try
            {
                (Matrix * matrix).GetExtrudedInsertion(out m_Position, out m_Scale, out m_Normal, out m_Angle);
            }
            finally
            {
                EndChange();
            }
        }
 
    protected override void OnSaveToStg(StgNode node)
        {
            m_Position.SaveToStg(node.AddNode("Position"));
            m_Normal.SaveToStg(node.AddNode("Normal"), Vector3D.UnitZ);
            m_Scale.SaveToStg(node.AddNode("Scale"), Vector3D.One);
            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_Scale = Vector3D.LoadFromStg(node.GetNode("Scale"), Vector3D.One);
            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 void BeginChange()
        {
            m_Changes++;
            BeginUpdate();
        }
 
    public void EndChange()
        {
            m_Changes--;
            if (m_Changes == 0)
            {
                m_Plan = null;
                m_Cache = null;
                Invalidate();
                EndUpdate();
            }
        }
 
    public double? Fire(Ray3D ray)
        {
            if (IsInvalid)
            {
                Regen(EventArgs.Empty);
            }
            var cache = Cache;
            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;
        }
}

Обёртка примитива

Обёртка примитива (Wrapper) – это объект-посредник между примитивом и инспектором свойств. С его помощью обеспечивается изменение состояния примитива. Класс обёртки описывается внутри класса примитива. Создадим класс обёртки. Для удобства работы с кодом, поместим описание класса-обёртки в отдельный файл. Класс должен наследовать абстрактный тип TypedObjectWrapper. Перекроем необходимые методы. Добавьте в программу новый класс со следующим содержанием:

CustomDwgEntity.Wrapper.cs
public partial class CustomDwgEntity
{
    class CustomElementWrapper : TypedObjectWrapper
    {
        private CustomDwgEntity m_Owner;
 
        public CustomElementWrapper(CustomDwgEntity owner)
            {
                m_Owner = owner;
            }
 
        protected override TypedObject GetTypedObject()
            {
                return m_Owner.m_Element;
            }
 
        protected override void OnTypeChanged(ImTypeDescriptor dsc)
            {
                throw new NotSupportedException();
            }
 
        protected override void OnAddProperty(TypedObject tobj, ImProperty p)
            {
                m_Owner.BeginUpdate();
                try
                {
                    var properties = tobj.GetProperties();
                    properties.Add(p);
                    tobj.ApplayOverridedProperties(properties);
 
                    m_Owner.m_Cache = null;
                    m_Owner.m_Plan = null;
                    m_Owner.Invalidate();
                }
                finally
                {
                    m_Owner.EndUpdate();
                }
            }
 
        protected override void OnModifiedProperty(TypedObject tobj, ImProperty p, object value)
            {
                m_Owner.BeginUpdate();
                try
                {
                    if (tobj.TryGetProperty(p, out var property))
                    {
                        property.Value = value;
                        tobj.ApplayOverridedProperties(new[] { property });
                    }
 
                    m_Owner.m_Cache = null;
                    m_Owner.m_Plan = null;
                    m_Owner.Invalidate();
                }
                finally
                {
                    m_Owner.EndUpdate();
                }
            }
 
        protected override void OnRemoveProperty(TypedObject tobj, ImProperty property)
            {
                m_Owner.BeginUpdate();
                try
                {
                    var properties = tobj.GetProperties();
                    properties.Remove(property);
                    tobj.ApplayOverridedProperties(properties);
 
                    m_Owner.m_Cache = null;
                    m_Owner.m_Plan = null;
                    m_Owner.Invalidate();
                }
                finally
                {
                    m_Owner.EndUpdate();
                }
            }
    }
}

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

Контроллер отвечает за отображение примитива на плане, а также на 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 drawing = e.Drawing;
        var obj = e.TypedObject;
        var cadView = (CadView)cadview;
        foreach (var p in obj.GetAllProperties())
        {
            var grips = TypedPropertyManagerCollection.Current.GetGrips(cadView, drawing, obj, p, e.Matrix);
            if (grips != null)
            {
                foreach (var tg in grips)
                {
                    if (tg.Location.EqualsEps(e.Position))
                    {
                        has_grips = true;
                    }
                    yield return tg;
                }
            }
        }
        if (!has_grips)
        {
            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 (!e.HasCache3D)
            {
                if (dc.Simplify)
                {
                    dc.RequestRedraw();
                    return;
                }
            }
            if (args.Pen.DeviceContext.gContains(e.Bounds3d))
            {
                var cache = e.Cache;
                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);
        }
}
developers/tutorial/customentity.1670412023.txt.gz · Последние изменения: 2022/12/07 11:20 — proxor