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

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


developers:tutorial:addlayer

Различия

Здесь показаны различия между двумя версиями данной страницы.

Ссылка на это сравнение

Следующая версия
Предыдущая версия
developers:tutorial:addlayer [2019/02/16 16:31]
vasya создано
developers:tutorial:addlayer [2022/03/15 19:14] (текущий)
proxor
Строка 1: Строка 1:
-Поведение модели может быть реализовано одним из следующих способов:​ +====== Отображение на видовом экране ====== 
-  * При открытии модели открывается программа предназначенная для работы с файлами данного типа (например ​файлы ведомостей,​ текстовые ​файлы и т.п.) + 
-  ​* ​При открытии модели открывается отдельное окно для редактирования ​(например файлы ​чертежа) +Для графического отображения моделей на экране в программном комплексе используется видовой экран, реализованный с помощью элемента [[developers:​references:​topomatic.cad.view.cadview|CadView]]. Видовой экран служит для отображения элементов модели на экране,​ масштабирования и поворота изображения,​ а также поддерживает операции редактирования модели. Для реализации графического вывода каждой отдельной модели используются слои видового экрана - наследники от [[developers:​references:​topomatic.cad.view.cadviewlayer|CadViewLayer]]. 
-  * При открытии модель отображается в окне плана (например файлы поверхности, подобъектов и т.п.)+При создании наследника от [[developers:​references:​topomatic.cad.view.cadviewlayer|CadViewLayer]] необходимо ​реализовать следующие свойства и методы:​ 
 +  * [[developers:​references:​topomatic.cad.view.cadviewlayer.layerguid|LayerGuid]] - должен возвращать уникальный идентификатор тип слоя. Этот идентификатор должен однозначно идентифицировать все экземпляры слоя данного типа. 
 +  * [[developers:​references:​topomatic.cad.view.cadviewlayer.name|Name]] - имя слоя 
 +  * [[developers:​references:​topomatic.cad.view.cadviewlayer.selectionset|SelectionSet]] - возвращает экземпляр класса,​ наследника от [[developers:​references:​topomatic.cad.view.selectionset|SelectionSet]],​ который отвечает за выделение и редактирование объектов 
 +  * **OnGetLimits** - эта функция должна определить можно ли рассчитать общие границы слоя и вернуть ​их.  
 +  * **OnGetSnapObjects** - используется в том случае,​ если к элементам модели необходима объектная привязка. 
 +  * **OnPaint** - непосредственно реализует отображение модели 
 + 
 +Для графических операций необходимо использовать экземпляр класса [[developers:​references:​topomatic.cad.foundation.cadpen|CadPen]],​ который приходит в метод **OnPaint** ​ в качестве параметра. Он реализует основные графические операции,​ такие как
 +  * [[developers:​references:​topomatic.cad.foundation.cadpen.drawline_topomatic.cad.foundation.vector2d_topomatic.cad.foundation.vector2d|DrawLine]] - нарисовать линию 
 +  * [[developers:​references:​topomatic.cad.foundation.cadpen.drawpoint_topomatic.cad.foundation.vector2d|DrawPoint]] - нарисовать точку 
 +  * [[developers:​references:​topomatic.cad.foundation.cadpen.vertexcircle_topomatic.cad.foundation.vector2d_system.double|VertexCircle]] - нарисовать круг 
 +  * [[developers:​references:​topomatic.cad.foundation.cadpen.vertexarc_topomatic.cad.foundation.vector2d_system.double_system.double_system.double|VertexArc]] - нарисовать арку 
 +  * [[developers:​references:​ba26a4f6fed24b8edc05d90315f5a5e8|VertexEllipse]] - нарисовать эллипс 
 + 
 +Все операции выполняются в системе координат ​модели. За текущий масштаб и другие трансформации отвечает видовой экран. 
 + 
 +За вывод текста на экран отвечает ​отдельный класс [[developers:​references:​topomatic.cad.foundation.fontmanager|FontManager]]. Получить его экземпляр можно используя синглтон [[developers:​references:​topomatic.cad.foundation.fontmanager.current|FontManager.Current]]. Для отображения текста на экране необходимо получить требуемый шрифт у экземпляра класса [[developers:​references:​topomatic.cad.foundation.fontmanager|FontManager]] и вызвать у него метод [[developers:​references:​0d4287aafc4aea88565fe81cee5a2730|DrawString]]. 
 + 
 +Создайте новую [[developers:​tutorial:​createmodel|модель]] и подключите её к программному комплексу [[http://​www.topomatic.ru|Топоматик Робур]]. 
 + 
 + 
 +С помощью диалогового окна Менеджер ссылок добавьте ссылки на следующие библиотеки:​ 
 + 
 +  * [[developers:​references:​topomatic.cad.foundation|Topomatic.Cad.Foundation.dll]] - базовые математические типы и операции 
 +  * [[developers:​references:​topomatic.controls|Topomatic.Controls.dll]] - базовые диалоги и таблицы 
 +  * [[developers:​references:​topomatic.cad.view|Topomatic.Cad.View.dll]] - видовой экран 
 + 
 +Реализуйте класс Model.cs, который описывает модель. В данном случае задача модели хранить список точек, введённых пользователем. 
 +<code csharp>​ 
 +    //​Класс реализующий структуру данных нашей модели 
 +    //для поддержки интерфейса IStateController наследуем от StateControllerObject 
 +    class Model : StateControllerObject,​ IStgSerializable 
 +    { 
 +        private bool m_ReadOnly = false; 
 + 
 +        public List<​Vector2D>​ Points = new List<​Vector2D>​();​ 
 + 
 +        //флаг только ​для ​чтения 
 +        public override bool ReadOnly 
 +        { 
 +            get 
 +            { 
 +                return m_ReadOnly;​ 
 +            } 
 + 
 +            set 
 +            { 
 +                m_ReadOnly = value; 
 +            } 
 +        } 
 + 
 +        //Загрузка из узла 
 +        public void LoadFromStg(StgNode node) 
 +        { 
 +            Points.Clear();​ 
 +            //При загрузке массива указывается тип составляющих массив значений 
 +            var array = node.GetArray("​Points",​ StgType.Node);​ 
 +            for (int i = 0; i < array.Count;​ i++) 
 +            { 
 +                Points.Add(Vector2D.LoadFromStg(array.GetNode(i)));​ 
 +            } 
 +        } 
 + 
 +        //​Сохранение в узел 
 +        public void SaveToStg(StgNode node) 
 +        { 
 +            //​Сохраняем значения в узел 
 +            //​Сохраняем массив с указанием типа значений 
 +            var array = node.AddArray("​Points",​ StgType.Node);​ 
 +            for (int i = 0; i < Points.Count;​ i++) 
 +            { 
 +                Points[i].SaveToStg(array.AddNode());​ 
 +            } 
 +        } 
 +    } 
 +</​code>​ 
 + 
 +Для отображения модели на видовом экране необходимо создать слой видового экрана. Поместите его в файл ​ModelLayer.cs. 
 +<code csharp>​ 
 +    class ModelLayer : CadViewLayer 
 +    { 
 + 
 +        //для удобства определяем статический метод, позволяющий получить наш слой с видового экрана 
 +        public static ModelLayer GetModelLayer(CadView cadView) 
 +        { 
 +            //сначала проверяем,​ есть ли наш слой сразу в самом видовом экране 
 +            //это возможно,​ если видовой экран создан отдельно и слой расположен прямо на видовом экране 
 +            var layer = cadView[ModelLayer.ID] as ModelLayer;​ 
 +            if (layer == null) 
 +            { 
 +                //​теперь проверяем не находится ли слой в составе нескольких слоёв модели 
 +                //это наиболее распространённая ситуация 
 +                //для этого ​мы получаем слой, который содержит внутри все слои всех моделей 
 +                var multi = cadView[Consts.ModelsLayer] as MultiLayer;​ 
 +                if (multi != null) 
 +                { 
 +                    //​после этого получаем текущий слой активной модели 
 +                    var active = multi.ResolveActive();​ 
 +                    //​проверяем его на соответствие нашему слою 
 +                    layer = active as ModelLayer;​ 
 +                    if (layer == null) 
 +                    { 
 +                        //​кроме того возможен вариант,​ что у нашей модели несколько слоев 
 +                        //в этом случае они объединяются внутри общего слоя модели,​ который и будет являться активным 
 +                        var compound = active as CompoundLayer;​ 
 +                        if (compound != null) 
 +                        { 
 +                            layer = compound[ModelLayer.ID] as ModelLayer;​ 
 +                        } 
 +                    } 
 +                } 
 +            } 
 +            //если наш слой найден, но он заблокирован на редактирование,​ то мы не можем его вернуть 
 +            if ((layer != null) && (!layer.ResolveEnable())) 
 +            { 
 +                return null; 
 +            } 
 +            return layer; 
 +        } 
 + 
 +        //наша модель 
 +        private Model m_Model; 
 + 
 +        //класс, отвечающий за выделение объектов 
 +        private SelectionSet m_SelectionSet;​ 
 + 
 +        public ModelLayer() 
 +        { 
 +            //в качестве классаотвечающего за выделение объектов мы используем заглушку которая реализует его по умолчанию,​ в этом случае он не выделяет ничего 
 +            m_SelectionSet = new DefaultSelectionSet(this);​ 
 +        } 
 + 
 +        //Guid нашего слоя 
 +        public static readonly Guid ID = new Guid("​{36C745EB-2111-4D44-B4A1-9BE0B7DBD730}"​);​ 
 + 
 +        //​Возвращаем в качестве LayerId ID объявленный выше 
 +        public override Guid LayerGuid 
 +        { 
 +            get 
 +            { 
 +                return ID; 
 +            } 
 +        } 
 + 
 +        //​Возвращаем нашу заглушку 
 +        public override SelectionSet SelectionSet 
 +        { 
 +            get 
 +            { 
 +                return m_SelectionSet;​ 
 +            } 
 +        } 
 + 
 +        public override string Name 
 +        { 
 +            get 
 +            { 
 +                return "​Слой тестовой модели";​ 
 +            } 
 +        } 
 + 
 +        public Model Model 
 +        { 
 +            get 
 +            { 
 +                return m_Model; 
 +            } 
 +            set 
 +            { 
 +                m_Model = value; 
 +            } 
 +        } 
 + 
 +        //​Рассчитываем границы слоя 
 +        protected override bool OnGetLimits(out BoundingBox2D limits) 
 +        { 
 +            //Если есть точки в модели 
 +            if (m_Model.Points.Count > 0) 
 +            { 
 +                //​Создаем рамку вокруг ​первой точки 
 +                limits = new BoundingBox2D(m_Model.Points[0],​ m_Model.Points[0]); 
 +                for (int i = 1; i < m_Model.Points.Count;​ i++) 
 +                { 
 +                    //и добавляем в нее все остальные точки 
 +                    limits.AddPoint(m_Model.Points[i]);​ 
 +                } 
 +                return true; 
 +            } 
 +            //если точек в модели нет, возвращаем пустую рамку 
 +            limits = BoundingBox2D.Empty;​ 
 +            return false; 
 +        } 
 + 
 +        protected override void OnGetSnapObjects(ObjectSnapEventArgs e) 
 +        { 
 +            //Поскольку мы не реализуем привязки,​ то здесь мы не делаем ничего 
 +        } 
 + 
 +        protected override void OnPaint(CadPen pen) 
 +        { 
 +            //​рисуем нашу линию жёлтым цветом 
 +            pen.Color = Color.Yellow;​ 
 +            //​Начинаем рисовать 
 +            pen.BeginDraw();​ 
 +            try 
 +            { 
 +                //для отрисовки используем возможность нарисовать массив нескольких точек 
 +                //Для этого вызываем начало отрисовки массива 
 +                pen.BeginArray();​ 
 +                for (int i = 0; i < m_Model.Points.Count;​ i++) 
 +                { 
 +                    //Добавляем точки 
 +                    pen.Vertex(m_Model.Points[i]);​ 
 +                } 
 +                //​Заканчиваем отрисовку массива,​ в виде линии 
 +                pen.EndArray(ArrayMode.Polyline);​ 
 +            } 
 +            finally 
 +            { 
 +                //​заканчиваем рисовать 
 +                pen.EndDraw();​ 
 +            } 
 +            //в каждой точке пишем номер оранжевым цветом 
 +            pen.Color = Color.Orange;​ 
 +            //​начинаем рисовать 
 +            pen.BeginDraw();​ 
 +            try 
 +            { 
 +                var font = FontManager.Current.DefaultFont;​ 
 +                for (int i = 0; i < m_Model.Points.Count;​ i++) 
 +                { 
 +                    //​пишем номер точки, высотой в 2 еденицы чертежа 
 +                    font.DrawString(i.ToString(),​ pen, m_Model.Points[i],​ 0.0, 2.0); 
 +                } 
 +            } 
 +            finally 
 +            { 
 +                //​заканчиваем рисовать 
 +                pen.EndDraw();​ 
 +            } 
 +        } 
 +    } 
 +</​code>​ 
 + 
 +<​note>​ 
 +Мы реализовали у слоя статический метод **ModelLayer.GetModelLayer** который позволяет получить наш слой с видового экрана. Такой метод объявлен для большинства стандартных слоёв программного комплекса [[http://​www.topomatic.ru|Топоматик Робур]]. 
 +</​note>​ 
 + 
 +Поскольку ​наша модель предназначена ​для ​отображения на видовом экране плана, класс ​редактора необходимо наследовать от [[developers:​references:​topomatic.applicationplatform.core.planmodeleditor|PlanModelEditor]]. Реализуйте ​редактор модели ​в файле Editor.cs. 
 + 
 +<code csharp>​ 
 +//​Класс реализующий редактор ​нашей модели 
 +    class Editor : PlanModelEditor 
 +    { 
 +        //​Реализация загрузки модели по указанному пути, должна вернуть реализацию класса нашей модели 
 +        public override object LoadFromFile(string fullpath) 
 +        { 
 +            //​создаем экземпляр класса модели 
 +            var model = new Model(); 
 +            //если fullpath null - то необходимо просто вернуть экземпляр класса модели,​ без загрузки данных 
 +            if (fullpath != null) 
 +            { 
 +                //​создаем ​файловый поток 
 +                using (var stream = new FileStream(fullpath,​ FileMode.Open,​ FileAccess.Read,​ FileShare.Read)) 
 +                { 
 +                    //​создаем документ для ​работы с Topomatic.Stg 
 +                    var document = new StgDocument();​ 
 +                    //​загружаем документ из потока в бинарном виде 
 +                    document.LoadFromStreamAsBinary(stream);​ 
 +                    //​загружаем данные нашей модели из документа 
 +                    model.LoadFromStg(document.Body); 
 +                } 
 +            } 
 +            //​всегда возвращаем экземпляр модели 
 +            return model; 
 +        } 
 + 
 +        //​Реализация сохранения модели по указанному пути 
 +        public override void SaveToFile(object model, string fullpath) 
 +        { 
 +            //в качестве параметра model приходит наша ​модель ​данных 
 +            var m = model as Model; 
 +            if (m != null) 
 +            { 
 +                //​создаем файловый поток 
 +                using (var stream = new FileStream(fullpath,​ FileMode.Create,​ FileAccess.Write,​ FileShare.None)) 
 +                { 
 +                    //​создаем документ для работы с Topomatic.Stg 
 +                    var document = new StgDocument();​ 
 +                    //сохраняем данные нашей модели в документ 
 +                    m.SaveToStg(document.Body);​ 
 +                    //​сохраняем документ в потока в бинарном виде 
 +                    document.SaveToStreamAsBinary(stream);​ 
 +                } 
 +            } 
 +        } 
 + 
 +        protected override CadViewLayer CreatePlanLayer(IProjectModel model) 
 +        { 
 +            var m = model.LockRead() as Model; 
 +            if (m != null) 
 +            { 
 +                return new ModelLayer() { Model = m }; 
 +            } 
 +            return null; 
 +        } 
 + 
 +        protected override void ReloadModel(IProjectModel model, EditorResult editorResult) 
 +        { 
 +            var m = model.LockRead() as Model; 
 +            if (m != null) 
 +            { 
 +                var layer = (ModelLayer)editorResult.PlanLayer;​ 
 +                layer.Model = m; 
 +            } 
 +        } 
 + 
 +        protected override void RemovePlanLayer(IProjectModel model, CadViewLayer layer) 
 +        { 
 +            var model_layer = layer as ModelLayer;​ 
 +            if (model_layer != null) 
 +            { 
 +                model_layer.Model = null; 
 +            } 
 +        } 
 +    } 
 +</​code>​ 
 + 
 +Обратите внимание,​ что в случае реализации редактора модели в виде наследника от [[developers:​references:​topomatic.applicationplatform.core.planmodeleditor|PlanModelEditor]] нет необходимости перекрывать функцию **Open**, но нужно реализовать функции работающие с вашим видовым слоем - **CreatePlanLayer** и **RemovePlanLayer**. 
 + 
 +В теле программного модуля,​ кроме [[developers:​tutorial:​createmodel|функций для ​создания и регистрации модели и редактора]],​ необходимо реализовать функцию для редактирования модели. В нашем случае она будет запрашивать у пользователя [[developers:​tutorial:​dynamicrender|ввод многоугольника из нескольких точек]] и сохранять их в нашу модель. 
 +<code csharp>​ 
 +... 
 +        [cmd("​edit_pointsmodel"​)] 
 +        public void EditModel() 
 +        { 
 +            var cadView = CadView; 
 +            if (cadView != null) 
 +            { 
 +                var model_layer = ModelLayer.GetModelLayer(cadView);​ 
 +                if (model_layer != null) 
 +                { 
 +                    //список выбранных точек 
 +                    var positions = new List<​Vector2D>​();​ 
 +                    //делегат для динамической отрисовки 
 +                    DrawCursorEvent dynamic_draw = delegate ​(CadPen pen, Vector3D vertex) 
 +                    { 
 +                        //если есть точки в списке,​ нужно их нарисовать и нарисовать линию 
 +                        //​от ​последней выбранной точки, до текущего положения курсора 
 +                        if (positions.Count > 0) 
 +                        { 
 +                            pen.Color = Color.Lime;​ 
 +                            pen.BeginDraw();​ 
 +                            try 
 +                            { 
 +                                for (int i = 1; i < positions.Count;​ i++) 
 +                                { 
 +                                    pen.DrawLine(positions[i - 1], positions[i]);​ 
 +                                } 
 +                                pen.DrawLine(positions[positions.Count - 1], vertex.Pos);​ 
 +                            } 
 +                            finally 
 +                            { 
 +                                pen.EndDraw();​ 
 +                            } 
 +                        } 
 +                    }; 
 +                    //​подписываемся на событие отрисовки 
 +                    cadView.DynamicDraw += dynamic_draw;​ 
 +                    try 
 +                    { 
 +                        Vector3D pos; 
 +                        //​просим пользователя указать несколько точек 
 +                        while (CadCursors.GetPoint(cadView,​ out pos, "​Укажите точку"​)) 
 +                        { 
 +                            positions.Add(pos.Pos);​ 
 +                        } 
 +                        //если точки заданы,​ то изменяем нашу модель 
 +                        if (positions.Count > 0) 
 +                        { 
 +                            //​получаем её со слоя 
 +                            var model = model_layer.Model;​ 
 +                            //​очищаем точки 
 +                            model.Points.Clear();​ 
 +                            //​добавляем новые точки 
 +                            model.Points.AddRange(positions);​ 
 +                            //​выставляем флаг модификации вручную 
 +                            var p = PluginCoreOps.FindModel(model);​ 
 +                            if (p != null) 
 +                                p.Modified = true; 
 +                            //​обновляем видовой экран 
 +                            cadView.Unlock();​ 
 +                            cadView.Invalidate();​ 
 +                        } 
 +                    } 
 +                    finally 
 +                    { 
 +                        //​отписываемся от события отрисовки 
 +                        cadView.DynamicDraw -= dynamic_draw;​ 
 +                    } 
 +                } 
 +            } 
 +        } 
 +... 
 +</​code>​ 
 + 
 +В файле .plugin мы [[developers:​tutorial:​createmodel|описываем и подключаем нашу модель]]. Кроме того добавляем дополнительную команду и пункт меню, предназначенный для редактирования модели. А в контекстном меню модели поддерживаем функционал для её активации. 
 + 
 +<code javascript>​ 
 +
 +  ... 
 +  "​actions":​ { 
 +    ... 
 +    "​id_edit_pointsmodel":​ { 
 +      "​cmd":​ "​edit_pointsmodel"​, 
 +      "​title":​ "​Редактировать активную модель"​ 
 +    } 
 +    ... 
 +  }, 
 +  "​contexts":​ { 
 +    ... 
 +    "​pointsmodel.context":​ { 
 +      "​priority":​ 1001, 
 +      "​items":​ [ 
 +        { "​default":​ "​core.id_activate \"​%0\""​ }, 
 +        "​$(if,​$(opened,​%0),​core.id_close \"​%0\"​ \"​Скрыть модель\",​core.id_open \"​%0\"​ \"​Показать модель\"​)",​ 
 +        "​core.id_rmitem \"​%0\"",​ 
 +        "​core.id_mvitem \"​%0\"",​ 
 +        "​core.id_dublicate \"​%0\"",​ 
 +        "​-",​ 
 +        "​core.id_rmitem \"​%0\""​ 
 +      ] 
 +    } 
 +    ... 
 +  }, 
 +  "​menubars":​ { 
 +    ... 
 +    "​rbproj.test_menu":​ { 
 +      "​items":​ [ 
 +        "​id_edit_pointsmodel"​ 
 +      ] 
 +    } 
 +    ... 
 +  } 
 +  ... 
 +
 +</​code>​ 
 + 
 +В результате мы получим возможность ​добавлять в проект наши тестовые модели и редактировать их содержимое по команде "​Редактировать активную модель"​ в контекстном меню моделиПосле изменения наша модель будет отображаться на видовом экране ​плана. 
 +{{ :​developers:​tutorial:​addlayer:​result.png?​direct&​600 |}} 
 + 
 +<​note>​[[developers:​tutorial:​tutorialcode|Исходный код]] примера расположен в проекте **"​tutorial7"​**.</​note>​
  
developers/tutorial/addlayer.1550334686.txt.gz · Последние изменения: 2021/07/22 14:28 (внешнее изменение)