Это старая версия документа.
Программный комплекс Топоматик Робур позволяет разрабатывать пользовательские примитивы. Примитив - это элемент чертежа, обладающий графическим отображением в области рисования. Условно, примитивы можно разделить на простые и сложные. К простым относятся точка, отрезок, дуга, окружность, луч, прямая, эллипс или однострочный текст. К сложным все остальные.
Особенностью разработки примитива в комплексе Топоматик Робур является возможность связать его с 3D-моделью, а также элементом информационной модели (ИМ). 3D-модель служит для отображения объекта на 3D виде. Элемент ИМ обладает семантическими свойствами, отображаемыми при работе со сводной информационной моделью.
Для создания пользовательского примитива необходимы следующие действия
Для связывания примитива с 3D-моделью рассмотрим процесс создания пользовательской библиотеки 3D-объектов. Во время создания библиотеки, мы также создадим пользовательские элементы ИМ и свяжем их с 3D-моделями. Этот процесс не является обязательным. Подготовка модуля Создайте и настройте новый модуль для подключения к программному комплексу Топоматик Робур. С помощью диалогового окна Менеджер ссылок добавьте ссылки на следующие библиотеки:
В этом примере мы создадим точечный пользовательский примитив и свяжем его с пользовательской 3D-моделью и элементом ИМ. Класс примитива содержит много кода, поэтому для удобства сделаем его разделяемым применив модификатор partial.
Создадим класс примитива. Класс должен наследовать абстрактный тип DwgEntity. Для связи с элементом ИМ также необходимо реализовать интерфейс IConstructionModelHolder. Перекроем необходимые методы и свойства, а также добавим свои для возможности изменять состояние примитива через окно свойств. Класс декорируется атрибутами DesignAliasAttribute и EntityControllerAttribute.
В методе OnRegen() необходимо переопределять границы примитива (свойство Bounds).
Для изменения отображения и положения примитива в пространстве добавим свойства:
Добавим метод GetPlan() возвращающий блок (DwgBlock) содержащий примитивы для отображения на плане. В нашем случает блок формируется с помощью метода GetView() элемента ИМ (класс ImViewElement). Добавьте в программу новый класс со следующим содержанием:
[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. Перекроем необходимые методы. Добавьте в программу новый класс со следующим содержанием:
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-модели в указанной точке. Добавьте в программу новый класс со следующим содержанием:
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); } }
В теле программного модуля объявите команду, и декорируйте её атрибутом «cmd». Команда «insert_custom_entity» предложит пользователю указать точку на плане, выбрать 3D-модель из библиотеки и добавить пользовательский примитив в чертёж активной модели.
[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(); } } }