====== Экспорт чертежа и объектов информационной модели ====== Программный комплекс Топоматик Робур предоставляет пользователю возможность управления формированием чертежей плана и сводной модели. Для этого необходимо реализовать поддержку бродкастов: - "generate_planchet" - для формирования чертежа плана - "generate_visualization_map" - для формирования сводной модели В этом примере мы сформируем примитивы на основе данных нашей модели и поместим их в экспортируемый плоский чертёж. Также мы сформируем объекты на основе данных нашей модели и поместим их в экспортируемую сводную ИМ. ==== Подготовка модуля ==== Создайте и настройте новый модуль для подключения к программному комплексу Топоматик Робур. С помощью диалогового окна Менеджер ссылок добавьте ссылки на следующие библиотеки: * Topomatic.Cad.Foundation.dll - базовые математические типы и операции * Topomatic.Cad.View.dll - видовой экран * Topomatic.Dtm - цифровая модель местности * Topomatic.Dwg - чертёж * Topomatic.Planchet.Runtime - формирование планшета * Topomatic.Visualization - сводная модель * Topomatic.Visualization.Runtime - формирование сводной модели ==== Подготовка проекта ==== Добавим элементы нашей модели на план. В нашем случае это будет выглядеть так: {{ :developers:tutorial:createmodel:tut_export_model_views_1.png?direct |}} ==== Экспорт элементов модели в плоский чертёж ==== Для экспорта элементов пользовательской модели в плоский чертёж необходимы следующий действия: - Создать команду для добавления примитивов на чертёж - Зарегистрировать вызов команды через бродкаст «generate_planchet» в plugin-файле ==== Создание команды добавления примитивов на чертёж ==== Команда должна вызвать метод принимающий в качестве аргумента объект типа **GeneratePlanchetEventArgs**. Свойство **GeneratePlanchetEventArgs.Layer** возвращает текущий экспортируемый слой типа **CadViewLayer**. С помощью этого свойства убедимся, что слой является слоем нашей модели. Свойство **GeneratePlanchetEventArgs.Model** возвращает корневую область модели чертежа типа **DwgBlock**, в которую следует добавить необходимые примитивы (подклассы **DwgEntity**). Модель чертежа можно получить с помощью свойства **DwgBlock.Drawing**. Прежде чем вносить изменения в чертёж мы зафиксируем его настройки методом **Drawing.PushState()** и выполним их сброс до состояния по умолчанию методом **Drawing.ResetStateDefault()**. Далее в блоке **try** внесём необходимые изменения в чертёж и в блоке **finally** вернём настройки чертежа в прежнее состояние методом **Drawing.PopState()**. В теле программного модуля объявите команду, и декорируйте её атрибутом «cmd». //функция срабатывающая при обявлении бродкаста "generate_planchet" //подписка на бродкаст осудествляется в plugin-файле в разделе "broadcasts" [cmd("tutorial_generate_planchet")] private void GeneratePlanchet(GeneratePlanchetEventArgs e) { // проверяем принадлежит ли слой нашей модели if (e.Layer is ModelLayer) { // отрисовываем примитивы GenerateModelPlanchet(e); e.Handled = true; } } /// /// Формирование примитивов на основе данных полученных из модели /// и добавление примитивов в чертёж /// /// private void GenerateModelPlanchet(GeneratePlanchetEventArgs e) { if (e.Layer is ModelLayer layer) { // определяем префикс имени слоя var path = e.Path; if (e.SaveFullPath) { path += "."; } else { path = string.Empty; } // если слой включен и относится к нашему типу модели // делаем активным необходимый слой чертежа и добавляем примитивы в чертёж if (layer.Visible && layer.Model is Model model) { var modelSpace = e.Model; var drawing = modelSpace.Drawing; // запоминаем настройки чертежа и сбрасываем к значениям по умолчанию drawing.PushState(); try { drawing.ResetStateDefault(); var layerName = path + layer.Name; var dwgLayer = drawing.Layers[layerName] ?? drawing.Layers.Add(layerName); drawing.ActiveLayer = dwgLayer; for (int i = 0; i < model.Count; i++) { var points = model[i]; var vertices = new List(); for (int j = 0; j < points.Count; j++) { var point = points[j]; var text = modelSpace.AddText(j.ToString(), point, 2, 1, 0, 0); text.Color = new CadColor(Color.Orange); vertices.Add(point); } var pline = modelSpace.AddPolyline(vertices); pline.Color = CadColor.Yellow; } } finally { // восстанавливаем предыдущие настройки чертежа drawing.PopState(); } } } } Команда «**tutorial_generate_planchet**» должна выполняться в момент создания планшета или экспорта ситуации. Для этого добавим соответствующую запись в plugin-файл в секции "**broadcast**" "broadcasts": { "generate_planchet": "tutorial_generate_planchet" } В результате работы команды "**tutorial_generate_planchet**" в экспортированном файле плоского чертежа появятся примитивы нашей модели. {{ :developers:tutorial:createmodel:tut_export_model_views_3.png?direct |}} ==== Экспорт объектов в сводную ИМ ==== Для экспорта объектов пользовательской модели в сводную ИМ необходимы следующий действия: - Создать команду для добавления объектов в сводную ИМ - Зарегистрировать вызов команды через бродкаст «generate_visualization_map» в plugin-файле ==== Создание команды добавления объектов в сводную ИМ ==== Команда должна вызвать метод принимающий в качестве аргумента объект типа **CreateVisualizationEventArgs**. Свойство **CreateVisualizationEventArgs.Layer** возвращает текущий экспортируемый слой типа **CadViewLayer**. По свойству Layer можно определить, какая именно модель в данный момент экспортируется. Свойство **CreateVisualizationEventArgs.Map** возвращает сводную ИМ типа **VisualizationMap**, в которую следует добавить необходимые объекты. Объекты ИМ объединяются в группы (**VisualizationGroup**). Каждая группа - это узел в дереве сводной модели. Текущую группу можно получить через свойство **VisualizationMap.Group**, и при необходимости переопределить её свойства через метод **VisualizationGroup.ApplayOverridedProperties()** или тип. Группы так же могут содержать в себе другие группы. Новая группа создаётся методом **VisualizationMap.BeginGroup()** и становится текущей. Метод **VisualizationGroup.EndGroup()** заканчивает работу с текущей группой и возвращает группу уровнем выше. Связку **BeginGroup() => EndGroup()** следует использовать в сочетании с блоком **try/finally**. # args - аргументы типа CreateVisualizationEventArgs VisualizationMap map = args.Map; map.BeginGroup("GroupName", false); try { VisualizationGroup group = map.Group; # пользовательские действия } finally { map.BeginGroup("GroupName", false); } Для добавления моделей в сводную ИМ используется метод **VisualizationMap.AddMesh()**, а для размещения добавленной модели в пространстве метод **VisualizationMap.AddInsertion()**. === Добавление объекта в сводную модель === Наша модель содержит в себе линейные объекты. В качестве примера для отображения на сводной модели примем, что линейный объект нашей модели это забор (**Fence**) с фиксированной высотой 3 метра. Для каждого участка забора создадим полигональную сеть (**MeshGeometry3D**). Через свойство **MeshGeometry3D.Groups** получим коллекцию групп сети (**MaterialGroupsCollection**) и добавим в неё группу с материалом (**MaterialGroup**). Определим координаты вершин (**Vector3F**) и добавим их в коллекцию вершин (**Vector3dCollection**) через свойство **MeshGeometry3D.Positions**. Определим грани (**Face**) опирающиеся на индексы вершин полигональной сети. Для каждой грани добавим в группу сети новый индекс методом **MaterialGroup.AddIndex()**. Через свойство **MeshGeometry3D.TriangleIndices** получим коллекцию треугольников (**TrianglesCollection**) и добавим в неё грани. Создадим трёхмерную модель (**GeometryModel3D**). Получим словарь с материалами через свойство **GeometryModel3D.Materials** и добавим в него материал (**PhongMaterial**). Через свойство **GeometryModel3D.Meshes** получим словарь полигональных сетей и добавим в него вышеописанную сеть. Сгенерируем новое имя для нашей 3D-модели методом **VisualizationMap.GenMeshName()**. Добавим нашу 3D-модель с новым именем в коллекцию моделей сводной ИМ методом **VisualizationMap.AddMesh**. Создадим новую группу методом **VisualizationMap.BeginGroup()**. В блоке **try** добавим вставку (**GeometryInsertion**) ранее добавленной 3D-модели методом **VisualizationMap.AddInsertion**. Через свойство **VisualizationMap.Group** получим текущую группу (**VisualizationGroup**). Назначим ей тип через свойство **VisualizationGroup.Type**. Через свойство **VisualizationGroup.Properties** получим коллекцию свойств (**ImProperties**) и добавим в неё новые свойства (**ImProperty**) "Количество узлов" и "Длина". В блоке **finally** закроем группу методом **VisualizationMap.EndGroup()**. В теле программного модуля объявите команду, и декорируйте её атрибутом «cmd». //функция срабатывающая при объявлении бродкаста "generate_visualization_map" //подписка на бродкаст осуществляется в plugin-файле в разделе "broadcasts" [cmd("tutorial_generate_visualization")] private void GenerateVisualization(CreateVisualizationEventArgs args) { // проверяем слой на соответствие типу слоя нашей модели if (args.Layer is ModelLayer) { GenerateModelVisualization(args); args.Handled = true; } } /// /// Формирование элементов информационной модели на основе данных полученных из модели /// и добавление их в информационную модель /// /// private void GenerateModelVisualization(CreateVisualizationEventArgs args) { var fenceHeight = 3.0; // высота забора var map = args.Map; // сводная модель var layer = args.Layer as ModelLayer; // проверяем слой на видимость и наличием нашей модели if (layer.Visible && layer.Model is Model model) { // собираем поверхности для определения высотного положения забора var project = ApplicationHost.Current.ActiveProject as ModelProject; var items = project.Model.GetChilds(); var surfaces = new List(); foreach (var item in items) { if (item.ModelType.Equals("dtm", StringComparison.OrdinalIgnoreCase) && item.Model is TerrainModel dtm) { var sfc = args.FetchSurfaces(dtm); surfaces.Add(sfc); } } map.BeginGroup("CustomModel", false); try { for (int i = 0; i < model.Count; i++) { // создаём полигональную сеть и добавляем в неё группу материалов var mesh = new MeshGeometry3D(); var materialGroup = new MaterialGroup { Material = "Fence_Material" }; mesh.Groups.Add(materialGroup); // наполняем сеть вершинами и определяем грани опирающиеся на эти точки var length = 0.0; //длина участка забора var points = model[i]; for (int j = 0; j < points.Count; j++) { var point = points[j]; var elevation = 0.0; foreach (var sfc in surfaces) { var z = sfc.GetElevation(point); if (z.HasValue && ValueConverter.CompValues(z.Value, elevation) == 1) { elevation = z.Value; } } mesh.Positions.Add(new Vector3F(new Vector3D(point, elevation))); mesh.Positions.Add(new Vector3F(new Vector3D(point, elevation + fenceHeight))); if (j < points.Count - 1) { var nextPoint = points[j + 1]; var delta = nextPoint - point; length += delta.Length; var face1 = new Face(j * 2, (j + 1) * 2, j * 2 + 1); var face2 = new Face((j + 1) * 2, (j + 1) * 2 + 1, j * 2 + 1); materialGroup.AddIndex(TrimShort(mesh.TriangleIndices.Count)); mesh.TriangleIndices.Add(face1); materialGroup.AddIndex(TrimShort(mesh.TriangleIndices.Count)); mesh.TriangleIndices.Add(face2); } } // создаём 3D-модель и добавляем в неё материал // добавляем сеть в 3D-модель var geometry = new GeometryModel3D(); if (!geometry.Materials.ContainsKey("Custom_Material")) { var material = new PhongMaterial(); material.Diffuse = new Vector3F(0.6, 0.6, 0.6); material.Shininess = 0.8f; material.SpecularLevel = 0.8f; geometry.Materials["Custom_Material"] = material; } geometry.Meshes["Fence_Mesh"] = mesh; // добавляем 3D-модель в сводную модель, // назначаем ей тип и необходимые свойства var meshName = map.GenMeshName("custom_mesh"); map.AddMesh(meshName, geometry); map.BeginGroup("Fence", false); try { map.AddInsertion(meshName, new Vector3D(0, 0, 0)); var type = map.FindType("SmdxCustomFence"); if (type == null) { var parent = map.FindType("SmdxElement"); var typeDesc = new ImTypeDescriptor("SmdxCustomFence", "Пользовательский забор", parent); type = map.CreateType(typeDesc); } map.Group.Type = type; map.Group.Properties.Add(new ImProperty("points", "Количество узлов", points.Count)); map.Group.Properties.Add(new ImProperty("length", "Длина", length)); } finally { map.EndGroup(); } } } finally { map.EndGroup(); } } } private static ushort TrimShort(int value) { Debug.Assert(value <= ushort.MaxValue); return value > ushort.MaxValue ? ushort.MaxValue : (ushort)value; } Команда «**tutorial_generate_visualization**» должна выполняться в момент создания сводной модели. Для этого добавим соответствующую запись в plugin-файл в секции "**broadcast**" "broadcasts": { "generate_visualization_map": "tutorial_generate_visualization" } В результате работы команды "**tutorial_generate_visualization**" в экспортированной сводной модели появятся объекты нашей модели. {{ :developers:tutorial:createmodel:tut_export_model_views_2.png?direct |}} [[developers:tutorial:tutorialcode|Исходный код]] примера расположен в проекте **"TutorialExportModelViews"**.