Это старая версия документа.
Для графического отображения моделей на экране в программном комплексе используется видовой экран, реализованный с помощью элемента CadView. Видовой экран служит для отображения элементов модели на экране, масштабирования и поворота изображения, а также поддерживает операции редактирования модели. Для реализации графического вывода каждой отдельной модели используются слои видового экрана - наследники от CadViewLayer. При создании наследника от CadViewLayer необходимо реализовать следующие свойства и методы:
Для графических операций необходимо использовать экземпляр класса CadPen, который приходит в метод OnPaint в качестве параметра. Он реализует основные графические операции, такие как:
Все операции выполняются в системе координат модели. За текущий масштаб и другие трансформации отвечает видовой экран.
За вывод текста на экран отвечает отдельный класс FontManager. Получить его экземпляр можно используя синглтон FontManager.Current. Для отображения текста на экране необходимо получить требуемый шрифт у экземпляра класса FontManager и вызвать у него метод DrawString.
Создайте новую модель и подключите её к программному комплексу Топоматик Робур.
С помощью диалогового окна Менеджер ссылок добавьте ссылки на следующие библиотеки:
Реализуйте класс Model.cs, который описывает модель. В данном случае задача модели хранить список точек, введённых пользователем.
//Класс реализующий структуру данных нашей модели //для поддержки интерфейса 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()); } } }
Для отображения модели на видовом экране необходимо создать слой видового экрана. Поместите его в файл ModelLayer.cs.
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(); } } }
Поскольку наша модель предназначена для отображения на видовом экране плана, класс редактора необходимо наследовать от PlanModelEditor. Реализуйте редактор модели в файле Editor.cs.
//Класс реализующий редактор нашей модели class Editor : PlanModelEditor { //Ссылки на другие модели в структуре public override ModelReference[] GetReferences(object model) { //В нашем случае никаких ссылок нет return null; } //Реализация загрузки модели по указанному пути, должна вернуть реализацию класса нашей модели 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; } } }
Обратите внимание, что в случае реализации редактора модели в виде наследника от PlanModelEditor нет необходимости перекрывать функцию Open, но нужно реализовать функции работающие с вашим видовым слоем - CreatePlanLayer и RemovePlanLayer.
В теле программного модуля, кроме функций для создания и регистрации модели и редактора, необходимо реализовать функцию для редактирования модели. В нашем случае она будет запрашивать у пользователя ввод многоугольника из нескольких точек и сохранять их в нашу модель.
... [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; } } } } ...
В файле .plugin мы описываем и подключаем нашу модель. Кроме того добавляем дополнительную команду и пункт меню, предназначенный для редактирования модели. А в контекстном меню модели поддерживаем функционал для её активации.
{ ... "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" ] } ... } ... }
В результате мы получим возможность добавлять в проект наши тестовые модели и редактировать их содержимое по команде «Редактировать активную модель» в контекстном меню модели. После изменения наша модель будет отображаться на видовом экране плана.